diff --git a/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.m b/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.m index 1beb207fa..ff86c4598 100644 --- a/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.m +++ b/SignalServiceKit/src/Devices/OWSReadReceiptsProcessor.m @@ -59,8 +59,8 @@ NSString *const OWSReadReceiptsProcessorMarkedMessageAsReadNotification = - (instancetype)initWithIncomingMessage:(TSIncomingMessage *)message storageManager:(TSStorageManager *)storageManager { - // Only groupthread sets authorId, thus this crappy code. - // TODO ALL incoming messages should have an authorId. + // authorId isn't set on all legacy messages, so we take + // extra measures to ensure we obtain a valid value. NSString *messageAuthorId; if (message.authorId) { // Group Thread messageAuthorId = message.authorId; diff --git a/SignalServiceKit/src/Devices/OWSSendReadReceiptsJob.m b/SignalServiceKit/src/Devices/OWSSendReadReceiptsJob.m index 4da8a0770..f88b4cd3d 100644 --- a/SignalServiceKit/src/Devices/OWSSendReadReceiptsJob.m +++ b/SignalServiceKit/src/Devices/OWSSendReadReceiptsJob.m @@ -1,5 +1,6 @@ -// Created by Michael Kirk on 9/24/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSSendReadReceiptsJob.h" #import "OWSMessageSender.h" @@ -41,8 +42,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)runWith:(TSIncomingMessage *)message { - // Only groupthread sets authorId, thus this crappy code. - // TODO Refactor so that ALL incoming messages have an authorId. + // authorId isn't set on all legacy messages, so we take + // extra measures to ensure we obtain a valid value. NSString *messageAuthorId; if (message.authorId) { // Group Thread messageAuthorId = message.authorId; diff --git a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m index 3812b3425..4c690ba6e 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSIncomingMessage.m @@ -92,8 +92,8 @@ NSString *const TSIncomingMessageWasReadOnThisDeviceNotification = @"TSIncomingM if ([interaction isKindOfClass:[TSIncomingMessage class]]) { TSIncomingMessage *message = (TSIncomingMessage *)interaction; - // Only groupthread sets authorId, thus this crappy code. - // TODO ALL incoming messages should have an authorId. + // authorId isn't set on all legacy messages, so we take + // extra measures to ensure we obtain a valid value. NSString *messageAuthorId; if (message.authorId) { // Group Thread messageAuthorId = message.authorId; diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m index dc51b9a9a..a38c203b9 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m @@ -3,6 +3,7 @@ // #import "OWSBatchMessageProcessor.h" +#import "NSArray+OWS.h" #import "OWSSignalServiceProtos.pb.h" #import "TSDatabaseView.h" #import "TSMessagesManager.h" @@ -19,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @class OWSSignalServiceProtosEnvelope; -@interface OWSBatchMessageProcessingJob : TSYapDatabaseObject +@interface OWSMessageContentJob : TSYapDatabaseObject @property (nonatomic, readonly) NSDate *createdAt; @@ -32,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -@interface OWSBatchMessageProcessingJob () +@interface OWSMessageContentJob () @property (nonatomic, readonly) NSData *envelopeData; @property (nonatomic, readonly, nullable) NSData *plaintextData; @@ -41,7 +42,12 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -@implementation OWSBatchMessageProcessingJob +@implementation OWSMessageContentJob + ++ (NSString *)collection +{ + return @"OWSBatchMessageProcessingJob"; +} - (instancetype)initWithEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData { @@ -68,20 +74,16 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Finder -NSString *const OWSBatchMessageProcessingJobFinderExtensionName = @"OWSBatchMessageProcessingJobFinderExtensionName"; -NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMessageProcessingJobFinderExtensionGroup"; - -@interface OWSBatchMessageProcessingJobFinder : NSObject +NSString *const OWSMessageContentJobFinderExtensionName = @"OWSBatchMessageProcessingFinderExtensionName"; +NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSBatchMessageProcessingFinderExtensionGroup"; -- (NSArray *)nextJobsForBatchSize:(NSUInteger)maxBatchSize; -- (void)addJobWithEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData; -- (void)removeJobWithId:(NSString *)uniqueId; +@interface OWSMessageContentJobFinder : NSObject @end #pragma mark - -@interface OWSBatchMessageProcessingJobFinder () +@interface OWSMessageContentJobFinder () @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @@ -89,7 +91,7 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes #pragma mark - -@implementation OWSBatchMessageProcessingJobFinder +@implementation OWSMessageContentJobFinder - (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection { @@ -105,50 +107,41 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes return self; } -- (NSArray *)nextJobsForBatchSize:(NSUInteger)maxBatchSize +- (NSArray *)nextJobsForBatchSize:(NSUInteger)maxBatchSize { - NSMutableArray *jobs = [NSMutableArray new]; + NSMutableArray *jobs = [NSMutableArray new]; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - YapDatabaseViewTransaction *viewTransaction = [transaction ext:OWSBatchMessageProcessingJobFinderExtensionName]; + YapDatabaseViewTransaction *viewTransaction = [transaction ext:OWSMessageContentJobFinderExtensionName]; OWSAssert(viewTransaction != nil); - NSMutableArray *jobIds = [NSMutableArray new]; - [viewTransaction enumerateKeysInGroup:OWSBatchMessageProcessingJobFinderExtensionGroup - usingBlock:^(NSString *_Nonnull collection, - NSString *_Nonnull key, - NSUInteger index, - BOOL *_Nonnull stop) { - [jobIds addObject:key]; - if (jobIds.count >= maxBatchSize) { - *stop = YES; - } - }]; - - for (NSString *jobId in jobIds) { - OWSBatchMessageProcessingJob *_Nullable job = - [OWSBatchMessageProcessingJob fetchObjectWithUniqueID:jobId transaction:transaction]; - if (job) { - [jobs addObject:job]; - } else { - OWSFail(@"Could not load job: %@", jobId); - } - } + [viewTransaction enumerateKeysAndObjectsInGroup:OWSMessageContentJobFinderExtensionGroup + usingBlock:^(NSString *_Nonnull collection, + NSString *_Nonnull key, + id _Nonnull object, + NSUInteger index, + BOOL *_Nonnull stop) { + OWSMessageContentJob *job = object; + [jobs addObject:job]; + if (jobs.count >= maxBatchSize) { + *stop = YES; + } + }]; }]; - return jobs; + return [jobs copy]; } - (void)addJobWithEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [[[OWSBatchMessageProcessingJob alloc] initWithEnvelopeData:envelopeData plaintextData:plaintextData] + [[[OWSMessageContentJob alloc] initWithEnvelopeData:envelopeData plaintextData:plaintextData] saveWithTransaction:transaction]; }]; } -- (void)removeJobWithId:(NSString *)uniqueId +- (void)removeJobsWithIds:(NSArray *)uniqueIds { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [transaction removeObjectForKey:uniqueId inCollection:[OWSBatchMessageProcessingJob collection]]; + [transaction removeObjectsForKeys:uniqueIds inCollection:[OWSMessageContentJob collection]]; }]; } @@ -164,17 +157,17 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes NSString *key2, id object2) { - if (![object1 isKindOfClass:[OWSBatchMessageProcessingJob class]]) { + if (![object1 isKindOfClass:[OWSMessageContentJob class]]) { OWSFail(@"Unexpected object: %@ in collection: %@", [object1 class], collection1); return NSOrderedSame; } - OWSBatchMessageProcessingJob *job1 = (OWSBatchMessageProcessingJob *)object1; + OWSMessageContentJob *job1 = (OWSMessageContentJob *)object1; - if (![object2 isKindOfClass:[OWSBatchMessageProcessingJob class]]) { + if (![object2 isKindOfClass:[OWSMessageContentJob class]]) { OWSFail(@"Unexpected object: %@ in collection: %@", [object2 class], collection2); return NSOrderedSame; } - OWSBatchMessageProcessingJob *job2 = (OWSBatchMessageProcessingJob *)object2; + OWSMessageContentJob *job2 = (OWSMessageContentJob *)object2; return [job1.createdAt compare:job2.createdAt]; }]; @@ -184,18 +177,18 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes NSString *_Nonnull collection, NSString *_Nonnull key, id _Nonnull object) { - if (![object isKindOfClass:[OWSBatchMessageProcessingJob class]]) { + if (![object isKindOfClass:[OWSMessageContentJob class]]) { OWSFail(@"Unexpected object: %@ in collection: %@", object, collection); return nil; } // Arbitrary string - all in the same group. We're only using the view for sorting. - return OWSBatchMessageProcessingJobFinderExtensionGroup; + return OWSMessageContentJobFinderExtensionGroup; }]; YapDatabaseViewOptions *options = [YapDatabaseViewOptions new]; - options.allowedCollections = [[YapWhitelistBlacklist alloc] - initWithWhitelist:[NSSet setWithObject:[OWSBatchMessageProcessingJob collection]]]; + options.allowedCollections = + [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[OWSMessageContentJob collection]]]; return [[YapDatabaseView alloc] initWithGrouping:grouping sorting:sorting versionTag:@"1" options:options]; } @@ -203,40 +196,40 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes + (void)syncRegisterDatabaseExtension:(YapDatabase *)database { - YapDatabaseView *existingView = [database registeredExtension:OWSBatchMessageProcessingJobFinderExtensionName]; + YapDatabaseView *existingView = [database registeredExtension:OWSMessageContentJobFinderExtensionName]; if (existingView) { - OWSFail(@"%@ was already initialized.", OWSBatchMessageProcessingJobFinderExtensionName); + OWSFail(@"%@ was already initialized.", OWSMessageContentJobFinderExtensionName); // already initialized return; } - [database registerExtension:[self databaseExtension] withName:OWSBatchMessageProcessingJobFinderExtensionName]; + [database registerExtension:[self databaseExtension] withName:OWSMessageContentJobFinderExtensionName]; } @end #pragma mark - Queue Processing -@interface OWSBatchMessageProcessingQueue : NSObject +@interface OWSMessageContentQueue : NSObject @property (nonatomic, readonly) TSMessagesManager *messagesManager; @property (nonatomic, readonly) YapDatabaseConnection *dbReadWriteConnection; -@property (nonatomic, readonly) OWSBatchMessageProcessingJobFinder *finder; +@property (nonatomic, readonly) OWSMessageContentJobFinder *finder; @property (nonatomic) BOOL isDrainingQueue; - (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager storageManager:(TSStorageManager *)storageManager - finder:(OWSBatchMessageProcessingJobFinder *)finder NS_DESIGNATED_INITIALIZER; + finder:(OWSMessageContentJobFinder *)finder NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @end #pragma mark - -@implementation OWSBatchMessageProcessingQueue +@implementation OWSMessageContentQueue - (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager storageManager:(TSStorageManager *)storageManager - finder:(OWSBatchMessageProcessingJobFinder *)finder + finder:(OWSMessageContentJobFinder *)finder { OWSSingletonAssert(); @@ -299,7 +292,7 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes { AssertIsOnMainThread(); - NSArray *jobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize]; + NSArray *jobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize]; OWSAssert(jobs); if (jobs.count < 1) { self.isDrainingQueue = NO; @@ -310,23 +303,22 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes [self processJobs:jobs completion:^{ dispatch_async(dispatch_get_main_queue(), ^{ - for (OWSBatchMessageProcessingJob *job in jobs) { - [self.finder removeJobWithId:job.uniqueId]; - } + [self.finder removeJobsWithIds:jobs.uniqueIds]; + DDLogVerbose(@"%@ completed %zd jobs. %zd jobs left.", self.tag, jobs.count, - [OWSBatchMessageProcessingJob numberOfKeysInCollection]); + [OWSMessageContentJob numberOfKeysInCollection]); [self drainQueueWorkStep]; }); }]; } -- (void)processJobs:(NSArray *)jobs completion:(void (^)())completion +- (void)processJobs:(NSArray *)jobs completion:(void (^)())completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - for (OWSBatchMessageProcessingJob *job in jobs) { + for (OWSMessageContentJob *job in jobs) { [self.messagesManager processEnvelope:job.envelopeProto plaintextData:job.plaintextData transaction:transaction]; @@ -354,7 +346,7 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes @interface OWSBatchMessageProcessor () -@property (nonatomic, readonly) OWSBatchMessageProcessingQueue *processingQueue; +@property (nonatomic, readonly) OWSMessageContentQueue *processingQueue; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @end @@ -374,12 +366,10 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes return self; } - OWSBatchMessageProcessingJobFinder *finder = - [[OWSBatchMessageProcessingJobFinder alloc] initWithDBConnection:dbConnection]; - OWSBatchMessageProcessingQueue *processingQueue = - [[OWSBatchMessageProcessingQueue alloc] initWithMessagesManager:messagesManager - storageManager:storageManager - finder:finder]; + OWSMessageContentJobFinder *finder = [[OWSMessageContentJobFinder alloc] initWithDBConnection:dbConnection]; + OWSMessageContentQueue *processingQueue = [[OWSMessageContentQueue alloc] initWithMessagesManager:messagesManager + storageManager:storageManager + finder:finder]; _processingQueue = processingQueue; @@ -412,7 +402,7 @@ NSString *const OWSBatchMessageProcessingJobFinderExtensionGroup = @"OWSBatchMes + (void)syncRegisterDatabaseExtension:(YapDatabase *)database { - [OWSBatchMessageProcessingJobFinder syncRegisterDatabaseExtension:database]; + [OWSMessageContentJobFinder syncRegisterDatabaseExtension:database]; } #pragma mark - instance methods diff --git a/SignalServiceKit/src/Messages/OWSBlockingManager.h b/SignalServiceKit/src/Messages/OWSBlockingManager.h index 7cfa80d54..424cb5179 100644 --- a/SignalServiceKit/src/Messages/OWSBlockingManager.h +++ b/SignalServiceKit/src/Messages/OWSBlockingManager.h @@ -23,6 +23,8 @@ extern NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange; - (NSArray *)blockedPhoneNumbers; +- (BOOL)isRecipientIdBlocked:(NSString *)recipientId; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Messages/OWSBlockingManager.m b/SignalServiceKit/src/Messages/OWSBlockingManager.m index 6ec156920..003825213 100644 --- a/SignalServiceKit/src/Messages/OWSBlockingManager.m +++ b/SignalServiceKit/src/Messages/OWSBlockingManager.m @@ -161,6 +161,11 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin } } +- (BOOL)isRecipientIdBlocked:(NSString *)recipientId +{ + return [self.blockedPhoneNumbers containsObject:recipientId]; +} + // This should be called every time the block list changes. - (void)handleUpdate diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.m b/SignalServiceKit/src/Messages/OWSMessageReceiver.m index fa5a0ddc5..46d655ff4 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.m +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.m @@ -3,6 +3,7 @@ // #import "OWSMessageReceiver.h" +#import "NSArray+OWS.h" #import "OWSBatchMessageProcessor.h" #import "OWSSignalServiceProtos.pb.h" #import "TSDatabaseView.h" @@ -20,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN @class OWSSignalServiceProtosEnvelope; -@interface OWSMessageProcessingJob : TSYapDatabaseObject +@interface OWSMessageDecryptJob : TSYapDatabaseObject @property (nonatomic, readonly) NSDate *createdAt; @@ -32,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -@interface OWSMessageProcessingJob () +@interface OWSMessageDecryptJob () @property (nonatomic, readonly) NSData *envelopeData; @@ -40,7 +41,12 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - -@implementation OWSMessageProcessingJob +@implementation OWSMessageDecryptJob + ++ (NSString *)collection +{ + return @"OWSMessageProcessingJob"; +} - (instancetype)initWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope { @@ -66,20 +72,16 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Finder -NSString *const OWSMessageProcessingJobFinderExtensionName = @"OWSMessageProcessingJobFinderExtensionName"; -NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProcessingJobFinderExtensionGroup"; +NSString *const OWSMessageDecryptJobFinderExtensionName = @"OWSMessageProcessingJobFinderExtensionName"; +NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessingJobFinderExtensionGroup"; -@interface OWSMessageProcessingJobFinder : NSObject - -- (NSArray *)nextJobsForBatchSize:(NSUInteger)maxBatchSize; -- (void)addJobForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope; -- (void)removeJobWithId:(NSString *)uniqueId; +@interface OWSMessageDecryptJobFinder : NSObject @end #pragma mark - -@interface OWSMessageProcessingJobFinder () +@interface OWSMessageDecryptJobFinder () @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @@ -87,7 +89,7 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces #pragma mark - -@implementation OWSMessageProcessingJobFinder +@implementation OWSMessageDecryptJobFinder - (instancetype)initWithDBConnection:(YapDatabaseConnection *)dbConnection { @@ -103,49 +105,40 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces return self; } -- (NSArray *)nextJobsForBatchSize:(NSUInteger)maxBatchSize +- (NSArray *)nextJobsForBatchSize:(NSUInteger)maxBatchSize { - NSMutableArray *jobs = [NSMutableArray new]; + NSMutableArray *jobs = [NSMutableArray new]; [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) { - YapDatabaseViewTransaction *viewTransaction = [transaction ext:OWSMessageProcessingJobFinderExtensionName]; + YapDatabaseViewTransaction *viewTransaction = [transaction ext:OWSMessageDecryptJobFinderExtensionName]; OWSAssert(viewTransaction != nil); - NSMutableArray *jobIds = [NSMutableArray new]; - [viewTransaction enumerateKeysInGroup:OWSMessageProcessingJobFinderExtensionGroup - usingBlock:^(NSString *_Nonnull collection, - NSString *_Nonnull key, - NSUInteger index, - BOOL *_Nonnull stop) { - [jobIds addObject:key]; - if (jobIds.count >= maxBatchSize) { - *stop = YES; - } - }]; - - for (NSString *jobId in jobIds) { - OWSMessageProcessingJob *_Nullable job = - [OWSMessageProcessingJob fetchObjectWithUniqueID:jobId transaction:transaction]; - if (job) { - [jobs addObject:job]; - } else { - OWSFail(@"Could not load job: %@", jobId); - } - } + [viewTransaction enumerateKeysAndObjectsInGroup:OWSMessageDecryptJobFinderExtensionGroup + usingBlock:^(NSString *_Nonnull collection, + NSString *_Nonnull key, + id _Nonnull object, + NSUInteger index, + BOOL *_Nonnull stop) { + OWSMessageDecryptJob *job = object; + [jobs addObject:job]; + if (jobs.count >= maxBatchSize) { + *stop = YES; + } + }]; }]; - return jobs; + return [jobs copy]; } - (void)addJobForEnvelope:(OWSSignalServiceProtosEnvelope *)envelope { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [[[OWSMessageProcessingJob alloc] initWithEnvelope:envelope] saveWithTransaction:transaction]; + [[[OWSMessageDecryptJob alloc] initWithEnvelope:envelope] saveWithTransaction:transaction]; }]; } -- (void)removeJobWithId:(NSString *)uniqueId +- (void)removeJobsWithIds:(NSArray *)uniqueIds { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { - [transaction removeObjectForKey:uniqueId inCollection:[OWSMessageProcessingJob collection]]; + [transaction removeObjectsForKeys:uniqueIds inCollection:[OWSMessageDecryptJob collection]]; }]; } @@ -161,17 +154,17 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces NSString *key2, id object2) { - if (![object1 isKindOfClass:[OWSMessageProcessingJob class]]) { + if (![object1 isKindOfClass:[OWSMessageDecryptJob class]]) { OWSFail(@"Unexpected object: %@ in collection: %@", [object1 class], collection1); return NSOrderedSame; } - OWSMessageProcessingJob *job1 = (OWSMessageProcessingJob *)object1; + OWSMessageDecryptJob *job1 = (OWSMessageDecryptJob *)object1; - if (![object2 isKindOfClass:[OWSMessageProcessingJob class]]) { + if (![object2 isKindOfClass:[OWSMessageDecryptJob class]]) { OWSFail(@"Unexpected object: %@ in collection: %@", [object2 class], collection2); return NSOrderedSame; } - OWSMessageProcessingJob *job2 = (OWSMessageProcessingJob *)object2; + OWSMessageDecryptJob *job2 = (OWSMessageDecryptJob *)object2; return [job1.createdAt compare:job2.createdAt]; }]; @@ -181,18 +174,18 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces NSString *_Nonnull collection, NSString *_Nonnull key, id _Nonnull object) { - if (![object isKindOfClass:[OWSMessageProcessingJob class]]) { + if (![object isKindOfClass:[OWSMessageDecryptJob class]]) { OWSFail(@"Unexpected object: %@ in collection: %@", object, collection); return nil; } // Arbitrary string - all in the same group. We're only using the view for sorting. - return OWSMessageProcessingJobFinderExtensionGroup; + return OWSMessageDecryptJobFinderExtensionGroup; }]; YapDatabaseViewOptions *options = [YapDatabaseViewOptions new]; options.allowedCollections = - [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[OWSMessageProcessingJob collection]]]; + [[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[OWSMessageDecryptJob collection]]]; return [[YapDatabaseView alloc] initWithGrouping:grouping sorting:sorting versionTag:@"1" options:options]; } @@ -200,40 +193,40 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces + (void)syncRegisterDatabaseExtension:(YapDatabase *)database { - YapDatabaseView *existingView = [database registeredExtension:OWSMessageProcessingJobFinderExtensionName]; + YapDatabaseView *existingView = [database registeredExtension:OWSMessageDecryptJobFinderExtensionName]; if (existingView) { - OWSFail(@"%@ was already initialized.", OWSMessageProcessingJobFinderExtensionName); + OWSFail(@"%@ was already initialized.", OWSMessageDecryptJobFinderExtensionName); // already initialized return; } - [database registerExtension:[self databaseExtension] withName:OWSMessageProcessingJobFinderExtensionName]; + [database registerExtension:[self databaseExtension] withName:OWSMessageDecryptJobFinderExtensionName]; } @end #pragma mark - Queue Processing -@interface OWSMessageProcessingQueue : NSObject +@interface OWSMessageDecryptQueue : NSObject @property (nonatomic, readonly) TSMessagesManager *messagesManager; @property (nonatomic, readonly) OWSBatchMessageProcessor *batchMessageProcessor; -@property (nonatomic, readonly) OWSMessageProcessingJobFinder *finder; +@property (nonatomic, readonly) OWSMessageDecryptJobFinder *finder; @property (nonatomic) BOOL isDrainingQueue; - (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor - finder:(OWSMessageProcessingJobFinder *)finder NS_DESIGNATED_INITIALIZER; + finder:(OWSMessageDecryptJobFinder *)finder NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; @end #pragma mark - -@implementation OWSMessageProcessingQueue +@implementation OWSMessageDecryptQueue - (instancetype)initWithMessagesManager:(TSMessagesManager *)messagesManager batchMessageProcessor:(OWSBatchMessageProcessor *)batchMessageProcessor - finder:(OWSMessageProcessingJobFinder *)finder + finder:(OWSMessageDecryptJobFinder *)finder { OWSSingletonAssert(); @@ -294,7 +287,7 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces { AssertIsOnMainThread(); - NSArray *jobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize]; + NSArray *jobs = [self.finder nextJobsForBatchSize:kIncomingMessageBatchSize]; OWSAssert(jobs); if (jobs.count < 1) { self.isDrainingQueue = NO; @@ -305,19 +298,17 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces [self processJobs:jobs completion:^{ dispatch_async(dispatch_get_main_queue(), ^{ - for (OWSMessageProcessingJob *job in jobs) { - [self.finder removeJobWithId:job.uniqueId]; - } + [self.finder removeJobsWithIds:jobs.uniqueIds]; DDLogVerbose(@"%@ completed %zd jobs. %zd jobs left.", self.tag, jobs.count, - [OWSMessageProcessingJob numberOfKeysInCollection]); + [OWSMessageDecryptJob numberOfKeysInCollection]); [self drainQueueWorkStep]; }); }]; } -- (void)processJobs:(NSArray *)jobs completion:(void (^)())completion +- (void)processJobs:(NSArray *)jobs completion:(void (^)())completion { [self processJobs:jobs unprocessedJobs:[jobs mutableCopy] @@ -325,8 +316,8 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces completion:completion]; } -- (void)processJobs:(NSArray *)jobs - unprocessedJobs:(NSMutableArray *)unprocessedJobs +- (void)processJobs:(NSArray *)jobs + unprocessedJobs:(NSMutableArray *)unprocessedJobs plaintextDataMap:(NSMutableDictionary *)plaintextDataMap completion:(void (^)())completion { @@ -334,7 +325,7 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces OWSAssert(unprocessedJobs.count <= jobs.count); if (unprocessedJobs.count < 1) { - for (OWSMessageProcessingJob *job in jobs) { + for (OWSMessageDecryptJob *job in jobs) { NSData *_Nullable plaintextData = plaintextDataMap[job.uniqueId]; [self.batchMessageProcessor enqueueEnvelopeData:job.envelopeData plaintextData:plaintextData]; } @@ -344,7 +335,7 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ OWSAssert(unprocessedJobs.count > 0); - OWSMessageProcessingJob *job = unprocessedJobs.firstObject; + OWSMessageDecryptJob *job = unprocessedJobs.firstObject; [unprocessedJobs removeObjectAtIndex:0]; [self.messagesManager decryptEnvelope:job.envelopeProto successBlock:^(NSData *_Nullable plaintextData) { @@ -383,7 +374,7 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces @interface OWSMessageReceiver () -@property (nonatomic, readonly) OWSMessageProcessingQueue *processingQueue; +@property (nonatomic, readonly) OWSMessageDecryptQueue *processingQueue; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @end @@ -403,11 +394,11 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces return self; } - OWSMessageProcessingJobFinder *finder = [[OWSMessageProcessingJobFinder alloc] initWithDBConnection:dbConnection]; - OWSMessageProcessingQueue *processingQueue = - [[OWSMessageProcessingQueue alloc] initWithMessagesManager:messagesManager - batchMessageProcessor:batchMessageProcessor - finder:finder]; + OWSMessageDecryptJobFinder *finder = [[OWSMessageDecryptJobFinder alloc] initWithDBConnection:dbConnection]; + OWSMessageDecryptQueue *processingQueue = + [[OWSMessageDecryptQueue alloc] initWithMessagesManager:messagesManager + batchMessageProcessor:batchMessageProcessor + finder:finder]; _processingQueue = processingQueue; @@ -442,7 +433,7 @@ NSString *const OWSMessageProcessingJobFinderExtensionGroup = @"OWSMessageProces + (void)syncRegisterDatabaseExtension:(YapDatabase *)database { - [OWSMessageProcessingJobFinder syncRegisterDatabaseExtension:database]; + [OWSMessageDecryptJobFinder syncRegisterDatabaseExtension:database]; } #pragma mark - instance methods diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.h b/SignalServiceKit/src/Messages/OWSMessageSender.h index d789c5fce..43a2b5047 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.h +++ b/SignalServiceKit/src/Messages/OWSMessageSender.h @@ -65,6 +65,7 @@ NS_SWIFT_NAME(MessageSender) * Send and resend text messages or resend messages with existing attachments. * If you haven't yet created the attachment, see the `sendAttachmentData:` variants. */ +// TODO: make transaction nonnull and remove `sendMessage:success:failure` - (void)sendMessage:(TSOutgoingMessage *)message success:(void (^)())successHandler failure:(void (^)(NSError *error))failureHandler; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index e6d83dafc..1695fcfd6 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -636,8 +636,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // you might, for example, have a pending outgoing message when // you block them. OWSAssert(recipientContactId.length > 0); - NSArray *blockedPhoneNumbers = _blockingManager.blockedPhoneNumbers; - if ([blockedPhoneNumbers containsObject:recipientContactId]) { + if ([_blockingManager isRecipientIdBlocked:recipientContactId]) { DDLogInfo(@"%@ skipping 1:1 send to blocked contact: %@", self.tag, recipientContactId); NSError *error = OWSErrorMakeMessageSendFailedToBlockListError(); // No need to retry - the user will continue to be blocked. diff --git a/SignalServiceKit/src/Messages/TSMessagesManager.m b/SignalServiceKit/src/Messages/TSMessagesManager.m index ded3d074f..869325ad7 100644 --- a/SignalServiceKit/src/Messages/TSMessagesManager.m +++ b/SignalServiceKit/src/Messages/TSMessagesManager.m @@ -275,7 +275,7 @@ const NSUInteger kIncomingMessageBatchSize = 10; { OWSAssert(envelope); - return [_blockingManager.blockedPhoneNumbers containsObject:envelope.source]; + return [_blockingManager isRecipientIdBlocked:envelope.source]; } #pragma mark - Decryption @@ -375,9 +375,6 @@ const NSUInteger kIncomingMessageBatchSize = 10; [self decryptEnvelope:envelope messageTypeName:@"Secure Message" - missingPayloadBlock:^{ - OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]); - } cipherMessageBlock:^(NSData *encryptedData) { return [[WhisperMessage alloc] initWithData:encryptedData]; } @@ -398,9 +395,6 @@ const NSUInteger kIncomingMessageBatchSize = 10; [self decryptEnvelope:envelope messageTypeName:@"PreKey Bundle" - missingPayloadBlock:^{ - OWSProdFail([OWSAnalyticsEvents messageManagerErrorPrekeyBundleEnvelopeHasNoContent]); - } cipherMessageBlock:^(NSData *encryptedData) { return [[PreKeyWhisperMessage alloc] initWithData:encryptedData]; } @@ -410,26 +404,24 @@ const NSUInteger kIncomingMessageBatchSize = 10; - (void)decryptEnvelope:(OWSSignalServiceProtosEnvelope *)envelope messageTypeName:(NSString *)messageTypeName - missingPayloadBlock:(void (^_Nonnull)())missingPayloadBlock cipherMessageBlock:(id (^_Nonnull)(NSData *))cipherMessageBlock successBlock:(DecryptSuccessBlock)successBlock failureBlock:(void (^)(NSError *_Nullable error))failureBlock { OWSAssert(envelope); OWSAssert(messageTypeName.length > 0); - OWSAssert(missingPayloadBlock); OWSAssert(cipherMessageBlock); OWSAssert(successBlock); OWSAssert(failureBlock); - TSStorageManager *storageManager = [TSStorageManager sharedManager]; + 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) { - missingPayloadBlock(); + OWSProdFail([OWSAnalyticsEvents messageManagerErrorMessageEnvelopeHasNoContent]); failureBlock(nil); return; } @@ -468,7 +460,7 @@ const NSUInteger kIncomingMessageBatchSize = 10; OWSAssert(transaction); OWSAssert([TSAccountManager isRegistered]); - DDLogInfo(@"%@ received envelope: %@", self.tag, [self descriptionForEnvelope:envelope]); + DDLogInfo(@"%@ handling decrypted envelope: %@", self.tag, [self descriptionForEnvelope:envelope]); OWSAssert(envelope.source.length > 0); OWSAssert(![self isEnvelopeBlocked:envelope]); @@ -510,6 +502,8 @@ const NSUInteger kIncomingMessageBatchSize = 10; if ([interaction isKindOfClass:[TSOutgoingMessage class]]) { TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)interaction; [outgoingMessage updateWithWasDeliveredWithTransaction:transaction]; + } else { + OWSFail(@"%@ Unexpected message with timestamp: %llu", self.tag, envelope.timestamp); } } @@ -653,7 +647,9 @@ const NSUInteger kIncomingMessageBatchSize = 10; [self.profileManager setProfileKeyData:profileKey forRecipientId:recipientId]; } - // TODO: Should we do this synchronously? + // By dispatching async, we introduce the possibility that these messages might be lost + // if the app exits before this block is executed. This is fine, since the call by + // definition will end if the app exits. dispatch_async(dispatch_get_main_queue(), ^{ if (callMessage.hasOffer) { [self.callMessageHandler receivedOffer:callMessage.offer fromCallerId:envelope.source]; @@ -801,7 +797,7 @@ const NSUInteger kIncomingMessageBatchSize = 10; TSGroupThread *groupThread = [TSGroupThread getOrCreateThreadWithGroupIdData:syncMessage.sent.message.group.id transaction:transaction]; - [groupThread updateAvatarWithAttachmentStream:attachmentStream transaction:transaction]; + [groupThread updateAvatarWithAttachmentStream:attachmentStream]; } transaction:transaction]; } else { @@ -844,6 +840,7 @@ const NSUInteger kIncomingMessageBatchSize = 10; DDLogWarn(@"%@ ignoring unsupported sync request message", self.tag); } } else if (syncMessage.hasBlocked) { + // TODO: Do this synchronously. dispatch_async(dispatch_get_main_queue(), ^{ NSArray *blockedPhoneNumbers = [syncMessage.blocked.numbers copy]; [_blockingManager setBlockedPhoneNumbers:blockedPhoneNumbers sendSyncMessage:NO]; @@ -857,6 +854,7 @@ const NSUInteger kIncomingMessageBatchSize = 10; [readReceiptsProcessor processWithTransaction:transaction]; } else if (syncMessage.hasVerified) { DDLogInfo(@"%@ Received verification state for %@", self.tag, syncMessage.verified.destination); + // TODO: Do this synchronously. dispatch_async(dispatch_get_main_queue(), ^{ [self.identityManager processIncomingSyncMessage:syncMessage.verified]; }); @@ -875,14 +873,12 @@ const NSUInteger kIncomingMessageBatchSize = 10; TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction]; - if (thread) { // TODO thread should always be nonnull. - [[[TSInfoMessage alloc] initWithTimestamp:envelope.timestamp - inThread:thread - messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; - } + [[[TSInfoMessage alloc] initWithTimestamp:envelope.timestamp + inThread:thread + messageType:TSInfoMessageTypeSessionDidEnd] saveWithTransaction:transaction]; dispatch_async([OWSDispatch sessionStoreQueue], ^{ - [[TSStorageManager sharedManager] deleteAllSessionsForContact:envelope.source]; + [self.storageManager deleteAllSessionsForContact:envelope.source]; }); } @@ -1186,6 +1182,7 @@ const NSUInteger kIncomingMessageBatchSize = 10; } if (thread && incomingMessage) { + // TODO: Do this synchronously. dispatch_async(dispatch_get_main_queue(), ^{ // In case we already have a read receipt for this new message (happens sometimes). OWSReadReceiptsProcessor *readReceiptsProcessor = diff --git a/SignalServiceKit/src/Util/NSArray+OWS.h b/SignalServiceKit/src/Util/NSArray+OWS.h new file mode 100644 index 000000000..15c3143f2 --- /dev/null +++ b/SignalServiceKit/src/Util/NSArray+OWS.h @@ -0,0 +1,13 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@interface NSArray (OWS) + +- (NSArray *)uniqueIds; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/NSArray+OWS.m b/SignalServiceKit/src/Util/NSArray+OWS.m new file mode 100644 index 000000000..adc18de36 --- /dev/null +++ b/SignalServiceKit/src/Util/NSArray+OWS.m @@ -0,0 +1,25 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "NSArray+OWS.h" +#import "TSYapDatabaseObject.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation NSArray (OWS) + +- (NSArray *)uniqueIds +{ + NSMutableArray *result = [NSMutableArray new]; + for (id object in self) { + OWSAssert([object isKindOfClass:[TSYapDatabaseObject class]]); + TSYapDatabaseObject *dbObject = object; + [result addObject:dbObject.uniqueId]; + } + return result; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/OWSAnalyticsEvents.h b/SignalServiceKit/src/Util/OWSAnalyticsEvents.h index c331230f2..ff67bcb2e 100755 --- a/SignalServiceKit/src/Util/OWSAnalyticsEvents.h +++ b/SignalServiceKit/src/Util/OWSAnalyticsEvents.h @@ -126,8 +126,6 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)messageManagerErrorOversizeMessage; -+ (NSString *)messageManagerErrorPrekeyBundleEnvelopeHasNoContent; - + (NSString *)messageManagerErrorSyncMessageFromUnknownSource; + (NSString *)messageManagerErrorUntrustedIdentityKeyException; diff --git a/SignalServiceKit/src/Util/OWSAnalyticsEvents.m b/SignalServiceKit/src/Util/OWSAnalyticsEvents.m index 2653b9a8b..f67e9673e 100755 --- a/SignalServiceKit/src/Util/OWSAnalyticsEvents.m +++ b/SignalServiceKit/src/Util/OWSAnalyticsEvents.m @@ -297,11 +297,6 @@ NS_ASSUME_NONNULL_BEGIN return @"message_manager_error_oversize_message"; } -+ (NSString *)messageManagerErrorPrekeyBundleEnvelopeHasNoContent -{ - return @"message_manager_error_prekey_bundle_envelope_has_no_content"; -} - + (NSString *)messageManagerErrorSyncMessageFromUnknownSource { return @"message_manager_error_sync_message_from_unknown_source";