Sketch out OWSVerificationManager.

// FREEBIE
pull/1/head
Matthew Chen 9 years ago
parent 43d1aa49dc
commit 9154cc46f0

@ -0,0 +1,30 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
extern NSString *const kNSNotificationName_VerificationStateDidChange;
typedef NS_ENUM(NSUInteger, OWSVerificationState) {
OWSVerificationStateDefault,
OWSVerificationStateVerified,
OWSVerificationStateNoLongerVerified,
};
// This class can be safely accessed and used from any thread.
@interface OWSVerificationManager : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)sharedManager;
- (void)setVerificationState:(OWSVerificationState)verificationState
forPhoneNumber:(NSString *)phoneNumber
isUserInitiatedChange:(BOOL)isUserInitiatedChange;
- (OWSVerificationState)verificationStateForPhoneNumber:(NSString *)phoneNumber;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,304 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "OWSVerificationManager.h"
#import "OWSMessageSender.h"
#import "TSStorageManager.h"
#import "TextSecureKitEnv.h"
NS_ASSUME_NONNULL_BEGIN
NSString *const kNSNotificationName_VerificationStateDidChange = @"kNSNotificationName_VerificationStateDidChange";
NSString *const kOWSVerificationManager_Collection = @"kOWSVerificationManager_Collection";
// This key is used to persist the current "verification map" state.
NSString *const kOWSVerificationManager_VerificationMapKey = @"kOWSVerificationManager_VerificationMapKey";
//// This key is used to persist the most recently synced "blocked phone numbers" state.
//NSString *const kOWSVerificationManager_SyncedBlockedPhoneNumbersKey = @"kOWSVerificationManager_SyncedBlockedPhoneNumbersKey";
NSString *OWSVerificationStateToString(OWSVerificationState verificationState)
{
switch (verificationState) {
case OWSVerificationStateDefault:
return @"OWSVerificationStateDefault";
case OWSVerificationStateVerified:
return @"OWSVerificationStateVerified";
case OWSVerificationStateNoLongerVerified:
return @"OWSVerificationStateNoLongerVerified";
}
}
@interface OWSVerificationManager ()
@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) NSMutableDictionary<NSString *, NSNumber *> *verificationMap;
@end
#pragma mark -
@implementation OWSVerificationManager
+ (instancetype)sharedManager
{
static OWSVerificationManager *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)setVerificationState:(OWSVerificationState)verificationState
forPhoneNumber:(NSString *)phoneNumber
isUserInitiatedChange:(BOOL)isUserInitiatedChange
{
OWSAssert(phoneNumber.length > 0);
DDLogInfo(@"%@ setVerificationState: %@ forPhoneNumber: %@", self.tag, OWSVerificationStateToString(verificationState), phoneNumber);
NSDictionary<NSString *, NSNumber *> *verificationMapCopy = nil;
@synchronized(self)
{
[self lazyLoadStateIfNecessary];
OWSAssert(self.verificationMap);
NSNumber * _Nullable existingValue = self.verificationMap[phoneNumber];
if (existingValue && existingValue.intValue == (int) verificationState) {
// Ignore redundant changes.
return;
}
self.verificationMap[phoneNumber] = @(verificationState);
verificationMapCopy = [self.verificationMap copy];
}
[self handleUpdate:verificationMapCopy
sendSyncMessage:isUserInitiatedChange];
}
- (OWSVerificationState)verificationStateForPhoneNumber:(NSString *)phoneNumber
{
OWSAssert(phoneNumber.length > 0);
@synchronized(self)
{
[self lazyLoadStateIfNecessary];
OWSAssert(self.verificationMap);
NSNumber * _Nullable existingValue = self.verificationMap[phoneNumber];
return (existingValue
? (OWSVerificationState) existingValue.intValue
: OWSVerificationStateDefault);
}
}
//- (void)removeBlockedPhoneNumber:(NSString *)phoneNumber
//{
// OWSAssert(phoneNumber.length > 0);
//
// DDLogInfo(@"%@ removeBlockedPhoneNumber: %@", self.tag, phoneNumber);
//
// @synchronized(self)
// {
// [self lazyLoadStateIfNecessary];
//
// 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 lazyLoadStateIfNecessary];
//
// NSSet *newSet = [NSSet setWithArray:blockedPhoneNumbers];
// if ([_blockedPhoneNumberSet isEqualToSet:newSet]) {
// return;
// }
//
// _blockedPhoneNumberSet = [newSet mutableCopy];
// }
//
// [self handleUpdate:sendSyncMessage];
//}
//
//- (NSArray<NSString *> *)blockedPhoneNumbers
//{
// @synchronized(self)
// {
// [self lazyLoadStateIfNecessary];
//
// 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:(NSDictionary<NSString *, NSNumber *> *)verificationMap
sendSyncMessage:(BOOL)sendSyncMessage
{
OWSAssert(verificationMap);
// NSArray<NSString *> *blockedPhoneNumbers = [self blockedPhoneNumbers];
[_storageManager setObject:verificationMap
forKey:kOWSVerificationManager_VerificationMapKey
inCollection:kOWSVerificationManager_Collection];
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 saveVerificationMap:blockedPhoneNumbers];
// }
[[NSNotificationCenter defaultCenter] postNotificationName:kNSNotificationName_VerificationStateDidChange
object:nil
userInfo:nil];
});
}
// This method should only be called from within a synchronized block.
- (void)lazyLoadStateIfNecessary
{
if (self.verificationMap) {
// verificationMap has already been loaded, abort.
return;
}
NSDictionary<NSString *, NSNumber *> *verificationMap =
[_storageManager objectForKey:kOWSVerificationManager_VerificationMapKey
inCollection:kOWSVerificationManager_Collection];
_verificationMap = (verificationMap ? [verificationMap mutableCopy] : [NSMutableDictionary new]);
// // If we haven't yet successfully synced the current "blocked phone numbers" changes,
// // try again to sync now.
// NSArray<NSString *> *syncedBlockedPhoneNumbers =
// [_storageManager objectForKey:kOWSVerificationManager_SyncedBlockedPhoneNumbersKey
// inCollection:kOWSVerificationManager_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 saveVerificationMap: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)saveSyncedVerificationMap:(NSArray<NSString *> *)verificationMap
//{
// OWSAssert(blockedPhoneNumbers);
//
// // Record the last set of "blocked phone numbers" which we successfully synced.
// [_storageManager setObject:verificationMap
// forKey:kOWSVerificationManager_VerificationMapKey
// inCollection:kOWSVerificationManager_Collection];
//}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END
Loading…
Cancel
Save