|
|
|
@ -2,13 +2,10 @@
|
|
|
|
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#import "TSMessagesManager.h"
|
|
|
|
|
#import "OWSMessageManager.h"
|
|
|
|
|
#import "ContactsManagerProtocol.h"
|
|
|
|
|
#import "ContactsUpdater.h"
|
|
|
|
|
#import "Cryptography.h"
|
|
|
|
|
#import "DataSource.h"
|
|
|
|
|
#import "MimeTypeUtil.h"
|
|
|
|
|
#import "NSData+messagePadding.h"
|
|
|
|
|
#import "NSDate+millisecondTimeStamp.h"
|
|
|
|
|
#import "NotificationsProtocol.h"
|
|
|
|
|
#import "OWSAttachmentsProcessor.h"
|
|
|
|
@ -17,7 +14,7 @@
|
|
|
|
|
#import "OWSDisappearingConfigurationUpdateInfoMessage.h"
|
|
|
|
|
#import "OWSDisappearingMessagesConfiguration.h"
|
|
|
|
|
#import "OWSDisappearingMessagesJob.h"
|
|
|
|
|
#import "OWSError.h"
|
|
|
|
|
#import "OWSIdentityManager.h"
|
|
|
|
|
#import "OWSIncomingMessageFinder.h"
|
|
|
|
|
#import "OWSIncomingSentMessageTranscript.h"
|
|
|
|
|
#import "OWSMessageSender.h"
|
|
|
|
@ -28,33 +25,21 @@
|
|
|
|
|
#import "OWSSyncGroupsRequestMessage.h"
|
|
|
|
|
#import "ProfileManagerProtocol.h"
|
|
|
|
|
#import "TSAccountManager.h"
|
|
|
|
|
#import "TSAttachmentStream.h"
|
|
|
|
|
#import "TSCall.h"
|
|
|
|
|
#import "TSContactThread.h"
|
|
|
|
|
#import "TSDatabaseView.h"
|
|
|
|
|
#import "TSGroupModel.h"
|
|
|
|
|
#import "TSGroupThread.h"
|
|
|
|
|
#import "TSIncomingMessage.h"
|
|
|
|
|
#import "TSInfoMessage.h"
|
|
|
|
|
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
|
|
|
|
|
#import "TSNetworkManager.h"
|
|
|
|
|
#import "TSPreKeyManager.h"
|
|
|
|
|
#import "TSStorageHeaders.h"
|
|
|
|
|
#import "TSOutgoingMessage.h"
|
|
|
|
|
#import "TSStorageManager+SessionStore.h"
|
|
|
|
|
#import "TSStorageManager.h"
|
|
|
|
|
#import "TextSecureKitEnv.h"
|
|
|
|
|
#import <AxolotlKit/AxolotlExceptions.h>
|
|
|
|
|
#import <AxolotlKit/SessionCipher.h>
|
|
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
|
|
|
|
|
// We need to use a consistent batch size throughout
|
|
|
|
|
// the incoming message pipeline (i.e. in the
|
|
|
|
|
// "decrypt" and "process" steps), or the pipeline
|
|
|
|
|
// doesn't flow smoothly.
|
|
|
|
|
//
|
|
|
|
|
// We want a value that is just high enough to yield
|
|
|
|
|
// perf benefits. The right value is probably 5-15.
|
|
|
|
|
const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
|
|
|
|
|
@interface TSMessagesManager ()
|
|
|
|
|
@interface OWSMessageManager ()
|
|
|
|
|
|
|
|
|
|
@property (nonatomic, readonly) id<OWSCallMessageHandler> callMessageHandler;
|
|
|
|
|
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
|
|
|
|
@ -63,15 +48,18 @@ const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
@property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder;
|
|
|
|
|
@property (nonatomic, readonly) OWSBlockingManager *blockingManager;
|
|
|
|
|
@property (nonatomic, readonly) OWSIdentityManager *identityManager;
|
|
|
|
|
@property (nonatomic, readonly) TSNetworkManager *networkManager;
|
|
|
|
|
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
@implementation TSMessagesManager
|
|
|
|
|
@implementation OWSMessageManager
|
|
|
|
|
|
|
|
|
|
+ (instancetype)sharedManager {
|
|
|
|
|
static TSMessagesManager *sharedMyManager = nil;
|
|
|
|
|
+ (instancetype)sharedManager
|
|
|
|
|
{
|
|
|
|
|
static OWSMessageManager *sharedMyManager = nil;
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
sharedMyManager = [[self alloc] initDefault];
|
|
|
|
@ -85,16 +73,14 @@ const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
TSStorageManager *storageManager = [TSStorageManager sharedManager];
|
|
|
|
|
id<ContactsManagerProtocol> contactsManager = [TextSecureKitEnv sharedEnv].contactsManager;
|
|
|
|
|
id<OWSCallMessageHandler> callMessageHandler = [TextSecureKitEnv sharedEnv].callMessageHandler;
|
|
|
|
|
ContactsUpdater *contactsUpdater = [ContactsUpdater sharedUpdater];
|
|
|
|
|
OWSIdentityManager *identityManager = [OWSIdentityManager sharedManager];
|
|
|
|
|
OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return [self initWithNetworkManager:networkManager
|
|
|
|
|
storageManager:storageManager
|
|
|
|
|
callMessageHandler:callMessageHandler
|
|
|
|
|
contactsManager:contactsManager
|
|
|
|
|
contactsUpdater:contactsUpdater
|
|
|
|
|
identityManager:identityManager
|
|
|
|
|
messageSender:messageSender];
|
|
|
|
|
}
|
|
|
|
@ -103,7 +89,6 @@ const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
storageManager:(TSStorageManager *)storageManager
|
|
|
|
|
callMessageHandler:(id<OWSCallMessageHandler>)callMessageHandler
|
|
|
|
|
contactsManager:(id<ContactsManagerProtocol>)contactsManager
|
|
|
|
|
contactsUpdater:(ContactsUpdater *)contactsUpdater
|
|
|
|
|
identityManager:(OWSIdentityManager *)identityManager
|
|
|
|
|
messageSender:(OWSMessageSender *)messageSender
|
|
|
|
|
{
|
|
|
|
@ -117,7 +102,6 @@ const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
_networkManager = networkManager;
|
|
|
|
|
_callMessageHandler = callMessageHandler;
|
|
|
|
|
_contactsManager = contactsManager;
|
|
|
|
|
_contactsUpdater = contactsUpdater;
|
|
|
|
|
_identityManager = identityManager;
|
|
|
|
|
_messageSender = messageSender;
|
|
|
|
|
|
|
|
|
@ -145,130 +129,6 @@ const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
[self updateApplicationBadgeCount];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Debugging
|
|
|
|
|
|
|
|
|
|
- (NSString *)descriptionForEnvelopeType:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
{
|
|
|
|
|
OWSAssert(envelope != nil);
|
|
|
|
|
|
|
|
|
|
switch (envelope.type) {
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeReceipt:
|
|
|
|
|
return @"DeliveryReceipt";
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeUnknown:
|
|
|
|
|
// Shouldn't happen
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeUnknown]);
|
|
|
|
|
return @"Unknown";
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeCiphertext:
|
|
|
|
|
return @"SignalEncryptedMessage";
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
|
|
|
|
|
// Unsupported
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeKeyExchange]);
|
|
|
|
|
return @"KeyExchange";
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle:
|
|
|
|
|
return @"PreKeyEncryptedMessage";
|
|
|
|
|
default:
|
|
|
|
|
// Shouldn't happen
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents messageManagerErrorEnvelopeTypeOther]);
|
|
|
|
|
return @"Other";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)descriptionForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
{
|
|
|
|
|
OWSAssert(envelope != nil);
|
|
|
|
|
|
|
|
|
|
return [NSString stringWithFormat:@"<Envelope type: %@, source: %@, timestamp: %llu content.length: %lu />",
|
|
|
|
|
[self descriptionForEnvelopeType:envelope],
|
|
|
|
|
envelopeAddress(envelope),
|
|
|
|
|
envelope.timestamp,
|
|
|
|
|
(unsigned long)envelope.content.length];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* We don't want to just log `content.description` because we'd potentially log message bodies for dataMesssages and
|
|
|
|
|
* sync transcripts
|
|
|
|
|
*/
|
|
|
|
|
- (NSString *)descriptionForContent:(OWSSignalServiceProtosContent *)content
|
|
|
|
|
{
|
|
|
|
|
if (content.hasSyncMessage) {
|
|
|
|
|
return [NSString stringWithFormat:@"<SyncMessage: %@ />", [self descriptionForSyncMessage:content.syncMessage]];
|
|
|
|
|
} else if (content.hasDataMessage) {
|
|
|
|
|
return [NSString stringWithFormat:@"<DataMessage: %@ />", [self descriptionForDataMessage:content.dataMessage]];
|
|
|
|
|
} else if (content.hasCallMessage) {
|
|
|
|
|
return [NSString stringWithFormat:@"<CallMessage: %@ />", content.callMessage];
|
|
|
|
|
} else if (content.hasNullMessage) {
|
|
|
|
|
return [NSString stringWithFormat:@"<NullMessage: %@ />", content.nullMessage];
|
|
|
|
|
} else {
|
|
|
|
|
// Don't fire an analytics event; if we ever add a new content type, we'd generate a ton of
|
|
|
|
|
// analytics traffic.
|
|
|
|
|
OWSFail(@"Unknown content type.");
|
|
|
|
|
return @"UnknownContent";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* We don't want to just log `dataMessage.description` because we'd potentially log message contents
|
|
|
|
|
*/
|
|
|
|
|
- (NSString *)descriptionForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
|
|
|
|
{
|
|
|
|
|
NSMutableString *description = [NSMutableString new];
|
|
|
|
|
|
|
|
|
|
if (dataMessage.hasGroup) {
|
|
|
|
|
[description appendString:@"(Group:YES) "];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsEndSession) != 0) {
|
|
|
|
|
[description appendString:@"EndSession"];
|
|
|
|
|
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsExpirationTimerUpdate) != 0) {
|
|
|
|
|
[description appendString:@"ExpirationTimerUpdate"];
|
|
|
|
|
} else if ((dataMessage.flags & OWSSignalServiceProtosDataMessageFlagsProfileKey) != 0) {
|
|
|
|
|
[description appendString:@"ProfileKey"];
|
|
|
|
|
} else if (dataMessage.attachments.count > 0) {
|
|
|
|
|
[description appendString:@"MessageWithAttachment"];
|
|
|
|
|
} else {
|
|
|
|
|
[description appendString:@"Plain"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [NSString stringWithFormat:@"<%@ />", description];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* We don't want to just log `syncMessage.description` because we'd potentially log message contents in sent transcripts
|
|
|
|
|
*/
|
|
|
|
|
- (NSString *)descriptionForSyncMessage:(OWSSignalServiceProtosSyncMessage *)syncMessage
|
|
|
|
|
{
|
|
|
|
|
NSMutableString *description = [NSMutableString new];
|
|
|
|
|
if (syncMessage.hasSent) {
|
|
|
|
|
[description appendString:@"SentTranscript"];
|
|
|
|
|
} else if (syncMessage.hasRequest) {
|
|
|
|
|
if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeContacts) {
|
|
|
|
|
[description appendString:@"ContactRequest"];
|
|
|
|
|
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeGroups) {
|
|
|
|
|
[description appendString:@"GroupRequest"];
|
|
|
|
|
} else if (syncMessage.request.type == OWSSignalServiceProtosSyncMessageRequestTypeBlocked) {
|
|
|
|
|
[description appendString:@"BlockedRequest"];
|
|
|
|
|
} else {
|
|
|
|
|
// Shouldn't happen
|
|
|
|
|
OWSFail(@"Unknown sync message request type");
|
|
|
|
|
[description appendString:@"UnknownRequest"];
|
|
|
|
|
}
|
|
|
|
|
} else if (syncMessage.hasBlocked) {
|
|
|
|
|
[description appendString:@"Blocked"];
|
|
|
|
|
} else if (syncMessage.read.count > 0) {
|
|
|
|
|
[description appendString:@"ReadReceipt"];
|
|
|
|
|
} else if (syncMessage.hasVerified) {
|
|
|
|
|
NSString *verifiedString =
|
|
|
|
|
[NSString stringWithFormat:@"Verification for: %@", syncMessage.verified.destination];
|
|
|
|
|
[description appendString:verifiedString];
|
|
|
|
|
} else {
|
|
|
|
|
// Shouldn't happen
|
|
|
|
|
OWSFail(@"Unknown sync message type");
|
|
|
|
|
[description appendString:@"Unknown"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return description;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Blocking
|
|
|
|
|
|
|
|
|
|
- (BOOL)isEnvelopeBlocked:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
@ -278,178 +138,6 @@ const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
return [_blockingManager isRecipientIdBlocked:envelope.source];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - Decryption
|
|
|
|
|
|
|
|
|
|
- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
successBlock:(DecryptSuccessBlock)successBlockParameter
|
|
|
|
|
failureBlock:(DecryptFailureBlock)failureBlockParameter
|
|
|
|
|
{
|
|
|
|
|
OWSAssert(envelope);
|
|
|
|
|
OWSAssert(successBlockParameter);
|
|
|
|
|
OWSAssert(failureBlockParameter);
|
|
|
|
|
OWSAssert([TSAccountManager isRegistered]);
|
|
|
|
|
|
|
|
|
|
// Ensure that successBlock and failureBlock are called on a worker queue.
|
|
|
|
|
DecryptSuccessBlock successBlock = ^(NSData *_Nullable plaintextData) {
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
successBlockParameter(plaintextData);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
DecryptFailureBlock failureBlock = ^() {
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
failureBlockParameter();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
DDLogInfo(@"%@ decrypting envelope: %@", self.tag, [self descriptionForEnvelope:envelope]);
|
|
|
|
|
|
|
|
|
|
OWSAssert(envelope.source.length > 0);
|
|
|
|
|
if ([self isEnvelopeBlocked:envelope]) {
|
|
|
|
|
DDLogInfo(@"%@ ignoring blocked envelope: %@", self.tag, envelope.source);
|
|
|
|
|
failureBlock();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@try {
|
|
|
|
|
switch (envelope.type) {
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeCiphertext: {
|
|
|
|
|
[self decryptSecureMessage:envelope
|
|
|
|
|
successBlock:^(NSData *_Nullable plaintextData) {
|
|
|
|
|
DDLogDebug(@"%@ decrypted secure message.", self.tag);
|
|
|
|
|
successBlock(plaintextData);
|
|
|
|
|
}
|
|
|
|
|
failureBlock:^(NSError *_Nullable error) {
|
|
|
|
|
DDLogError(@"%@ decrypting secure message from address: %@ failed with error: %@",
|
|
|
|
|
self.tag,
|
|
|
|
|
envelopeAddress(envelope),
|
|
|
|
|
error);
|
|
|
|
|
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandleSecureMessage]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
}];
|
|
|
|
|
// Return to avoid double-acknowledging.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypePrekeyBundle: {
|
|
|
|
|
[self decryptPreKeyBundle:envelope
|
|
|
|
|
successBlock:^(NSData *_Nullable plaintextData) {
|
|
|
|
|
DDLogDebug(@"%@ decrypted pre-key whisper message", self.tag);
|
|
|
|
|
successBlock(plaintextData);
|
|
|
|
|
}
|
|
|
|
|
failureBlock:^(NSError *_Nullable error) {
|
|
|
|
|
DDLogError(@"%@ decrypting pre-key whisper message from address: %@ failed "
|
|
|
|
|
@"with error: %@",
|
|
|
|
|
self.tag,
|
|
|
|
|
envelopeAddress(envelope),
|
|
|
|
|
error);
|
|
|
|
|
OWSProdError([OWSAnalyticsEvents messageManagerErrorCouldNotHandlePrekeyBundle]);
|
|
|
|
|
failureBlock();
|
|
|
|
|
}];
|
|
|
|
|
// Return to avoid double-acknowledging.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// These message types don't have a payload to decrypt.
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeReceipt:
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeKeyExchange:
|
|
|
|
|
case OWSSignalServiceProtosEnvelopeTypeUnknown:
|
|
|
|
|
successBlock(nil);
|
|
|
|
|
// Return to avoid double-acknowledging.
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
DDLogWarn(@"Received unhandled envelope type: %d", (int)envelope.type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} @catch (NSException *exception) {
|
|
|
|
|
DDLogError(@"Received an incorrectly formatted protocol buffer: %@", exception.debugDescription);
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents messageManagerErrorInvalidProtocolMessage]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
failureBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)decryptSecureMessage:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
successBlock:(DecryptSuccessBlock)successBlock
|
|
|
|
|
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
|
|
|
|
|
{
|
|
|
|
|
OWSAssert(envelope);
|
|
|
|
|
OWSAssert(successBlock);
|
|
|
|
|
OWSAssert(failureBlock);
|
|
|
|
|
|
|
|
|
|
[self decryptEnvelope:envelope
|
|
|
|
|
messageTypeName:@"Secure Message"
|
|
|
|
|
cipherMessageBlock:^(NSData *encryptedData) {
|
|
|
|
|
return [[WhisperMessage alloc] initWithData:encryptedData];
|
|
|
|
|
}
|
|
|
|
|
successBlock:successBlock
|
|
|
|
|
failureBlock:failureBlock];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)decryptPreKeyBundle:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
successBlock:(DecryptSuccessBlock)successBlock
|
|
|
|
|
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
|
|
|
|
|
{
|
|
|
|
|
OWSAssert(envelope);
|
|
|
|
|
OWSAssert(successBlock);
|
|
|
|
|
OWSAssert(failureBlock);
|
|
|
|
|
|
|
|
|
|
// Check whether we need to refresh our PreKeys every time we receive a PreKeyWhisperMessage.
|
|
|
|
|
[TSPreKeyManager checkPreKeys];
|
|
|
|
|
|
|
|
|
|
[self decryptEnvelope:envelope
|
|
|
|
|
messageTypeName:@"PreKey Bundle"
|
|
|
|
|
cipherMessageBlock:^(NSData *encryptedData) {
|
|
|
|
|
return [[PreKeyWhisperMessage alloc] initWithData:encryptedData];
|
|
|
|
|
}
|
|
|
|
|
successBlock:successBlock
|
|
|
|
|
failureBlock:failureBlock];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
messageTypeName:(NSString *)messageTypeName
|
|
|
|
|
cipherMessageBlock:(id<CipherMessage> (^_Nonnull)(NSData *))cipherMessageBlock
|
|
|
|
|
successBlock:(DecryptSuccessBlock)successBlock
|
|
|
|
|
failureBlock:(void (^)(NSError *_Nullable error))failureBlock
|
|
|
|
|
{
|
|
|
|
|
OWSAssert(envelope);
|
|
|
|
|
OWSAssert(messageTypeName.length > 0);
|
|
|
|
|
OWSAssert(cipherMessageBlock);
|
|
|
|
|
OWSAssert(successBlock);
|
|
|
|
|
OWSAssert(failureBlock);
|
|
|
|
|
|
|
|
|
|
TSStorageManager *storageManager = self.storageManager;
|
|
|
|
|
NSString *recipientId = envelope.source;
|
|
|
|
|
int deviceId = envelope.sourceDevice;
|
|
|
|
|
|
|
|
|
|
// DEPRECATED - Remove after all clients have been upgraded.
|
|
|
|
|
NSData *encryptedData = envelope.hasContent ? envelope.content : envelope.legacyMessage;
|
|
|
|
|
if (!encryptedData) {
|
|
|
|
|
OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]);
|
|
|
|
|
failureBlock(nil);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatch_async([OWSDispatch sessionStoreQueue], ^{
|
|
|
|
|
@try {
|
|
|
|
|
id<CipherMessage> cipherMessage = cipherMessageBlock(encryptedData);
|
|
|
|
|
SessionCipher *cipher = [[SessionCipher alloc] initWithSessionStore:storageManager
|
|
|
|
|
preKeyStore:storageManager
|
|
|
|
|
signedPreKeyStore:storageManager
|
|
|
|
|
identityKeyStore:self.identityManager
|
|
|
|
|
recipientId:recipientId
|
|
|
|
|
deviceId:deviceId];
|
|
|
|
|
|
|
|
|
|
NSData *plaintextData = [[cipher decrypt:cipherMessage] removePadding];
|
|
|
|
|
successBlock(plaintextData);
|
|
|
|
|
} @catch (NSException *exception) {
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
[self processException:exception envelope:envelope];
|
|
|
|
|
NSString *errorDescription = [NSString
|
|
|
|
|
stringWithFormat:@"Exception while decrypting %@: %@", messageTypeName, exception.description];
|
|
|
|
|
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeFailedToDecryptMessage, errorDescription);
|
|
|
|
|
failureBlock(error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - message handling
|
|
|
|
|
|
|
|
|
|
- (void)processEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
@ -1205,69 +893,11 @@ const NSUInteger kIncomingMessageBatchSize = 10;
|
|
|
|
|
return incomingMessage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)processException:(NSException *)exception envelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
{
|
|
|
|
|
OWSAssert([NSThread isMainThread]);
|
|
|
|
|
|
|
|
|
|
DDLogError(@"%@ Got exception: %@ of type: %@ with reason: %@",
|
|
|
|
|
self.tag,
|
|
|
|
|
exception.description,
|
|
|
|
|
exception.name,
|
|
|
|
|
exception.reason);
|
|
|
|
|
|
|
|
|
|
__block TSErrorMessage *errorMessage;
|
|
|
|
|
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
|
if ([exception.name isEqualToString:NoSessionException]) {
|
|
|
|
|
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorNoSession], envelope);
|
|
|
|
|
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
|
|
|
|
|
} else if ([exception.name isEqualToString:InvalidKeyException]) {
|
|
|
|
|
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKey], envelope);
|
|
|
|
|
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
|
|
|
|
|
} else if ([exception.name isEqualToString:InvalidKeyIdException]) {
|
|
|
|
|
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidKeyId], envelope);
|
|
|
|
|
errorMessage = [TSErrorMessage invalidKeyExceptionWithEnvelope:envelope withTransaction:transaction];
|
|
|
|
|
} else if ([exception.name isEqualToString:DuplicateMessageException]) {
|
|
|
|
|
// Duplicate messages are dismissed
|
|
|
|
|
return;
|
|
|
|
|
} else if ([exception.name isEqualToString:InvalidVersionException]) {
|
|
|
|
|
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorInvalidMessageVersion], envelope);
|
|
|
|
|
errorMessage = [TSErrorMessage invalidVersionWithEnvelope:envelope withTransaction:transaction];
|
|
|
|
|
} else if ([exception.name isEqualToString:UntrustedIdentityKeyException]) {
|
|
|
|
|
// Should no longer get here, since we now record the new identity for incoming messages.
|
|
|
|
|
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorUntrustedIdentityKeyException], envelope);
|
|
|
|
|
OWSFail(@"%@ Failed to trust identity on incoming message from: %@", self.tag, envelopeAddress(envelope));
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorCorruptMessage], envelope);
|
|
|
|
|
errorMessage = [TSErrorMessage corruptedMessageWithEnvelope:envelope withTransaction:transaction];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[errorMessage saveWithTransaction:transaction];
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
if (errorMessage != nil) {
|
|
|
|
|
[self notifyForErrorMessage:errorMessage withEnvelope:envelope];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)notifyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
|
|
|
|
{
|
|
|
|
|
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
|
|
|
|
|
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - helpers
|
|
|
|
|
|
|
|
|
|
// used in log formatting
|
|
|
|
|
NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope)
|
|
|
|
|
{
|
|
|
|
|
return [NSString stringWithFormat:@"%@.%d", envelope.source, (unsigned int)envelope.sourceDevice];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)isDataMessageGroupAvatarUpdate:(OWSSignalServiceProtosDataMessage *)dataMessage
|
|
|
|
|
{
|
|
|
|
|
return dataMessage.hasGroup
|
|
|
|
|
&& dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate
|
|
|
|
|
return dataMessage.hasGroup && dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeUpdate
|
|
|
|
|
&& dataMessage.group.hasAvatar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1290,16 +920,18 @@ NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSUInteger)unreadMessagesCount {
|
|
|
|
|
- (NSUInteger)unreadMessagesCount
|
|
|
|
|
{
|
|
|
|
|
__block NSUInteger numberOfItems;
|
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
|
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];
|
|
|
|
|
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
return numberOfItems;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread {
|
|
|
|
|
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread
|
|
|
|
|
{
|
|
|
|
|
__block NSUInteger numberOfItems;
|
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
|
id databaseView = [transaction ext:TSUnreadDatabaseViewExtensionName];
|
|
|
|
@ -1316,10 +948,11 @@ NSString *envelopeAddress(OWSSignalServiceProtosEnvelope *envelope)
|
|
|
|
|
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:numberOfItems];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread {
|
|
|
|
|
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread
|
|
|
|
|
{
|
|
|
|
|
__block NSUInteger numberOfItems;
|
|
|
|
|
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
|
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId];
|
|
|
|
|
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId];
|
|
|
|
|
}];
|
|
|
|
|
return numberOfItems;
|
|
|
|
|
}
|