mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
8.5 KiB
Objective-C
264 lines
8.5 KiB
Objective-C
//
|
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
#import "OWSBlockingManager.h"
|
|
#import "OWSBlockedPhoneNumbersMessage.h"
|
|
#import "OWSMessageSender.h"
|
|
#import "TSStorageManager.h"
|
|
#import "TextSecureKitEnv.h"
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
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 kOWSBlockingManager_BlockedPhoneNumbersKey = @"kOWSBlockingManager_BlockedPhoneNumbersKey";
|
|
// This key is used to persist the most recently synced "blocked phone numbers" state.
|
|
NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockingManager_SyncedBlockedPhoneNumbersKey";
|
|
|
|
@interface OWSBlockingManager ()
|
|
|
|
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
|
@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.
|
|
@property (nonatomic, readonly) NSMutableSet<NSString *> *blockedPhoneNumberSet;
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
@implementation OWSBlockingManager
|
|
|
|
+ (instancetype)sharedManager
|
|
{
|
|
static OWSBlockingManager *sharedMyManager = nil;
|
|
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;
|
|
|
|
return [self initWithStorageManager:storageManager messageSender:messageSender];
|
|
}
|
|
|
|
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager messageSender:(OWSMessageSender *)messageSender
|
|
{
|
|
self = [super init];
|
|
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
|
|
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
|
|
{
|
|
OWSAssert(phoneNumber.length > 0);
|
|
|
|
DDLogInfo(@"%@ addBlockedPhoneNumber: %@", self.tag, phoneNumber);
|
|
|
|
@synchronized(self)
|
|
{
|
|
[self lazyLoadBlockedPhoneNumbersIfNecessary];
|
|
|
|
if ([_blockedPhoneNumberSet containsObject:phoneNumber]) {
|
|
// Ignore redundant changes.
|
|
return;
|
|
}
|
|
|
|
[_blockedPhoneNumberSet addObject:phoneNumber];
|
|
}
|
|
|
|
[self handleUpdate];
|
|
}
|
|
|
|
- (void)removeBlockedPhoneNumber:(NSString *)phoneNumber
|
|
{
|
|
OWSAssert(phoneNumber.length > 0);
|
|
|
|
DDLogInfo(@"%@ removeBlockedPhoneNumber: %@", self.tag, phoneNumber);
|
|
|
|
@synchronized(self)
|
|
{
|
|
[self lazyLoadBlockedPhoneNumbersIfNecessary];
|
|
|
|
if (![_blockedPhoneNumberSet containsObject:phoneNumber]) {
|
|
// Ignore redundant changes.
|
|
return;
|
|
}
|
|
|
|
[_blockedPhoneNumberSet removeObject:phoneNumber];
|
|
}
|
|
|
|
[self handleUpdate];
|
|
}
|
|
|
|
- (void)setBlockedPhoneNumbers:(NSArray<NSString *> *)blockedPhoneNumbers sendSyncMessage:(BOOL)sendSyncMessage
|
|
{
|
|
OWSAssert(blockedPhoneNumbers != nil);
|
|
|
|
DDLogInfo(@"%@ setBlockedPhoneNumbers: %d", self.tag, (int)blockedPhoneNumbers.count);
|
|
|
|
@synchronized(self)
|
|
{
|
|
[self lazyLoadBlockedPhoneNumbersIfNecessary];
|
|
|
|
NSSet *newSet = [NSSet setWithArray:blockedPhoneNumbers];
|
|
if ([_blockedPhoneNumberSet isEqualToSet:newSet]) {
|
|
return;
|
|
}
|
|
|
|
_blockedPhoneNumberSet = [newSet mutableCopy];
|
|
}
|
|
|
|
[self handleUpdate:sendSyncMessage];
|
|
}
|
|
|
|
- (NSArray<NSString *> *)blockedPhoneNumbers
|
|
{
|
|
@synchronized(self)
|
|
{
|
|
[self lazyLoadBlockedPhoneNumbersIfNecessary];
|
|
|
|
return [_blockedPhoneNumberSet.allObjects sortedArrayUsingSelector:@selector(compare:)];
|
|
}
|
|
}
|
|
|
|
// This should be called every time the block list changes.
|
|
|
|
- (void)handleUpdate
|
|
{
|
|
// By default, always send a sync message when the block list changes.
|
|
[self handleUpdate:YES];
|
|
}
|
|
|
|
- (void)handleUpdate:(BOOL)sendSyncMessage
|
|
{
|
|
NSArray<NSString *> *blockedPhoneNumbers = [self blockedPhoneNumbers];
|
|
|
|
[_storageManager setObject:blockedPhoneNumbers
|
|
forKey:kOWSBlockingManager_BlockedPhoneNumbersKey
|
|
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (sendSyncMessage) {
|
|
[self sendBlockedPhoneNumbersMessage:blockedPhoneNumbers];
|
|
} else {
|
|
// 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] postNotificationName:kNSNotificationName_BlockedPhoneNumbersDidChange
|
|
object:nil
|
|
userInfo:nil];
|
|
});
|
|
}
|
|
|
|
// This method should only be called from within a synchronized block.
|
|
- (void)lazyLoadBlockedPhoneNumbersIfNecessary
|
|
{
|
|
if (_blockedPhoneNumberSet) {
|
|
// _blockedPhoneNumberSet has already been loaded, abort.
|
|
return;
|
|
}
|
|
|
|
NSArray<NSString *> *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<NSString *> *syncedBlockedPhoneNumbers =
|
|
[_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);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self sendBlockedPhoneNumbersMessage:blockedPhoneNumbers];
|
|
});
|
|
}
|
|
}
|
|
|
|
- (void)sendBlockedPhoneNumbersMessage:(NSArray<NSString *> *)blockedPhoneNumbers
|
|
{
|
|
OWSAssert(blockedPhoneNumbers);
|
|
|
|
OWSBlockedPhoneNumbersMessage *message =
|
|
[[OWSBlockedPhoneNumbersMessage alloc] initWithPhoneNumbers:blockedPhoneNumbers];
|
|
|
|
[self.messageSender sendMessage:message
|
|
success:^{
|
|
DDLogInfo(@"%@ Successfully sent blocked phone numbers sync message", self.tag);
|
|
|
|
// Record the last set of "blocked phone numbers" which we successfully synced.
|
|
[self saveSyncedBlockedPhoneNumbers:blockedPhoneNumbers];
|
|
}
|
|
failure:^(NSError *error) {
|
|
DDLogError(@"%@ Failed to send blocked phone numbers sync message with error: %@", self.tag, error);
|
|
|
|
// TODO: We might want to retry more often than just app launch.
|
|
}];
|
|
}
|
|
|
|
- (void)saveSyncedBlockedPhoneNumbers:(NSArray<NSString *> *)blockedPhoneNumbers
|
|
{
|
|
OWSAssert(blockedPhoneNumbers);
|
|
|
|
// Record the last set of "blocked phone numbers" which we successfully synced.
|
|
[_storageManager setObject:blockedPhoneNumbers
|
|
forKey:kOWSBlockingManager_SyncedBlockedPhoneNumbersKey
|
|
inCollection:kOWSBlockingManager_BlockedPhoneNumbersCollection];
|
|
}
|
|
|
|
#pragma mark - Logging
|
|
|
|
+ (NSString *)tag
|
|
{
|
|
return [NSString stringWithFormat:@"[%@]", self.class];
|
|
}
|
|
|
|
- (NSString *)tag
|
|
{
|
|
return self.class.tag;
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|