diff --git a/src/Messages/TSBlockingManager.h b/src/Messages/OWSBlockingManager.h similarity index 85% rename from src/Messages/TSBlockingManager.h rename to src/Messages/OWSBlockingManager.h index 3a537884a..ab7832995 100644 --- a/src/Messages/TSBlockingManager.h +++ b/src/Messages/OWSBlockingManager.h @@ -7,10 +7,10 @@ NS_ASSUME_NONNULL_BEGIN @class TSStorageManager; @class OWSMessageSender; -extern NSString * const kNSNotificationName_BlockedPhoneNumbersDidChange; +extern NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange; // This class can be safely accessed and used from any thread. -@interface TSBlockingManager : NSObject +@interface OWSBlockingManager : NSObject - (instancetype)init NS_UNAVAILABLE; diff --git a/src/Messages/TSBlockingManager.m b/src/Messages/OWSBlockingManager.m similarity index 77% rename from src/Messages/TSBlockingManager.m rename to src/Messages/OWSBlockingManager.m index c10357402..4549eb6fc 100644 --- a/src/Messages/TSBlockingManager.m +++ b/src/Messages/OWSBlockingManager.m @@ -2,7 +2,7 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // -#import "TSBlockingManager.h" +#import "OWSBlockingManager.h" #import "OWSBlockedPhoneNumbersMessage.h" #import "OWSMessageSender.h" #import "TSStorageManager.h" @@ -10,14 +10,14 @@ NS_ASSUME_NONNULL_BEGIN -NSString * const kNSNotificationName_BlockedPhoneNumbersDidChange = @"kNSNotificationName_BlockedPhoneNumbersDidChange"; -NSString * const kTSStorageManager_BlockedPhoneNumbersCollection = @"kTSStorageManager_BlockedPhoneNumbersCollection"; +NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange = @"kNSNotificationName_BlockedPhoneNumbersDidChange"; +NSString *const kOWSBlockingManager_BlockedPhoneNumbersCollection = @"kOWSBlockingManager_BlockedPhoneNumbersCollection"; // This key is used to persist the current "blocked phone numbers" state. -NSString * const kTSStorageManager_BlockedPhoneNumbersKey = @"kTSStorageManager_BlockedPhoneNumbersKey"; +NSString *const kOWSBlockingManager_BlockedPhoneNumbersKey = @"kOWSBlockingManager_BlockedPhoneNumbersKey"; // This key is used to persist the most recently synced "blocked phone numbers" state. -NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageManager_SyncedBlockedPhoneNumbersKey"; +NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockingManager_SyncedBlockedPhoneNumbersKey"; -@interface TSBlockingManager () +@interface OWSBlockingManager () @property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; @@ -31,11 +31,11 @@ NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageMan #pragma mark - -@implementation TSBlockingManager +@implementation OWSBlockingManager + (instancetype)sharedManager { - static TSBlockingManager *sharedMyManager = nil; + static OWSBlockingManager *sharedMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedMyManager = [[self alloc] initDefault]; @@ -48,11 +48,10 @@ NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageMan TSStorageManager *storageManager = [TSStorageManager sharedManager]; OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender; - return [self initStorageManager:storageManager - messageSender:messageSender]; + return [self initWithStorageManager:storageManager messageSender:messageSender]; } -- (instancetype)initStorageManager:(TSStorageManager *)storageManager messageSender:(OWSMessageSender *)messageSender +- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager messageSender:(OWSMessageSender *)messageSender { self = [super init]; @@ -62,47 +61,55 @@ NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageMan OWSAssert(storageManager); OWSAssert(messageSender); - + _storageManager = storageManager; _messageSender = messageSender; OWSSingletonAssert(); + // Register this manager with the message sender. + // This is a circular dependency. + [messageSender setBlockingManager:self]; + return self; } -- (void)addBlockedPhoneNumber:(NSString *)phoneNumber { +- (void)addBlockedPhoneNumber:(NSString *)phoneNumber +{ OWSAssert(phoneNumber.length > 0); DDLogInfo(@"%@ addBlockedPhoneNumber: %@", self.tag, phoneNumber); - @synchronized (self) { + @synchronized(self) + { [self lazyLoadBlockedPhoneNumbersIfNecessary]; if ([_blockedPhoneNumberSet containsObject:phoneNumber]) { // Ignore redundant changes. return; } - + [_blockedPhoneNumberSet addObject:phoneNumber]; } [self handleUpdate]; } -- (void)removeBlockedPhoneNumber:(NSString *)phoneNumber { +- (void)removeBlockedPhoneNumber:(NSString *)phoneNumber +{ OWSAssert(phoneNumber.length > 0); DDLogInfo(@"%@ removeBlockedPhoneNumber: %@", self.tag, phoneNumber); - @synchronized (self) { + @synchronized(self) + { [self lazyLoadBlockedPhoneNumbersIfNecessary]; if (![_blockedPhoneNumberSet containsObject:phoneNumber]) { // Ignore redundant changes. return; } - + [_blockedPhoneNumberSet removeObject:phoneNumber]; } @@ -115,22 +122,25 @@ NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageMan DDLogInfo(@"%@ setBlockedPhoneNumbers: %d", self.tag, (int)blockedPhoneNumbers.count); - @synchronized (self) { + @synchronized(self) + { [self lazyLoadBlockedPhoneNumbersIfNecessary]; NSSet *newSet = [NSSet setWithArray:blockedPhoneNumbers]; if ([_blockedPhoneNumberSet isEqualToSet:newSet]) { return; } - + _blockedPhoneNumberSet = [newSet mutableCopy]; } [self handleUpdate:sendSyncMessage]; } -- (NSArray *)blockedPhoneNumbers { - @synchronized (self) { +- (NSArray *)blockedPhoneNumbers +{ + @synchronized(self) + { [self lazyLoadBlockedPhoneNumbersIfNecessary]; return [_blockedPhoneNumberSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; @@ -150,8 +160,8 @@ NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageMan NSArray *blockedPhoneNumbers = [self blockedPhoneNumbers]; [_storageManager setObject:blockedPhoneNumbers - forKey:kTSStorageManager_BlockedPhoneNumbersKey - inCollection:kTSStorageManager_BlockedPhoneNumbersCollection]; + forKey:kOWSBlockingManager_BlockedPhoneNumbersKey + inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection]; dispatch_async(dispatch_get_main_queue(), ^{ if (sendSyncMessage) { @@ -186,15 +196,16 @@ NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageMan return; } - NSArray *blockedPhoneNumbers = [_storageManager objectForKey:kTSStorageManager_BlockedPhoneNumbersKey - inCollection:kTSStorageManager_BlockedPhoneNumbersCollection]; + NSArray *blockedPhoneNumbers = + [_storageManager objectForKey:kOWSBlockingManager_BlockedPhoneNumbersKey + inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection]; _blockedPhoneNumberSet = [[NSMutableSet alloc] initWithArray:(blockedPhoneNumbers ?: [NSArray new])]; // If we haven't yet successfully synced the current "blocked phone numbers" changes, // try again to sync now. NSArray *syncedBlockedPhoneNumbers = - [_storageManager objectForKey:kTSStorageManager_SyncedBlockedPhoneNumbersKey - inCollection:kTSStorageManager_BlockedPhoneNumbersCollection]; + [_storageManager objectForKey:kOWSBlockingManager_SyncedBlockedPhoneNumbersKey + inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection]; NSSet *syncedBlockedPhoneNumberSet = [[NSSet alloc] initWithArray:(syncedBlockedPhoneNumbers ?: [NSArray new])]; if (![_blockedPhoneNumberSet isEqualToSet:syncedBlockedPhoneNumberSet]) { DDLogInfo(@"%@ retrying sync of blocked phone numbers", self.tag); @@ -231,8 +242,8 @@ NSString *const kTSStorageManager_SyncedBlockedPhoneNumbersKey = @"kTSStorageMan // Record the last set of "blocked phone numbers" which we successfully synced. [_storageManager setObject:blockedPhoneNumbers - forKey:kTSStorageManager_SyncedBlockedPhoneNumbersKey - inCollection:kTSStorageManager_BlockedPhoneNumbersCollection]; + forKey:kOWSBlockingManager_SyncedBlockedPhoneNumbersKey + inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection]; } #pragma mark - Logging diff --git a/src/Messages/OWSMessageSender.h b/src/Messages/OWSMessageSender.h index 7d8c865bf..3cc4653d8 100644 --- a/src/Messages/OWSMessageSender.h +++ b/src/Messages/OWSMessageSender.h @@ -7,6 +7,7 @@ NS_ASSUME_NONNULL_BEGIN @class ContactsUpdater; @class OWSUploadingService; @class SignalRecipient; +@class OWSBlockingManager; @class TSInvalidIdentityKeySendingErrorMessage; @class TSNetworkManager; @class TSOutgoingMessage; @@ -31,6 +32,8 @@ NS_SWIFT_NAME(MessageSender) contactsManager:(id)contactsManager contactsUpdater:(ContactsUpdater *)contactsUpdater; +- (void)setBlockingManager:(OWSBlockingManager *)blockingManager; + /** * Send and resend text messages or resend messages with existing attachments. * If you haven't yet created the attachment, see the `sendAttachmentData:` variants. diff --git a/src/Messages/OWSMessageSender.m b/src/Messages/OWSMessageSender.m index 136f59c9b..e45e2a1c7 100644 --- a/src/Messages/OWSMessageSender.m +++ b/src/Messages/OWSMessageSender.m @@ -5,6 +5,7 @@ #import "OWSMessageSender.h" #import "ContactsUpdater.h" #import "NSData+messagePadding.h" +#import "OWSBlockingManager.h" #import "OWSDevice.h" #import "OWSDisappearingMessagesJob.h" #import "OWSError.h" @@ -257,6 +258,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; @property (nonatomic, readonly) TSNetworkManager *networkManager; @property (nonatomic, readonly) TSStorageManager *storageManager; +@property (nonatomic, readonly) OWSBlockingManager *blockingManager; @property (nonatomic, readonly) OWSUploadingService *uploadingService; @property (nonatomic, readonly) YapDatabaseConnection *dbConnection; @property (nonatomic, readonly) id contactsManager; @@ -293,6 +295,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; return self; } +- (void)setBlockingManager:(OWSBlockingManager *)blockingManager +{ + OWSAssert(blockingManager); + OWSAssert(!_blockingManager); + + _blockingManager = blockingManager; +} + - (NSOperationQueue *)sendingQueueForMessage:(TSOutgoingMessage *)message { OWSAssert(message); @@ -516,10 +526,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; if (recipients.count == 0) { if (error) { - return failureHandler(error); + failureHandler(error); + return; } else { DDLogError(@"%@ Unknown error finding contacts", self.tag); - return failureHandler(OWSErrorMakeFailedToSendOutgoingMessageError()); + failureHandler(OWSErrorMakeFailedToSendOutgoingMessageError()); + return; } } @@ -541,6 +553,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; ? self.storageManager.localNumber : contactThread.contactIdentifier; + OWSAssert(recipientContactId.length > 0); + NSArray *blockedPhoneNumbers = _blockingManager.blockedPhoneNumbers; + if ([blockedPhoneNumbers containsObject:recipientContactId]) { + DDLogInfo(@"%@ skipping 1:1 send to blocked contact: %@", self.tag, recipientContactId); + NSError *error = OWSErrorMakeMessageSendFailedToBlocklistError(); + failureHandler(error); + return; + } + SignalRecipient *recipient = [SignalRecipient recipientWithTextSecureIdentifier:recipientContactId]; if (!recipient) { NSError *error; @@ -554,14 +575,16 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } DDLogError(@"%@ contact lookup failed with error: %@", self.tag, error); - return failureHandler(error); + failureHandler(error); + return; } } if (!recipient) { NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError(); DDLogWarn(@"recipient contact still not found after attempting lookup."); - return failureHandler(error); + failureHandler(error); + return; } [self sendMessage:message @@ -609,11 +632,31 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [self saveGroupMessage:message inThread:thread]; NSMutableArray *futures = [NSMutableArray array]; + NSArray *blockedPhoneNumbers = _blockingManager.blockedPhoneNumbers; + int blockedCount = 0; for (SignalRecipient *rec in recipients) { - // we don't need to send the message to ourselves, but otherwise we send - if (![[rec uniqueId] isEqualToString:[TSStorageManager localNumber]]) { - [futures addObject:[self sendMessageFuture:message recipient:rec thread:thread]]; + // We don't need to send the message to ourselves... + if ([rec.uniqueId isEqualToString:[TSStorageManager localNumber]]) { + continue; } + // ...or to anyone on our blocklist... + OWSAssert(rec.uniqueId.length > 0); + if ([blockedPhoneNumbers containsObject:rec.uniqueId]) { + DDLogInfo(@"%@ skipping group send to blocked contact: %@", self.tag, rec.uniqueId); + blockedCount++; + continue; + } + + // ...otherwise we send. + [futures addObject:[self sendMessageFuture:message recipient:rec thread:thread]]; + } + + // If all recipients in the group are in our blocklist, + // there's nothing to do. + if (blockedCount > 0 && futures.count == 0) { + NSError *error = OWSErrorMakeMessageSendFailedToBlocklistError(); + failureHandler(error); + return; } TOCFuture *completionFuture = futures.toc_thenAll; diff --git a/src/Messages/TSMessagesManager.m b/src/Messages/TSMessagesManager.m index 4ea23fc6d..ddb643a6f 100644 --- a/src/Messages/TSMessagesManager.m +++ b/src/Messages/TSMessagesManager.m @@ -10,6 +10,7 @@ #import "NSDate+millisecondTimeStamp.h" #import "NotificationsProtocol.h" #import "OWSAttachmentsProcessor.h" +#import "OWSBlockingManager.h" #import "OWSCallMessageHandler.h" #import "OWSDisappearingConfigurationUpdateInfoMessage.h" #import "OWSDisappearingMessagesConfiguration.h" @@ -37,7 +38,6 @@ #import "TextSecureKitEnv.h" #import #import -#import "TSBlockingManager.h" NS_ASSUME_NONNULL_BEGIN @@ -49,7 +49,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob; @property (nonatomic, readonly) OWSIncomingMessageFinder *incomingMessageFinder; -@property (nonatomic, readonly) TSBlockingManager *blockingManager; +@property (nonatomic, readonly) OWSBlockingManager *blockingManager; @end @@ -104,7 +104,7 @@ NS_ASSUME_NONNULL_BEGIN _dbConnection = storageManager.newDatabaseConnection; _disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:storageManager]; _incomingMessageFinder = [[OWSIncomingMessageFinder alloc] initWithDatabase:storageManager.database]; - _blockingManager = [TSBlockingManager sharedManager]; + _blockingManager = [OWSBlockingManager sharedManager]; OWSSingletonAssert(); @@ -157,6 +157,14 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert([NSThread isMainThread]); DDLogInfo(@"%@ received envelope: %@", self.tag, [self descriptionForEnvelope:envelope]); + + OWSAssert(envelope.source.length > 0); + BOOL isEnvelopeBlocked = [_blockingManager.blockedPhoneNumbers containsObject:envelope.source]; + if (isEnvelopeBlocked) { + DDLogInfo(@"%@ ignoring blocked envelope: %@", self.tag, envelope.source); + return; + } + @try { switch (envelope.type) { case OWSSignalServiceProtosEnvelopeTypeCiphertext: { diff --git a/src/Util/OWSError.h b/src/Util/OWSError.h index c3d400945..22605621e 100644 --- a/src/Util/OWSError.h +++ b/src/Util/OWSError.h @@ -22,6 +22,7 @@ typedef NS_ENUM(NSInteger, OWSErrorCode) { OWSErrorCodeUserError = 2001, OWSErrorCodeNoSuchSignalRecipient = 777404, OWSErrorCodeMessageSendDisabledDueToPreKeyUpdateFailures = 777405, + OWSErrorCodeMessageSendFailedToBlocklist = 777406, }; extern NSError *OWSErrorWithCodeDescription(OWSErrorCode code, NSString *description); @@ -30,5 +31,6 @@ extern NSError *OWSErrorMakeFailedToSendOutgoingMessageError(); extern NSError *OWSErrorMakeNoSuchSignalRecipientError(); extern NSError *OWSErrorMakeAssertionError(); extern NSError *OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError(); +extern NSError *OWSErrorMakeMessageSendFailedToBlocklistError(); NS_ASSUME_NONNULL_END diff --git a/src/Util/OWSError.m b/src/Util/OWSError.m index e423070ee..cb0bde385 100644 --- a/src/Util/OWSError.m +++ b/src/Util/OWSError.m @@ -47,4 +47,11 @@ NSError *OWSErrorMakeMessageSendDisabledDueToPreKeyUpdateFailuresError() @"Error mesage indicating that message send is disabled due to prekey update failures")); } +NSError *OWSErrorMakeMessageSendFailedToBlocklistError() +{ + return OWSErrorWithCodeDescription(OWSErrorCodeMessageSendFailedToBlocklist, + NSLocalizedString(@"ERROR_DESCRIPTION_MESSAGE_SEND_FAILED_DUE_TO_BLOCKLIST", + @"Error mesage indicating that message send failed due to blocklist")); +} + NS_ASSUME_NONNULL_END