session-ios/SignalServiceKit/src/Messages/OWSBlockingManager.m

300 lines
9.5 KiB
Mathematica
Raw Normal View History

2017-03-31 02:04:19 +02:00
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
2017-03-31 02:04:19 +02:00
//
2017-04-03 20:42:04 +02:00
#import "OWSBlockingManager.h"
#import "AppContext.h"
#import "NSNotificationCenter+OWS.h"
2017-03-31 02:04:19 +02:00
#import "OWSBlockedPhoneNumbersMessage.h"
#import "OWSMessageSender.h"
#import "TSStorageManager.h"
#import "TextSecureKitEnv.h"
2017-12-19 03:42:50 +01:00
#import "YapDatabaseConnection+OWS.h"
2017-03-31 02:04:19 +02:00
NS_ASSUME_NONNULL_BEGIN
2017-04-03 20:42:04 +02:00
NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange = @"kNSNotificationName_BlockedPhoneNumbersDidChange";
2017-04-03 20:42:04 +02:00
NSString *const kOWSBlockingManager_BlockedPhoneNumbersCollection = @"kOWSBlockingManager_BlockedPhoneNumbersCollection";
2017-03-31 02:04:19 +02:00
// This key is used to persist the current "blocked phone numbers" state.
2017-04-03 20:42:04 +02:00
NSString *const kOWSBlockingManager_BlockedPhoneNumbersKey = @"kOWSBlockingManager_BlockedPhoneNumbersKey";
2017-03-31 02:04:19 +02:00
// This key is used to persist the most recently synced "blocked phone numbers" state.
2017-04-03 20:42:04 +02:00
NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockingManager_SyncedBlockedPhoneNumbersKey";
2017-03-31 02:04:19 +02:00
2017-04-03 20:42:04 +02:00
@interface OWSBlockingManager ()
2017-03-31 02:04:19 +02:00
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
2017-03-31 02:04:19 +02:00
@property (nonatomic, readonly) OWSMessageSender *messageSender;
// We don't store the phone numbers as instances of PhoneNumber to avoid
// consistency issues between clients, but these should all be valid e164
// phone numbers.
2017-06-07 23:48:12 +02:00
@property (atomic, readonly) NSMutableSet<NSString *> *blockedPhoneNumberSet;
2017-03-31 02:04:19 +02:00
@end
#pragma mark -
2017-04-03 20:42:04 +02:00
@implementation OWSBlockingManager
2017-03-31 02:04:19 +02:00
2017-03-31 19:43:05 +02:00
+ (instancetype)sharedManager
{
2017-04-03 20:42:04 +02:00
static OWSBlockingManager *sharedMyManager = nil;
2017-03-31 02:04:19 +02:00
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] initDefault];
});
return sharedMyManager;
}
- (instancetype)initDefault
{
TSStorageManager *storageManager = [TSStorageManager sharedManager];
OWSMessageSender *messageSender = [TextSecureKitEnv sharedEnv].messageSender;
2017-04-03 20:42:04 +02:00
return [self initWithStorageManager:storageManager messageSender:messageSender];
2017-03-31 02:04:19 +02:00
}
2017-04-03 20:42:04 +02:00
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager messageSender:(OWSMessageSender *)messageSender
2017-03-31 02:04:19 +02:00
{
self = [super init];
if (!self) {
return self;
}
OWSAssert(storageManager);
OWSAssert(messageSender);
2017-04-03 20:42:04 +02:00
_dbConnection = storageManager.newDatabaseConnection;
2017-03-31 02:04:19 +02:00
_messageSender = messageSender;
OWSSingletonAssert();
// Register this manager with the message sender.
// This is a circular dependency.
[messageSender setBlockingManager:self];
2017-03-31 02:04:19 +02:00
return self;
}
2017-06-06 23:43:41 +02:00
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:OWSApplicationDidBecomeActiveNotification
2017-06-06 23:43:41 +02:00
object:nil];
}
2017-04-03 20:42:04 +02:00
- (void)addBlockedPhoneNumber:(NSString *)phoneNumber
{
2017-03-31 02:04:19 +02:00
OWSAssert(phoneNumber.length > 0);
2017-11-08 20:04:51 +01:00
DDLogInfo(@"%@ addBlockedPhoneNumber: %@", self.logTag, phoneNumber);
2017-04-03 20:42:04 +02:00
@synchronized(self)
{
2017-06-07 23:48:12 +02:00
[self ensureLazyInitialization];
2017-03-31 19:43:05 +02:00
2017-03-31 02:04:19 +02:00
if ([_blockedPhoneNumberSet containsObject:phoneNumber]) {
2017-03-31 19:43:05 +02:00
// Ignore redundant changes.
2017-03-31 02:04:19 +02:00
return;
}
2017-04-03 20:42:04 +02:00
2017-03-31 02:04:19 +02:00
[_blockedPhoneNumberSet addObject:phoneNumber];
}
2017-03-31 19:43:05 +02:00
[self handleUpdate];
2017-03-31 02:04:19 +02:00
}
2017-04-03 20:42:04 +02:00
- (void)removeBlockedPhoneNumber:(NSString *)phoneNumber
{
2017-03-31 02:04:19 +02:00
OWSAssert(phoneNumber.length > 0);
2017-11-08 20:04:51 +01:00
DDLogInfo(@"%@ removeBlockedPhoneNumber: %@", self.logTag, phoneNumber);
2017-04-03 20:42:04 +02:00
@synchronized(self)
{
2017-06-07 23:48:12 +02:00
[self ensureLazyInitialization];
2017-03-31 19:43:05 +02:00
2017-03-31 02:04:19 +02:00
if (![_blockedPhoneNumberSet containsObject:phoneNumber]) {
2017-03-31 19:43:05 +02:00
// Ignore redundant changes.
2017-03-31 02:04:19 +02:00
return;
}
2017-04-03 20:42:04 +02:00
2017-03-31 02:04:19 +02:00
[_blockedPhoneNumberSet removeObject:phoneNumber];
}
2017-03-31 19:43:05 +02:00
[self handleUpdate];
2017-03-31 02:04:19 +02:00
}
2017-03-31 19:43:05 +02:00
- (void)setBlockedPhoneNumbers:(NSArray<NSString *> *)blockedPhoneNumbers sendSyncMessage:(BOOL)sendSyncMessage
{
2017-03-31 02:04:19 +02:00
OWSAssert(blockedPhoneNumbers != nil);
2017-11-08 20:04:51 +01:00
DDLogInfo(@"%@ setBlockedPhoneNumbers: %d", self.logTag, (int)blockedPhoneNumbers.count);
2017-04-03 20:42:04 +02:00
@synchronized(self)
{
2017-06-07 23:48:12 +02:00
[self ensureLazyInitialization];
2017-03-31 19:43:05 +02:00
2017-03-31 02:04:19 +02:00
NSSet *newSet = [NSSet setWithArray:blockedPhoneNumbers];
if ([_blockedPhoneNumberSet isEqualToSet:newSet]) {
return;
}
2017-04-03 20:42:04 +02:00
2017-03-31 02:04:19 +02:00
_blockedPhoneNumberSet = [newSet mutableCopy];
}
2017-03-31 19:43:05 +02:00
[self handleUpdate:sendSyncMessage];
2017-03-31 02:04:19 +02:00
}
2017-04-03 20:42:04 +02:00
- (NSArray<NSString *> *)blockedPhoneNumbers
{
@synchronized(self)
{
2017-06-07 23:48:12 +02:00
[self ensureLazyInitialization];
2017-03-31 19:43:05 +02:00
2017-03-31 02:04:19 +02:00
return [_blockedPhoneNumberSet.allObjects sortedArrayUsingSelector:@selector(compare:)];
}
}
2017-09-20 17:48:37 +02:00
- (BOOL)isRecipientIdBlocked:(NSString *)recipientId
{
return [self.blockedPhoneNumbers containsObject:recipientId];
}
2017-03-31 02:04:19 +02:00
// This should be called every time the block list changes.
2017-03-31 19:43:05 +02:00
- (void)handleUpdate
{
// By default, always send a sync message when the block list changes.
[self handleUpdate:YES];
}
- (void)handleUpdate:(BOOL)sendSyncMessage
{
2017-03-31 02:04:19 +02:00
NSArray<NSString *> *blockedPhoneNumbers = [self blockedPhoneNumbers];
2017-03-31 19:43:05 +02:00
[self.dbConnection setObject:blockedPhoneNumbers
forKey:kOWSBlockingManager_BlockedPhoneNumbersKey
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
2017-03-31 02:04:19 +02:00
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
2017-03-31 19:43:05 +02:00
if (sendSyncMessage) {
[self sendBlockedPhoneNumbersMessage:blockedPhoneNumbers];
} else {
2017-04-05 23:34:47 +02:00
// If this update came from an incoming block list sync message,
// update the "synced blocked phone numbers" state immediately,
// since we're now in sync.
//
// There could be data loss if both clients modify the block list
// at the same time, but:
//
// a) Block list changes will be rare.
// b) Conflicting block list changes will be even rarer.
// c) It's unlikely a user will make conflicting changes on two
// devices around the same time.
// d) There isn't a good way to avoid this.
[self saveSyncedBlockedPhoneNumbers:blockedPhoneNumbers];
}
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_BlockedPhoneNumbersDidChange
object:nil
userInfo:nil];
2017-03-31 02:04:19 +02:00
});
}
2017-03-31 19:43:05 +02:00
// This method should only be called from within a synchronized block.
2017-06-07 23:48:12 +02:00
- (void)ensureLazyInitialization
2017-03-31 02:04:19 +02:00
{
2017-03-31 19:43:05 +02:00
if (_blockedPhoneNumberSet) {
// _blockedPhoneNumberSet has already been loaded, abort.
return;
}
2017-03-31 02:04:19 +02:00
2017-04-03 20:42:04 +02:00
NSArray<NSString *> *blockedPhoneNumbers =
[self.dbConnection objectForKey:kOWSBlockingManager_BlockedPhoneNumbersKey
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
2017-03-31 02:04:19 +02:00
_blockedPhoneNumberSet = [[NSMutableSet alloc] initWithArray:(blockedPhoneNumbers ?: [NSArray new])];
2017-06-06 23:43:41 +02:00
[self syncBlockedPhoneNumbersIfNecessary];
[self observeNotifications];
}
- (void)syncBlockedPhoneNumbers
{
OWSAssert(_blockedPhoneNumberSet);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self sendBlockedPhoneNumbersMessage:self.blockedPhoneNumbers];
});
}
2017-06-06 23:43:41 +02:00
// This method should only be called from within a synchronized block.
- (void)syncBlockedPhoneNumbersIfNecessary
{
OWSAssert(_blockedPhoneNumberSet);
2017-03-31 02:04:19 +02:00
// If we haven't yet successfully synced the current "blocked phone numbers" changes,
// try again to sync now.
NSArray<NSString *> *syncedBlockedPhoneNumbers =
[self.dbConnection objectForKey:kOWSBlockingManager_SyncedBlockedPhoneNumbersKey
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
2017-03-31 02:04:19 +02:00
NSSet *syncedBlockedPhoneNumberSet = [[NSSet alloc] initWithArray:(syncedBlockedPhoneNumbers ?: [NSArray new])];
if (![_blockedPhoneNumberSet isEqualToSet:syncedBlockedPhoneNumberSet]) {
2017-11-08 20:04:51 +01:00
DDLogInfo(@"%@ retrying sync of blocked phone numbers", self.logTag);
[self sendBlockedPhoneNumbersMessage:self.blockedPhoneNumbers];
2017-03-31 02:04:19 +02:00
}
}
- (void)sendBlockedPhoneNumbersMessage:(NSArray<NSString *> *)blockedPhoneNumbers
{
OWSAssert(blockedPhoneNumbers);
OWSBlockedPhoneNumbersMessage *message =
[[OWSBlockedPhoneNumbersMessage alloc] initWithPhoneNumbers:blockedPhoneNumbers];
2017-11-15 19:21:31 +01:00
[self.messageSender enqueueMessage:message
2017-03-31 02:04:19 +02:00
success:^{
2017-11-08 20:04:51 +01:00
DDLogInfo(@"%@ Successfully sent blocked phone numbers sync message", self.logTag);
2017-03-31 02:04:19 +02:00
// Record the last set of "blocked phone numbers" which we successfully synced.
[self saveSyncedBlockedPhoneNumbers:blockedPhoneNumbers];
2017-03-31 02:04:19 +02:00
}
failure:^(NSError *error) {
2017-11-08 20:04:51 +01:00
DDLogError(@"%@ Failed to send blocked phone numbers sync message with error: %@", self.logTag, error);
2017-03-31 02:04:19 +02:00
}];
}
- (void)saveSyncedBlockedPhoneNumbers:(NSArray<NSString *> *)blockedPhoneNumbers
{
OWSAssert(blockedPhoneNumbers);
// Record the last set of "blocked phone numbers" which we successfully synced.
[self.dbConnection setObject:blockedPhoneNumbers
forKey:kOWSBlockingManager_SyncedBlockedPhoneNumbersKey
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
}
2017-06-06 23:43:41 +02:00
#pragma mark - Notifications
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
2017-12-19 17:38:25 +01:00
OWSAssertIsOnMainThread();
2017-06-06 23:43:41 +02:00
2017-06-07 23:48:12 +02:00
@synchronized(self)
{
[self syncBlockedPhoneNumbersIfNecessary];
}
2017-06-06 23:43:41 +02:00
}
2017-03-31 02:04:19 +02:00
@end
NS_ASSUME_NONNULL_END