session-ios/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterM...

161 lines
6.4 KiB
Objective-C

#import "PhoneNumberDirectoryFilterManager.h"
#import "ContactsManager.h"
#import "Cryptography.h"
#import "Environment.h"
#import "NotificationManifest.h"
#import "PreferencesUtil.h"
#import "RPServerRequestsManager.h"
#import "SignalUtil.h"
#import "ThreadManager.h"
#import "TSContactsIntersectionRequest.h"
#import "TSStorageManager.h"
#import "TSRecipient.h"
#import "Util.h"
#define MINUTE (60.0)
#define HOUR (MINUTE*60.0)
#define DIRECTORY_UPDATE_TIMEOUT_PERIOD (1.0*MINUTE)
#define DIRECTORY_UPDATE_RETRY_PERIOD (1.0*HOUR)
@implementation PhoneNumberDirectoryFilterManager {
@private TOCCancelTokenSource* currentUpdateLifetime;
}
- (id)init {
if (self = [super init]) {
phoneNumberDirectoryFilter = PhoneNumberDirectoryFilter.phoneNumberDirectoryFilterDefault;
_isRefreshing = NO;
}
return self;
}
- (void)startUntilCancelled:(TOCCancelToken*)cancelToken {
lifetimeToken = cancelToken;
phoneNumberDirectoryFilter = [Environment.preferences tryGetSavedPhoneNumberDirectory];
if (phoneNumberDirectoryFilter == nil) {
phoneNumberDirectoryFilter = PhoneNumberDirectoryFilter.phoneNumberDirectoryFilterDefault;
}
[self scheduleUpdate];
}
- (PhoneNumberDirectoryFilter*)getCurrentFilter {
@synchronized(self) {
return phoneNumberDirectoryFilter;
}
}
- (void)forceUpdate {
[self scheduleUpdateAt:NSDate.date];
}
- (void)scheduleUpdate {
return [self scheduleUpdateAt:self.getCurrentFilter.getExpirationDate];
}
- (void)scheduleUpdateAt:(NSDate*)date {
void(^doUpdate)(void) = ^{
if (Environment.isRedPhoneRegistered) {
[self updateRedPhone];
}
};
[currentUpdateLifetime cancel];
currentUpdateLifetime = [TOCCancelTokenSource new];
[lifetimeToken whenCancelledDo:^{ [currentUpdateLifetime cancel]; }];
[TimeUtil scheduleRun:doUpdate
at:date
onRunLoop:[ThreadManager normalLatencyThreadRunLoop]
unlessCancelled:currentUpdateLifetime.token];
}
- (void) updateRedPhone {
_isRefreshing = YES;
[[RPServerRequestsManager sharedInstance] performRequest:[RPAPICall fetchBloomFilter] success:^(NSURLSessionDataTask *task, id responseObject) {
PhoneNumberDirectoryFilter *directory = [PhoneNumberDirectoryFilter phoneNumberDirectoryFilterFromURLResponse:(NSHTTPURLResponse*)task.response body:responseObject];
@synchronized(self) {
phoneNumberDirectoryFilter = directory;
}
[Environment.preferences setSavedPhoneNumberDirectory:directory];
[self updateTextSecureWithRedPhoneSucces:YES];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogError(@"Error to fetch contact interesection: %@", error.debugDescription);
NSString* desc = [NSString stringWithFormat:NSLocalizedString(@"FAILED_RETRYING_DIRECTORY_DOWNLOAD", @""),
DIRECTORY_UPDATE_RETRY_PERIOD/HOUR];
Environment.errorNoter(desc, error, false);
BloomFilter* filter = [phoneNumberDirectoryFilter bloomFilter];
NSDate* retryDate = [NSDate dateWithTimeInterval:DIRECTORY_UPDATE_RETRY_PERIOD
sinceDate:[NSDate date]];
@synchronized(self) {
phoneNumberDirectoryFilter = [PhoneNumberDirectoryFilter phoneNumberDirectoryFilterWithBloomFilter:filter
andExpirationDate:retryDate];
}
[self updateTextSecureWithRedPhoneSucces:NO];
}];
}
- (void)updateTextSecureWithRedPhoneSucces:(BOOL)redPhoneSuccess {
NSArray *allContacts = [[[Environment getCurrent] contactsManager] allContacts];
NSMutableDictionary *contactsByPhoneNumber = [NSMutableDictionary dictionary];
NSMutableDictionary *phoneNumbersByHashes = [NSMutableDictionary dictionary];
for (Contact *contact in allContacts) {
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
[phoneNumbersByHashes setObject:phoneNumber.toE164 forKey:[Cryptography truncatedSHA1Base64EncodedWithoutPadding:phoneNumber.toE164]];
[contactsByPhoneNumber setObject:contact forKey:phoneNumber.toE164];
}
}
NSArray *hashes = [phoneNumbersByHashes allKeys];
TSRequest *request = [[TSContactsIntersectionRequest alloc]initWithHashesArray:hashes];
[[TSNetworkManager sharedManager] queueAuthenticatedRequest:request success:^(NSURLSessionDataTask *tsTask, id responseDict) {
NSMutableArray *tsIdentifiers = [NSMutableArray array];
NSMutableDictionary *relayForIdentifier = [NSMutableDictionary dictionary];
NSArray *contactsArray = [(NSDictionary*)responseDict objectForKey:@"contacts"];
if (contactsArray) {
for (NSDictionary *dict in contactsArray) {
NSString *hash = [dict objectForKey:@"token"];
if (hash) {
[tsIdentifiers addObject:[phoneNumbersByHashes objectForKey:hash]];
NSString *relay = [dict objectForKey:@"relay"];
if (relay) {
[relayForIdentifier setObject:relay forKey:[phoneNumbersByHashes objectForKey:hash]];
}
}
}
}
[[TSStorageManager sharedManager].dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (NSString *identifier in tsIdentifiers) {
TSRecipient *recipient = [TSRecipient recipientWithTextSecureIdentifier:identifier withTransaction:transaction];
if (!recipient) {
NSString *relay = [relayForIdentifier objectForKey:recipient];
recipient = [[TSRecipient alloc] initWithTextSecureIdentifier:identifier relay:relay];
}
[recipient saveWithTransaction:transaction];
}
}];
_isRefreshing = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_DIRECTORY_WAS_UPDATED object:nil];
[self scheduleUpdate];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
_isRefreshing = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_DIRECTORY_FAILED object:nil];
}];
}
@end