mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00

git-subtree-dir: submodules/MtProtoKit git-subtree-mainline: 3b155750f5a4894ff3dedf1860a37e94e0ea9571 git-subtree-split: 14ab734b977fd4f1686a2a13415f6a4c9b9fdd6d
1363 lines
54 KiB
Objective-C
1363 lines
54 KiB
Objective-C
#import "MTContext.h"
|
|
|
|
#import <inttypes.h>
|
|
|
|
#import "MTLogging.h"
|
|
#import "MTTimer.h"
|
|
#import "MTQueue.h"
|
|
#import "MTKeychain.h"
|
|
|
|
#import "MTDatacenterAddressSet.h"
|
|
#import "MTDatacenterAddress.h"
|
|
#import "MTDatacenterAuthInfo.h"
|
|
#import "MTDatacenterSaltInfo.h"
|
|
#import "MTSessionInfo.h"
|
|
#import "MTApiEnvironment.h"
|
|
|
|
#import "MTDiscoverDatacenterAddressAction.h"
|
|
#import "MTDatacenterAuthAction.h"
|
|
#import "MTDatacenterTransferAuthAction.h"
|
|
|
|
#import "MTTransportScheme.h"
|
|
#import "MTTcpTransport.h"
|
|
|
|
#import "MTApiEnvironment.h"
|
|
|
|
#import <libkern/OSAtomic.h>
|
|
|
|
#import "MTDiscoverConnectionSignals.h"
|
|
|
|
#import "MTTransportSchemeStats.h"
|
|
|
|
#if defined(MtProtoKitDynamicFramework)
|
|
# import <MTProtoKitDynamic/MTDisposable.h>
|
|
# import <MTProtoKitDynamic/MTSignal.h>
|
|
#elif defined(MtProtoKitMacFramework)
|
|
# import <MTProtoKitMac/MTDisposable.h>
|
|
# import <MTProtoKitMac/MTSignal.h>
|
|
#else
|
|
# import <MtProtoKit/MTDisposable.h>
|
|
# import <MtProtoKit/MTSignal.h>
|
|
#endif
|
|
|
|
@implementation MTContextBlockChangeListener
|
|
|
|
- (void)contextIsPasswordRequiredUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId
|
|
{
|
|
if (_contextIsPasswordRequiredUpdated)
|
|
_contextIsPasswordRequiredUpdated(context, datacenterId);
|
|
}
|
|
|
|
- (MTSignal *)fetchContextDatacenterPublicKeys:(MTContext *)context datacenterId:(NSInteger)datacenterId {
|
|
if (_fetchContextDatacenterPublicKeys) {
|
|
return _fetchContextDatacenterPublicKeys(context, datacenterId);
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (MTSignal *)isContextNetworkAccessAllowed:(MTContext *)context {
|
|
if (_isContextNetworkAccessAllowed) {
|
|
return _isContextNetworkAccessAllowed(context);
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@interface MTTransportSchemeKey : NSObject<NSCoding, NSCopying>
|
|
|
|
@property (nonatomic, readonly) NSInteger datacenterId;
|
|
@property (nonatomic, readonly) bool isProxy;
|
|
@property (nonatomic, readonly) bool isMedia;
|
|
|
|
@end
|
|
|
|
@implementation MTTransportSchemeKey
|
|
|
|
- (instancetype)initWithDatacenterId:(NSInteger)datacenterId isProxy:(bool)isProxy isMedia:(bool)isMedia {
|
|
self = [super init];
|
|
if (self != nil) {
|
|
_datacenterId = datacenterId;
|
|
_isProxy = isProxy;
|
|
_isMedia = isMedia;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
|
return [self initWithDatacenterId:[aDecoder decodeIntegerForKey:@"datacenterId"] isProxy:[aDecoder decodeBoolForKey:@"isProxy"] isMedia:[aDecoder decodeBoolForKey:@"isMedia"]];
|
|
}
|
|
|
|
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
|
[aCoder encodeInteger:_datacenterId forKey:@"datacenterId"];
|
|
[aCoder encodeBool:_isProxy forKey:@"isProxy"];
|
|
[aCoder encodeBool:_isMedia forKey:@"isMedia"];
|
|
}
|
|
|
|
- (instancetype)copyWithZone:(NSZone *)__unused zone {
|
|
return self;
|
|
}
|
|
|
|
- (NSUInteger)hash {
|
|
return _datacenterId * 31 * 31 + (_isProxy ? 1 : 0) * 31 + (_isMedia ? 1 : 0);
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)object {
|
|
if (![object isKindOfClass:[MTTransportSchemeKey class]]) {
|
|
return false;
|
|
}
|
|
MTTransportSchemeKey *other = (MTTransportSchemeKey *)object;
|
|
if (_datacenterId != other->_datacenterId) {
|
|
return false;
|
|
}
|
|
if (_isProxy != other->_isProxy) {
|
|
return false;
|
|
}
|
|
if (_isMedia != other->_isMedia) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface MTContext () <MTDiscoverDatacenterAddressActionDelegate, MTDatacenterAuthActionDelegate, MTDatacenterTransferAuthActionDelegate>
|
|
{
|
|
int64_t _uniqueId;
|
|
|
|
NSTimeInterval _globalTimeDifference;
|
|
|
|
NSMutableDictionary *_datacenterSeedAddressSetById;
|
|
NSMutableDictionary *_datacenterAddressSetById;
|
|
NSMutableDictionary<MTTransportSchemeKey *, MTTransportScheme *> *_datacenterManuallySelectedSchemeById;
|
|
|
|
NSMutableDictionary<NSNumber *, NSMutableDictionary<MTDatacenterAddress *, MTTransportSchemeStats *> *> *_transportSchemeStats;
|
|
MTTimer *_schemeStatsSyncTimer;
|
|
|
|
NSMutableDictionary *_datacenterAuthInfoById;
|
|
|
|
NSMutableDictionary *_datacenterPublicKeysById;
|
|
|
|
NSMutableDictionary *_authTokenById;
|
|
|
|
NSMutableArray *_changeListeners;
|
|
|
|
MTSignal *_discoverBackupAddressListSignal;
|
|
|
|
NSMutableDictionary *_discoverDatacenterAddressActions;
|
|
NSMutableDictionary *_datacenterAuthActions;
|
|
NSMutableDictionary *_datacenterTempAuthActions;
|
|
NSMutableDictionary *_datacenterTransferAuthActions;
|
|
|
|
NSMutableDictionary *_cleanupSessionIdsByAuthKeyId;
|
|
NSMutableArray *_currentSessionInfos;
|
|
|
|
NSMutableDictionary *_periodicTasksTimerByDatacenterId;
|
|
|
|
volatile OSSpinLock _passwordEntryRequiredLock;
|
|
NSMutableDictionary *_passwordRequiredByDatacenterId;
|
|
|
|
NSMutableDictionary *_transportSchemeDisposableByDatacenterId;
|
|
id<MTDisposable> _backupAddressListDisposable;
|
|
|
|
NSMutableDictionary<NSNumber *, id<MTDisposable> > *_fetchPublicKeysActions;
|
|
|
|
MTDisposableSet *_cleanupSessionInfoDisposables;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation MTContext
|
|
|
|
- (instancetype)init
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
NSAssert(false, @"use initWithSerialization:apiEnvironment:");
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithSerialization:(id<MTSerialization>)serialization apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys
|
|
{
|
|
#ifdef DEBUG
|
|
NSAssert(serialization != nil, @"serialization should not be nil");
|
|
NSAssert(apiEnvironment != nil, @"apiEnvironment should not be nil");
|
|
#endif
|
|
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
arc4random_buf(&_uniqueId, sizeof(_uniqueId));
|
|
|
|
_serialization = serialization;
|
|
_apiEnvironment = apiEnvironment;
|
|
_isTestingEnvironment = isTestingEnvironment;
|
|
_useTempAuthKeys = useTempAuthKeys;
|
|
|
|
_datacenterSeedAddressSetById = [[NSMutableDictionary alloc] init];
|
|
|
|
_datacenterAddressSetById = [[NSMutableDictionary alloc] init];
|
|
_datacenterManuallySelectedSchemeById = [[NSMutableDictionary alloc] init];
|
|
|
|
_transportSchemeStats = [[NSMutableDictionary alloc] init];
|
|
|
|
_datacenterAuthInfoById = [[NSMutableDictionary alloc] init];
|
|
_datacenterPublicKeysById = [[NSMutableDictionary alloc] init];
|
|
|
|
_authTokenById = [[NSMutableDictionary alloc] init];
|
|
|
|
_changeListeners = [[NSMutableArray alloc] init];
|
|
|
|
_discoverDatacenterAddressActions = [[NSMutableDictionary alloc] init];
|
|
_datacenterAuthActions = [[NSMutableDictionary alloc] init];
|
|
_datacenterTempAuthActions = [[NSMutableDictionary alloc] init];
|
|
_datacenterTransferAuthActions = [[NSMutableDictionary alloc] init];
|
|
|
|
_cleanupSessionIdsByAuthKeyId = [[NSMutableDictionary alloc] init];
|
|
_currentSessionInfos = [[NSMutableArray alloc] init];
|
|
|
|
_passwordRequiredByDatacenterId = [[NSMutableDictionary alloc] init];
|
|
|
|
_fetchPublicKeysActions = [[NSMutableDictionary alloc] init];
|
|
|
|
_cleanupSessionInfoDisposables = [[MTDisposableSet alloc] init];
|
|
|
|
[self updatePeriodicTasks];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self cleanup];
|
|
}
|
|
|
|
+ (MTQueue *)contextQueue
|
|
{
|
|
static MTQueue *queue = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
queue = [[MTQueue alloc] initWithName:"com.mtproto.MTContextQueue"];
|
|
});
|
|
return queue;
|
|
}
|
|
|
|
- (void)cleanup
|
|
{
|
|
NSDictionary *datacenterAuthActions = _datacenterAuthActions;
|
|
_datacenterAuthActions = nil;
|
|
|
|
NSDictionary *datacenterTempAuthActions = _datacenterTempAuthActions;
|
|
_datacenterTempAuthActions = nil;
|
|
|
|
NSDictionary *discoverDatacenterAddressActions = _discoverDatacenterAddressActions;
|
|
_discoverDatacenterAddressActions = nil;
|
|
|
|
NSDictionary *datacenterTransferAuthActions = _datacenterTransferAuthActions;
|
|
_datacenterTransferAuthActions = nil;
|
|
|
|
NSDictionary *fetchPublicKeysActions = _fetchPublicKeysActions;
|
|
_fetchPublicKeysActions = nil;
|
|
|
|
id<MTDisposable> cleanupSessionInfoDisposables = _cleanupSessionInfoDisposables;
|
|
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
for (NSNumber *nDatacenterId in discoverDatacenterAddressActions)
|
|
{
|
|
MTDiscoverDatacenterAddressAction *action = discoverDatacenterAddressActions[nDatacenterId];
|
|
action.delegate = nil;
|
|
[action cancel];
|
|
}
|
|
|
|
for (NSNumber *nDatacenterId in datacenterAuthActions)
|
|
{
|
|
MTDatacenterAuthAction *action = datacenterAuthActions[nDatacenterId];
|
|
action.delegate = nil;
|
|
[action cancel];
|
|
}
|
|
|
|
for (NSNumber *nDatacenterId in datacenterTempAuthActions)
|
|
{
|
|
MTDatacenterAuthAction *action = datacenterTempAuthActions[nDatacenterId];
|
|
action.delegate = nil;
|
|
[action cancel];
|
|
}
|
|
|
|
for (NSNumber *nDatacenterId in datacenterTransferAuthActions)
|
|
{
|
|
MTDatacenterTransferAuthAction *action = datacenterTransferAuthActions[nDatacenterId];
|
|
action.delegate = nil;
|
|
[action cancel];
|
|
}
|
|
|
|
for (NSNumber *nDatacenterId in fetchPublicKeysActions)
|
|
{
|
|
id<MTDisposable> disposable = fetchPublicKeysActions[nDatacenterId];
|
|
[disposable dispose];
|
|
}
|
|
|
|
[cleanupSessionInfoDisposables dispose];
|
|
}];
|
|
}
|
|
|
|
- (void)performBatchUpdates:(void (^)())block
|
|
{
|
|
if (block != nil)
|
|
[[MTContext contextQueue] dispatchOnQueue:block];
|
|
}
|
|
|
|
- (void)setKeychain:(id<MTKeychain>)keychain
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
_keychain = keychain;
|
|
|
|
if (_keychain != nil)
|
|
{
|
|
NSNumber *nGlobalTimeDifference = [keychain objectForKey:@"globalTimeDifference" group:@"temp"];
|
|
if (nGlobalTimeDifference != nil)
|
|
_globalTimeDifference = [nGlobalTimeDifference doubleValue];
|
|
|
|
NSDictionary *datacenterAddressSetById = [keychain objectForKey:@"datacenterAddressSetById" group:@"persistent"];
|
|
if (datacenterAddressSetById != nil) {
|
|
_datacenterAddressSetById = [[NSMutableDictionary alloc] initWithDictionary:datacenterAddressSetById];
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext loaded datacenterAddressSetById: %@]", _datacenterAddressSetById);
|
|
}
|
|
}
|
|
|
|
NSDictionary *datacenterManuallySelectedSchemeById = [keychain objectForKey:@"datacenterManuallySelectedSchemeById_v1" group:@"persistent"];
|
|
if (datacenterManuallySelectedSchemeById != nil) {
|
|
_datacenterManuallySelectedSchemeById = [[NSMutableDictionary alloc] initWithDictionary:datacenterManuallySelectedSchemeById];
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext loaded datacenterManuallySelectedSchemeById: %@]", _datacenterManuallySelectedSchemeById);
|
|
}
|
|
}
|
|
|
|
|
|
[_apiEnvironment.datacenterAddressOverrides enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAddress *address, __unused BOOL *stop) {
|
|
_datacenterAddressSetById[nDatacenterId] = [[MTDatacenterAddressSet alloc] initWithAddressList:@[address]];
|
|
}];
|
|
|
|
NSDictionary *datacenterAuthInfoById = [keychain objectForKey:@"datacenterAuthInfoById" group:@"persistent"];
|
|
if (datacenterAuthInfoById != nil)
|
|
_datacenterAuthInfoById = [[NSMutableDictionary alloc] initWithDictionary:datacenterAuthInfoById];
|
|
|
|
NSDictionary *datacenterPublicKeysById = [keychain objectForKey:@"datacenterPublicKeysById" group:@"ephemeral"];
|
|
if (datacenterPublicKeysById != nil) {
|
|
_datacenterPublicKeysById = [[NSMutableDictionary alloc] initWithDictionary:datacenterPublicKeysById];
|
|
}
|
|
|
|
NSDictionary *transportSchemeStats = [keychain objectForKey:@"transportSchemeStats_v1" group:@"temp"];
|
|
if (transportSchemeStats != nil) {
|
|
[_transportSchemeStats removeAllObjects];
|
|
[transportSchemeStats enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, NSDictionary<MTDatacenterAddress *, MTTransportSchemeStats *> *values, __unused BOOL *stop) {
|
|
_transportSchemeStats[nDatacenterId] = [[NSMutableDictionary alloc] initWithDictionary:values];
|
|
}];
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext] loaded transportSchemeStats:\n%@", [MTTransportSchemeStats formatStats:_transportSchemeStats]);
|
|
}
|
|
}
|
|
|
|
NSDictionary *authTokenById = [keychain objectForKey:@"authTokenById" group:@"persistent"];
|
|
if (authTokenById != nil)
|
|
_authTokenById = [[NSMutableDictionary alloc] initWithDictionary:authTokenById];
|
|
|
|
NSDictionary *cleanupSessionIdsByAuthKeyId = [keychain objectForKey:@"cleanupSessionIdsByAuthKeyId" group:@"cleanup"];
|
|
if (cleanupSessionIdsByAuthKeyId != nil)
|
|
_cleanupSessionIdsByAuthKeyId = [[NSMutableDictionary alloc] initWithDictionary:cleanupSessionIdsByAuthKeyId];
|
|
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: received keychain globalTimeDifference:%f datacenterAuthInfoById:%@]", (int)self, _globalTimeDifference, _datacenterAuthInfoById);
|
|
}
|
|
} else {
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: received keychain nil]", (int)self);
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)addChangeListener:(id<MTContextChangeListener>)changeListener
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (![_changeListeners containsObject:changeListener])
|
|
{
|
|
[_changeListeners addObject:changeListener];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)removeChangeListener:(id<MTContextChangeListener>)changeListener
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
[_changeListeners removeObject:changeListener];
|
|
} synchronous:true];
|
|
}
|
|
|
|
- (void)setDiscoverBackupAddressListSignal:(MTSignal *)signal {
|
|
[[MTContext contextQueue] dispatchOnQueue:^ {
|
|
_discoverBackupAddressListSignal = signal;
|
|
} synchronous:true];
|
|
}
|
|
|
|
- (NSTimeInterval)globalTime
|
|
{
|
|
return [[NSDate date] timeIntervalSince1970] + [self globalTimeDifference];
|
|
}
|
|
|
|
- (NSTimeInterval)globalTimeDifference
|
|
{
|
|
__block NSTimeInterval result = 0.0;
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
result = _globalTimeDifference;
|
|
} synchronous:true];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (NSTimeInterval)globalTimeOffsetFromUTC
|
|
{
|
|
return [self globalTimeDifference] + [[NSTimeZone localTimeZone] secondsFromGMT];
|
|
}
|
|
|
|
- (void)setGlobalTimeDifference:(NSTimeInterval)globalTimeDifference
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
_globalTimeDifference = globalTimeDifference;
|
|
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: global time difference changed: %.1fs]", (int)self, globalTimeDifference);
|
|
}
|
|
|
|
[_keychain setObject:@(_globalTimeDifference) forKey:@"globalTimeDifference" group:@"temp"];
|
|
}];
|
|
}
|
|
|
|
- (void)setSeedAddressSetForDatacenterWithId:(NSInteger)datacenterId seedAddressSet:(MTDatacenterAddressSet *)seedAddressSet
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
_datacenterSeedAddressSetById[@(datacenterId)] = seedAddressSet;
|
|
}];
|
|
}
|
|
|
|
- (void)updateAddressSetForDatacenterWithId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet forceUpdateSchemes:(bool)forceUpdateSchemes
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (addressSet != nil && datacenterId != 0)
|
|
{
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: address set updated for %d]", (int)self, datacenterId);
|
|
}
|
|
|
|
bool updateSchemes = forceUpdateSchemes;
|
|
|
|
bool previousAddressSetWasEmpty = ((MTDatacenterAddressSet *)_datacenterAddressSetById[@(datacenterId)]).addressList.count == 0;
|
|
|
|
_datacenterAddressSetById[@(datacenterId)] = addressSet;
|
|
[_keychain setObject:_datacenterAddressSetById forKey:@"datacenterAddressSetById" group:@"persistent"];
|
|
|
|
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
|
|
|
for (id<MTContextChangeListener> listener in currentListeners)
|
|
{
|
|
if ([listener respondsToSelector:@selector(contextDatacenterAddressSetUpdated:datacenterId:addressSet:)])
|
|
[listener contextDatacenterAddressSetUpdated:self datacenterId:datacenterId addressSet:addressSet];
|
|
}
|
|
|
|
if (previousAddressSetWasEmpty || updateSchemes || true) {
|
|
for (id<MTContextChangeListener> listener in currentListeners) {
|
|
if ([listener respondsToSelector:@selector(contextDatacenterTransportSchemesUpdated:datacenterId:)]) {
|
|
[listener contextDatacenterTransportSchemesUpdated:self datacenterId:datacenterId];
|
|
}
|
|
}
|
|
} else {
|
|
/*for (NSNumber *nMedia in @[@false, @true]) {
|
|
for (NSNumber *nIsProxy in @[@false, @true]) {
|
|
MTDatacenterAddress *address = [self transportSchemeForDatacenterWithId:datacenterId media:[nMedia boolValue] isProxy:[nIsProxy boolValue]].address;
|
|
bool matches = false;
|
|
if (address != nil) {
|
|
for (MTDatacenterAddress *listAddress in addressSet.addressList) {
|
|
if ([listAddress.ip isEqualToString:address.ip]) {
|
|
if (listAddress.secret != nil && address.secret != nil && [listAddress.secret isEqualToData:address.secret]) {
|
|
matches = true;
|
|
} else if (listAddress.secret == nil && address.secret == nil) {
|
|
matches = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!matches) {
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: updated address set for %d doesn't contain current %@, updating]", (int)self, datacenterId, address);
|
|
}
|
|
|
|
[self updateTransportSchemeForDatacenterWithId:datacenterId transportScheme:[self defaultTransportSchemeForDatacenterWithId:datacenterId media:[nMedia boolValue] isProxy:[nIsProxy boolValue]] media:[nMedia boolValue] isProxy:[nIsProxy boolValue]];
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
if (updateSchemes) {
|
|
id<MTDisposable> disposable = _transportSchemeDisposableByDatacenterId[@(datacenterId)];
|
|
if (disposable != nil) {
|
|
[disposable dispose];
|
|
[_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
|
|
|
|
[self transportSchemeForDatacenterWithIdRequired:datacenterId moreOptimalThan:nil beginWithHttp:false media:false isProxy:_apiEnvironment.socksProxySettings != nil];
|
|
}
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)addAddressForDatacenterWithId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address
|
|
{
|
|
if (address == nil || datacenterId == 0)
|
|
return;
|
|
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
bool updated = false;
|
|
|
|
MTDatacenterAddressSet *addressSet = [self addressSetForDatacenterWithId:datacenterId];
|
|
if (addressSet == nil)
|
|
{
|
|
addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:@[address]];
|
|
updated = true;
|
|
}
|
|
else if (![addressSet.addressList containsObject:address])
|
|
{
|
|
NSMutableArray *updatedAddressList = [[NSMutableArray alloc] init];
|
|
[updatedAddressList addObject:address];
|
|
[updatedAddressList addObjectsFromArray:addressSet.addressList];
|
|
|
|
addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:updatedAddressList];
|
|
updated = true;
|
|
}
|
|
|
|
if (updated)
|
|
{
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: added address %@ for datacenter %d]", (int)self, address, datacenterId);
|
|
}
|
|
|
|
_datacenterAddressSetById[@(datacenterId)] = addressSet;
|
|
[_keychain setObject:_datacenterAddressSetById forKey:@"datacenterAddressSetById" group:@"persistent"];
|
|
|
|
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
|
|
|
for (id<MTContextChangeListener> listener in currentListeners)
|
|
{
|
|
if ([listener respondsToSelector:@selector(contextDatacenterAddressSetUpdated:datacenterId:addressSet:)])
|
|
[listener contextDatacenterAddressSetUpdated:self datacenterId:datacenterId addressSet:addressSet];
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (authInfo != nil && datacenterId != 0)
|
|
{
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: auth info updated for %d]", (int)self, datacenterId);
|
|
}
|
|
|
|
_datacenterAuthInfoById[@(datacenterId)] = authInfo;
|
|
[_keychain setObject:_datacenterAuthInfoById forKey:@"datacenterAuthInfoById" group:@"persistent"];
|
|
|
|
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
|
|
|
for (id<MTContextChangeListener> listener in currentListeners)
|
|
{
|
|
if ([listener respondsToSelector:@selector(contextDatacenterAuthInfoUpdated:datacenterId:authInfo:)])
|
|
[listener contextDatacenterAuthInfoUpdated:self datacenterId:datacenterId authInfo:authInfo];
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (bool)isPasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId
|
|
{
|
|
OSSpinLockLock(&_passwordEntryRequiredLock);
|
|
bool currentValue = [_passwordRequiredByDatacenterId[@(datacenterId)] boolValue];
|
|
OSSpinLockUnlock(&_passwordEntryRequiredLock);
|
|
|
|
return currentValue;
|
|
}
|
|
|
|
- (bool)updatePasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId required:(bool)required
|
|
{
|
|
OSSpinLockLock(&_passwordEntryRequiredLock);
|
|
bool currentValue = [_passwordRequiredByDatacenterId[@(datacenterId)] boolValue];
|
|
bool updated = currentValue != required;
|
|
_passwordRequiredByDatacenterId[@(datacenterId)] = @(required);
|
|
OSSpinLockUnlock(&_passwordEntryRequiredLock);
|
|
|
|
if (updated)
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
|
|
|
for (id<MTContextChangeListener> listener in currentListeners)
|
|
{
|
|
if ([listener respondsToSelector:@selector(contextIsPasswordRequiredUpdated:datacenterId:)])
|
|
[listener contextIsPasswordRequiredUpdated:self datacenterId:datacenterId];
|
|
}
|
|
}];
|
|
}
|
|
|
|
return currentValue;
|
|
}
|
|
|
|
- (void)updateTransportSchemeForDatacenterWithId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media isProxy:(bool)isProxy {
|
|
[[MTContext contextQueue] dispatchOnQueue:^ {
|
|
if (transportScheme != nil && datacenterId != 0) {
|
|
_datacenterManuallySelectedSchemeById[[[MTTransportSchemeKey alloc] initWithDatacenterId:datacenterId isProxy:isProxy isMedia:media]] = transportScheme;
|
|
[_keychain setObject:_datacenterManuallySelectedSchemeById forKey:@"datacenterManuallySelectedSchemeById_v1" group:@"persistent"];
|
|
|
|
[self reportTransportSchemeSuccessForDatacenterId:datacenterId transportScheme:transportScheme];
|
|
[self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:transportScheme process:^MTTransportSchemeStats *(MTTransportSchemeStats *current) {
|
|
current = [current withUpdatedLastFailureTimestamp:0];
|
|
current = [current withUpdatedLastResponseTimestamp:(int32_t)CFAbsoluteTimeGetCurrent()];
|
|
return current;
|
|
}];
|
|
|
|
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
|
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"[MTContext#%x: %@ transport scheme updated for %d: %@]", (int)self, media ? @"media" : @"generic", datacenterId, transportScheme);
|
|
}
|
|
|
|
for (id<MTContextChangeListener> listener in currentListeners) {
|
|
if ([listener respondsToSelector:@selector(contextDatacenterTransportSchemesUpdated:datacenterId:)])
|
|
[listener contextDatacenterTransportSchemesUpdated:self datacenterId:datacenterId];
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo *)sessionInfo
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
|
|
#warning implement and reenable
|
|
return;
|
|
|
|
if (authKeyId == 0 || sessionInfo == nil)
|
|
return;
|
|
|
|
NSMutableArray *sessionIds = _cleanupSessionIdsByAuthKeyId[@(authKeyId)];
|
|
if (sessionIds == nil)
|
|
{
|
|
sessionIds = [[NSMutableArray alloc] init];
|
|
_cleanupSessionIdsByAuthKeyId[@(authKeyId)] = sessionIds;
|
|
}
|
|
else if (![sessionIds respondsToSelector:@selector(setObject:forKey:)])
|
|
{
|
|
sessionIds = [[NSMutableArray alloc] initWithArray:sessionIds];
|
|
_cleanupSessionIdsByAuthKeyId[@(authKeyId)] = sessionIds;
|
|
}
|
|
|
|
[sessionIds addObject:@(sessionInfo.sessionId)];
|
|
[_currentSessionInfos addObject:sessionInfo];
|
|
|
|
[_keychain setObject:_cleanupSessionIdsByAuthKeyId forKey:@"cleanupSessionIdsByAuthKeyId" group:@"cleanup"];
|
|
}];
|
|
}
|
|
|
|
- (void)collectSessionIdsForCleanupWithAuthKeyId:(int64_t)authKeyId completion:(void (^)(NSArray *sessionIds))completion
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
NSMutableSet *liveSessionIds = [[NSMutableSet alloc] init];
|
|
for (NSInteger i = (NSInteger)_currentSessionInfos.count - 1; i >= 0; i--)
|
|
{
|
|
MTSessionInfo *sessionInfo = _currentSessionInfos[i];
|
|
if (!sessionInfo.canBeDeleted)
|
|
[liveSessionIds addObject:@(sessionInfo.sessionId)];
|
|
else
|
|
[_currentSessionInfos removeObjectAtIndex:(NSUInteger)i];
|
|
}
|
|
|
|
NSMutableArray *currentSessionIds = [[NSMutableArray alloc] init];
|
|
for (NSNumber *nSessionId in _cleanupSessionIdsByAuthKeyId[@(authKeyId)])
|
|
{
|
|
if (![liveSessionIds containsObject:nSessionId])
|
|
[currentSessionIds addObject:nSessionId];
|
|
}
|
|
|
|
if (completion)
|
|
completion(currentSessionIds);
|
|
}];
|
|
}
|
|
|
|
- (void)sessionIdsDeletedForAuthKeyId:(int64_t)authKeyId sessionIds:(NSArray *)sessionIds
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
for (NSInteger i = (NSInteger)_currentSessionInfos.count - 1; i >= 0; i--)
|
|
{
|
|
MTSessionInfo *sessionInfo = _currentSessionInfos[i];
|
|
if ([sessionIds containsObject:@(sessionInfo.sessionId)])
|
|
[_currentSessionInfos removeObjectAtIndex:(NSUInteger)i];
|
|
}
|
|
|
|
NSMutableArray *cleanupSessionIds = _cleanupSessionIdsByAuthKeyId[@(authKeyId)];
|
|
if (![cleanupSessionIds respondsToSelector:@selector(removeObjectAtIndex:)])
|
|
{
|
|
cleanupSessionIds = [[NSMutableArray alloc] initWithArray:cleanupSessionIds];
|
|
_cleanupSessionIdsByAuthKeyId[@(authKeyId)] = cleanupSessionIds;
|
|
}
|
|
for (NSInteger i = ((NSUInteger)cleanupSessionIds.count) - 1; i >= 0; i--)
|
|
{
|
|
if ([sessionIds containsObject:cleanupSessionIds[(NSUInteger)i]])
|
|
[cleanupSessionIds removeObjectAtIndex:(NSUInteger)i];
|
|
}
|
|
|
|
[_keychain setObject:_cleanupSessionIdsByAuthKeyId forKey:@"cleanupSessionIdsByAuthKeyId" group:@"cleanup"];
|
|
}];
|
|
}
|
|
|
|
- (NSArray *)knownDatacenterIds
|
|
{
|
|
NSMutableSet *datacenterIds = [[NSMutableSet alloc] init];
|
|
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
for (NSNumber *nDatacenterId in _datacenterSeedAddressSetById.allKeys)
|
|
{
|
|
[datacenterIds addObject:nDatacenterId];
|
|
}
|
|
|
|
for (NSNumber *nDatacenterId in _datacenterAddressSetById.allKeys)
|
|
{
|
|
[datacenterIds addObject:nDatacenterId];
|
|
}
|
|
} synchronous:true];
|
|
|
|
return [[datacenterIds allObjects] sortedArrayUsingComparator:^NSComparisonResult(NSNumber *n1, NSNumber *n2)
|
|
{
|
|
return [n1 compare:n2];
|
|
}];
|
|
}
|
|
|
|
- (void)enumerateAddressSetsForDatacenters:(void (^)(NSInteger datacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop))block
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (block == nil)
|
|
return;
|
|
|
|
NSMutableSet *processedDatacenterIds = [[NSMutableSet alloc] init];
|
|
|
|
[_datacenterAddressSetById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop)
|
|
{
|
|
[processedDatacenterIds addObject:nDatacenterId];
|
|
block([nDatacenterId integerValue], addressSet, stop);
|
|
}];
|
|
|
|
[_datacenterSeedAddressSetById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop)
|
|
{
|
|
if (![processedDatacenterIds containsObject:nDatacenterId])
|
|
block([nDatacenterId integerValue], addressSet, stop);
|
|
}];
|
|
} synchronous:true];
|
|
}
|
|
|
|
- (MTDatacenterAddressSet *)addressSetForDatacenterWithId:(NSInteger)datacenterId
|
|
{
|
|
__block MTDatacenterAddressSet *result = nil;
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
MTDatacenterAddressSet *addressSet = _datacenterAddressSetById[@(datacenterId)];
|
|
if (addressSet != nil && addressSet.addressList.count != 0) {
|
|
result = _datacenterAddressSetById[@(datacenterId)];
|
|
} else {
|
|
result = _datacenterSeedAddressSetById[@(datacenterId)];
|
|
}
|
|
} synchronous:true];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (MTTransportScheme * _Nullable)chooseTransportSchemeForConnectionToDatacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes {
|
|
__block MTTransportScheme *result = nil;
|
|
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
__block MTTransportScheme *schemeWithEarliestFailure = nil;
|
|
__block int32_t earliestFailure = INT32_MAX;
|
|
|
|
int32_t timestamp = (int32_t)CFAbsoluteTimeGetCurrent();
|
|
__block bool scanIpv6 = false;
|
|
for (MTTransportScheme *scheme in schemes) {
|
|
if (scheme.address.isIpv6) {
|
|
[self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:scheme process:^MTTransportSchemeStats *(MTTransportSchemeStats *current) {
|
|
if (scheme.address.isIpv6 && current.lastResponseTimestamp > timestamp - 60 * 60) {
|
|
scanIpv6 = true;
|
|
}
|
|
return current;
|
|
}];
|
|
}
|
|
}
|
|
|
|
for (MTTransportScheme *scheme in schemes.reverseObjectEnumerator) {
|
|
if (scheme.address.isIpv6 && !scanIpv6) {
|
|
continue;
|
|
}
|
|
[self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:scheme process:^MTTransportSchemeStats *(MTTransportSchemeStats *current) {
|
|
if (schemeWithEarliestFailure == nil) {
|
|
schemeWithEarliestFailure = scheme;
|
|
earliestFailure = current.lastFailureTimestamp;
|
|
} else if (earliestFailure > current.lastFailureTimestamp) {
|
|
earliestFailure = current.lastFailureTimestamp;
|
|
schemeWithEarliestFailure = scheme;
|
|
}
|
|
return current;
|
|
}];
|
|
}
|
|
result = schemeWithEarliestFailure;
|
|
} synchronous:true];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (NSArray<MTTransportScheme *> * _Nonnull)transportSchemesForDatacenterWithId:(NSInteger)datacenterId media:(bool)media enforceMedia:(bool)enforceMedia isProxy:(bool)isProxy
|
|
{
|
|
__block NSMutableArray <MTTransportScheme *> *results = [[NSMutableArray alloc] init];
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
MTDatacenterAddress *overrideAddress = _apiEnvironment.datacenterAddressOverrides[@(datacenterId)];
|
|
if (overrideAddress != nil) {
|
|
[results addObject:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:overrideAddress media:false]];
|
|
} else {
|
|
[results addObjectsFromArray:[self _allTransportSchemesForDatacenterWithId:datacenterId]];
|
|
}
|
|
MTTransportScheme *manualScheme = _datacenterManuallySelectedSchemeById[[[MTTransportSchemeKey alloc] initWithDatacenterId:datacenterId isProxy:isProxy isMedia:media]];
|
|
if (manualScheme != nil && ![results containsObject:manualScheme]) {
|
|
[results addObject:manualScheme];
|
|
}
|
|
} synchronous:true];
|
|
|
|
for (int i = (int)(results.count - 1); i >= 0; i--) {
|
|
if (enforceMedia && !results[i].address.preferForMedia) {
|
|
[results removeObjectAtIndex:i];
|
|
} else if (!media && results[i].address.preferForMedia) {
|
|
[results removeObjectAtIndex:i];
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId {
|
|
__block MTDatacenterAuthInfo *result = nil;
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
result = _datacenterAuthInfoById[@(datacenterId)];
|
|
} synchronous:true];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (NSArray<NSDictionary *> *)publicKeysForDatacenterWithId:(NSInteger)datacenterId {
|
|
__block NSArray<NSDictionary *> *result = nil;
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
result = _datacenterPublicKeysById[@(datacenterId)];
|
|
} synchronous:true];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (void)updatePublicKeysForDatacenterWithId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
if (publicKeys != nil) {
|
|
_datacenterPublicKeysById[@(datacenterId)] = publicKeys;
|
|
[_keychain setObject:_datacenterPublicKeysById forKey:@"datacenterPublicKeysById" group:@"ephemeral"];
|
|
|
|
for (id<MTContextChangeListener> listener in _changeListeners) {
|
|
if ([listener respondsToSelector:@selector(contextDatacenterPublicKeysUpdated:datacenterId:publicKeys:)]) {
|
|
[listener contextDatacenterPublicKeysUpdated:self datacenterId:datacenterId publicKeys:publicKeys];
|
|
}
|
|
}
|
|
}
|
|
} synchronous:false];
|
|
}
|
|
|
|
- (void)publicKeysForDatacenterWithIdRequired:(NSInteger)datacenterId {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
if (_fetchPublicKeysActions[@(datacenterId)] == nil) {
|
|
for (id<MTContextChangeListener> listener in _changeListeners) {
|
|
if ([listener respondsToSelector:@selector(fetchContextDatacenterPublicKeys:datacenterId:)]) {
|
|
MTSignal *signal = [listener fetchContextDatacenterPublicKeys:self datacenterId:datacenterId];
|
|
if (signal != nil) {
|
|
__weak MTContext *weakSelf = self;
|
|
MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
|
|
_fetchPublicKeysActions[@(datacenterId)] = disposable;
|
|
[disposable setDisposable:[signal startWithNext:^(NSArray<NSDictionary *> *next) {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
__strong MTContext *strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
[strongSelf->_fetchPublicKeysActions removeObjectForKey:@(datacenterId)];
|
|
[strongSelf updatePublicKeysForDatacenterWithId:datacenterId publicKeys:next];
|
|
}
|
|
} synchronous:false];
|
|
}]];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} synchronous:false];
|
|
}
|
|
|
|
- (void)removeAllAuthTokens
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
[_authTokenById removeAllObjects];
|
|
[_keychain setObject:_authTokenById forKey:@"authTokenById" group:@"persistent"];
|
|
|
|
for (NSNumber *nDatacenterId in _datacenterTransferAuthActions)
|
|
{
|
|
MTDatacenterTransferAuthAction *action = _datacenterTransferAuthActions[nDatacenterId];
|
|
action.delegate = nil;
|
|
[action cancel];
|
|
}
|
|
[_datacenterTransferAuthActions removeAllObjects];
|
|
}];
|
|
}
|
|
|
|
- (id)authTokenForDatacenterWithId:(NSInteger)datacenterId
|
|
{
|
|
__block id result = nil;
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
result = _authTokenById[@(datacenterId)];
|
|
} synchronous:true];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (NSArray<MTTransportScheme *> * _Nonnull)_allTransportSchemesForDatacenterWithId:(NSInteger)datacenterId {
|
|
NSMutableArray<MTTransportScheme *> *result = [[NSMutableArray alloc] init];
|
|
MTDatacenterAddressSet *addressSet = [self addressSetForDatacenterWithId:datacenterId];
|
|
if (addressSet == nil) {
|
|
[self addressSetForDatacenterWithIdRequired:datacenterId];
|
|
} else {
|
|
for (MTDatacenterAddress *address in addressSet.addressList) {
|
|
[result addObject:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:address.preferForMedia]];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
- (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId media:(bool)media
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
[self transportSchemeForDatacenterWithIdRequired:datacenterId moreOptimalThan:nil beginWithHttp:false media:media isProxy:_apiEnvironment.socksProxySettings != nil];
|
|
}];
|
|
}
|
|
|
|
- (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId moreOptimalThan:(MTTransportScheme *)suboptimalScheme beginWithHttp:(bool)beginWithHttp media:(bool)media isProxy:(bool)isProxy
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (_transportSchemeDisposableByDatacenterId == nil)
|
|
_transportSchemeDisposableByDatacenterId = [[NSMutableDictionary alloc] init];
|
|
id<MTDisposable> disposable = _transportSchemeDisposableByDatacenterId[@(datacenterId)];
|
|
if (disposable == nil)
|
|
{
|
|
__weak MTContext *weakSelf = self;
|
|
MTDatacenterAddressSet *initialAddressSet = [self addressSetForDatacenterWithId:datacenterId];
|
|
NSMutableArray *addressList = [[NSMutableArray alloc] initWithArray:initialAddressSet.addressList];
|
|
MTDatacenterAddressSet *seedAddress = _datacenterSeedAddressSetById[@(datacenterId)];
|
|
if (seedAddress != nil) {
|
|
for (MTDatacenterAddress *address in seedAddress.addressList) {
|
|
if (![addressList containsObject:address]) {
|
|
[addressList addObject:address];
|
|
}
|
|
}
|
|
}
|
|
MTDatacenterAddressSet *addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:addressList];
|
|
MTSignal *discoverSignal = [MTDiscoverConnectionSignals discoverSchemeWithContext:self datacenterId:datacenterId addressList:addressSet.addressList media:media isProxy:isProxy];
|
|
MTSignal *conditionSignal = [MTSignal single:@(true)];
|
|
for (id<MTContextChangeListener> listener in _changeListeners) {
|
|
if ([listener respondsToSelector:@selector(isContextNetworkAccessAllowed:)]) {
|
|
MTSignal *signal = [listener isContextNetworkAccessAllowed:self];
|
|
if (signal != nil) {
|
|
conditionSignal = signal;
|
|
}
|
|
}
|
|
}
|
|
MTSignal *filteredSignal = [[conditionSignal mapToSignal:^(NSNumber *value) {
|
|
if ([value boolValue]) {
|
|
return discoverSignal;
|
|
} else {
|
|
return [MTSignal never];
|
|
}
|
|
}] take:1];
|
|
|
|
_transportSchemeDisposableByDatacenterId[@(datacenterId)] = [[filteredSignal onDispose:^
|
|
{
|
|
__strong MTContext *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
[strongSelf->_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
|
|
}];
|
|
}
|
|
}] startWithNext:^(MTTransportScheme *next)
|
|
{
|
|
if (MTLogEnabled()) {
|
|
MTLog(@"scheme: %@", next);
|
|
}
|
|
__strong MTContext *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
[strongSelf updateTransportSchemeForDatacenterWithId:datacenterId transportScheme:next media:media isProxy:isProxy];
|
|
}
|
|
} error:^(id error)
|
|
{
|
|
|
|
} completed:^
|
|
{
|
|
|
|
}];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)_withTransportSchemeStatsForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme process:(MTTransportSchemeStats * (^)(MTTransportSchemeStats *))process {
|
|
NSAssert([[MTContext contextQueue] isCurrentQueue], @"[[MTContext contextQueue] isCurrentQueue]");
|
|
if (_transportSchemeStats[@(datacenterId)] == nil) {
|
|
_transportSchemeStats[@(datacenterId)] = [[NSMutableDictionary alloc] init];
|
|
}
|
|
MTTransportSchemeStats *current = _transportSchemeStats[@(datacenterId)][transportScheme.address];
|
|
if (current == nil) {
|
|
current = [[MTTransportSchemeStats alloc] initWithLastFailureTimestamp:0 lastResponseTimestamp:0];
|
|
}
|
|
MTTransportSchemeStats *updated = process(current);
|
|
if (updated == nil || ![updated isEqual:current]) {
|
|
if (updated == nil) {
|
|
[_transportSchemeStats[@(datacenterId)] removeObjectForKey:transportScheme.address];
|
|
} else {
|
|
if (MTLogEnabled()) {
|
|
//MTLog(@"Updated stats for %@: %@", transportScheme.address, updated);
|
|
}
|
|
_transportSchemeStats[@(datacenterId)][transportScheme.address] = updated;
|
|
}
|
|
[self _scheduleSyncTransportSchemeStats];
|
|
}
|
|
}
|
|
|
|
- (void)_scheduleSyncTransportSchemeStats {
|
|
NSAssert([[MTContext contextQueue] isCurrentQueue], @"[[MTContext contextQueue] isCurrentQueue]");
|
|
if (_schemeStatsSyncTimer == nil) {
|
|
__weak MTContext *weakSelf = self;
|
|
_schemeStatsSyncTimer = [[MTTimer alloc] initWithTimeout:5.0 repeat:false completion:^{
|
|
__strong MTContext *strongSelf = weakSelf;
|
|
if (strongSelf == nil) {
|
|
return;
|
|
}
|
|
strongSelf->_schemeStatsSyncTimer = nil;
|
|
[strongSelf _syncTransportSchemeStats];
|
|
} queue:[MTContext contextQueue].nativeQueue];
|
|
[_schemeStatsSyncTimer start];
|
|
}
|
|
}
|
|
|
|
- (void)_syncTransportSchemeStats {
|
|
NSAssert([[MTContext contextQueue] isCurrentQueue], @"[[MTContext contextQueue] isCurrentQueue]");
|
|
[_keychain setObject:_transportSchemeStats forKey:@"transportSchemeStats_v1" group:@"temp"];
|
|
}
|
|
|
|
- (void)reportTransportSchemeFailureForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
[self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:transportScheme process:^(MTTransportSchemeStats *current) {
|
|
return [current withUpdatedLastFailureTimestamp:(int32_t)CFAbsoluteTimeGetCurrent()];
|
|
}];
|
|
}];
|
|
}
|
|
|
|
- (void)reportTransportSchemeSuccessForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
[self _withTransportSchemeStatsForDatacenterId:datacenterId transportScheme:transportScheme process:^(MTTransportSchemeStats *current) {
|
|
return [current withUpdatedLastResponseTimestamp:(int32_t)CFAbsoluteTimeGetCurrent()];
|
|
}];
|
|
}];
|
|
}
|
|
|
|
- (void)invalidateTransportSchemesForDatacenterIds:(NSArray<NSNumber *> * _Nonnull)datacenterIds {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
for (NSNumber *datacenterId in datacenterIds) {
|
|
[self transportSchemeForDatacenterWithIdRequired:[datacenterId integerValue] moreOptimalThan:nil beginWithHttp:false media:false isProxy:_apiEnvironment.socksProxySettings != nil];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)invalidateTransportSchemesForKnownDatacenterIds {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
NSMutableSet *datacenterIds = [[NSMutableSet alloc] init];
|
|
|
|
for (NSNumber *nId in _datacenterAddressSetById.allKeys) {
|
|
[datacenterIds addObject:nId];
|
|
}
|
|
|
|
for (NSNumber *nId in _datacenterSeedAddressSetById.allKeys) {
|
|
[datacenterIds addObject:nId];
|
|
}
|
|
|
|
for (NSNumber *datacenterId in datacenterIds) {
|
|
[self transportSchemeForDatacenterWithIdRequired:[datacenterId integerValue] moreOptimalThan:nil beginWithHttp:false media:false isProxy:_apiEnvironment.socksProxySettings != nil];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)invalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme isProbablyHttp:(bool)isProbablyHttp media:(bool)media
|
|
{
|
|
if (transportScheme == nil)
|
|
return;
|
|
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
[self transportSchemeForDatacenterWithIdRequired:datacenterId moreOptimalThan:transportScheme beginWithHttp:isProbablyHttp media:media isProxy:_apiEnvironment.socksProxySettings != nil];
|
|
|
|
double delay = 20.0f;
|
|
if (_apiEnvironment.networkSettings == nil || _apiEnvironment.networkSettings.reducedBackupDiscoveryTimeout) {
|
|
delay = 5.0;
|
|
}
|
|
[self _beginBackupAddressDiscoveryWithDelay:delay];
|
|
}];
|
|
}
|
|
|
|
- (void)_beginBackupAddressDiscoveryWithDelay:(double)delay {
|
|
if (_backupAddressListDisposable == nil && _discoverBackupAddressListSignal != nil) {
|
|
__weak MTContext *weakSelf = self;
|
|
_backupAddressListDisposable = [[[_discoverBackupAddressListSignal delay:delay onQueue:[MTQueue mainQueue]] onDispose:^{
|
|
__strong MTContext *strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
[strongSelf->_backupAddressListDisposable dispose];
|
|
strongSelf->_backupAddressListDisposable = nil;
|
|
}
|
|
}] startWithNext:nil];
|
|
}
|
|
}
|
|
|
|
- (void)beginExplicitBackupAddressDiscovery {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
[_backupAddressListDisposable dispose];
|
|
_backupAddressListDisposable = nil;
|
|
[self _beginBackupAddressDiscoveryWithDelay:0.0];
|
|
}];
|
|
}
|
|
|
|
- (void)revalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if ([transportScheme isOptimal])
|
|
{
|
|
if (_transportSchemeDisposableByDatacenterId == nil)
|
|
_transportSchemeDisposableByDatacenterId = [[NSMutableDictionary alloc] init];
|
|
id<MTDisposable> disposable = _transportSchemeDisposableByDatacenterId[@(datacenterId)];
|
|
[disposable dispose];
|
|
[_transportSchemeDisposableByDatacenterId removeObjectForKey:@(datacenterId)];
|
|
}
|
|
if (_backupAddressListDisposable != nil) {
|
|
[_backupAddressListDisposable dispose];
|
|
_backupAddressListDisposable = nil;
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id)authToken
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (authToken != nil)
|
|
_authTokenById[@(datacenterId)] = authToken;
|
|
else
|
|
[_authTokenById removeObjectForKey:@(datacenterId)];
|
|
[_keychain setObject:_authTokenById forKey:@"authTokenById" group:@"persistent"];
|
|
|
|
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
|
|
|
for (id<MTContextChangeListener> listener in currentListeners)
|
|
{
|
|
if ([listener respondsToSelector:@selector(contextDatacenterAuthTokenUpdated:datacenterId:authToken:)])
|
|
[listener contextDatacenterAuthTokenUpdated:self datacenterId:datacenterId authToken:authToken];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)addressSetForDatacenterWithIdRequired:(NSInteger)datacenterId
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (_discoverDatacenterAddressActions[@(datacenterId)] == nil)
|
|
{
|
|
MTDiscoverDatacenterAddressAction *discoverAction = [[MTDiscoverDatacenterAddressAction alloc] init];
|
|
discoverAction.delegate = self;
|
|
_discoverDatacenterAddressActions[@(datacenterId)] = discoverAction;
|
|
[discoverAction execute:self datacenterId:datacenterId];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)discoverDatacenterAddressActionCompleted:(MTDiscoverDatacenterAddressAction *)action
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
for (NSNumber *nDatacenterId in _discoverDatacenterAddressActions)
|
|
{
|
|
if (_discoverDatacenterAddressActions[nDatacenterId] == action)
|
|
{
|
|
[_discoverDatacenterAddressActions removeObjectForKey:nDatacenterId];
|
|
|
|
break;
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (_datacenterAuthActions[@(datacenterId)] == nil)
|
|
{
|
|
MTDatacenterAuthAction *authAction = [[MTDatacenterAuthAction alloc] initWithTempAuth:false tempAuthKeyType:MTDatacenterAuthTempKeyTypeMain];
|
|
authAction.delegate = self;
|
|
_datacenterAuthActions[@(datacenterId)] = authAction;
|
|
[authAction execute:self datacenterId:datacenterId isCdn:isCdn];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)tempAuthKeyForDatacenterWithIdRequired:(NSInteger)datacenterId keyType:(MTDatacenterAuthTempKeyType)keyType {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
if (_datacenterTempAuthActions[@(datacenterId)] == nil) {
|
|
MTDatacenterAuthAction *authAction = [[MTDatacenterAuthAction alloc] initWithTempAuth:true tempAuthKeyType:keyType];
|
|
authAction.delegate = self;
|
|
_datacenterTempAuthActions[@(datacenterId)] = authAction;
|
|
[authAction execute:self datacenterId:datacenterId isCdn:false];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)datacenterAuthActionCompleted:(MTDatacenterAuthAction *)action
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (action.tempAuth) {
|
|
for (NSNumber *nDatacenterId in _datacenterTempAuthActions) {
|
|
if (_datacenterTempAuthActions[nDatacenterId] == action) {
|
|
[_datacenterTempAuthActions removeObjectForKey:nDatacenterId];
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (NSNumber *nDatacenterId in _datacenterAuthActions) {
|
|
if (_datacenterAuthActions[nDatacenterId] == action) {
|
|
[_datacenterAuthActions removeObjectForKey:nDatacenterId];
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)authTokenForDatacenterWithIdRequired:(NSInteger)datacenterId authToken:(id)authToken masterDatacenterId:(NSInteger)masterDatacenterId
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
if (authToken == nil)
|
|
return;
|
|
|
|
if (_datacenterTransferAuthActions[@(datacenterId)] == nil && masterDatacenterId != datacenterId)
|
|
{
|
|
MTDatacenterTransferAuthAction *transferAction = [[MTDatacenterTransferAuthAction alloc] init];
|
|
transferAction.delegate = self;
|
|
_datacenterTransferAuthActions[@(datacenterId)] = transferAction;
|
|
[transferAction execute:self masterDatacenterId:masterDatacenterId destinationDatacenterId:datacenterId authToken:authToken];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)datacenterTransferAuthActionCompleted:(MTDatacenterTransferAuthAction *)action
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
for (NSNumber *nDatacenterId in _datacenterTransferAuthActions)
|
|
{
|
|
if (_datacenterTransferAuthActions[nDatacenterId] == action)
|
|
{
|
|
[_datacenterTransferAuthActions removeObjectForKey:nDatacenterId];
|
|
|
|
break;
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)reportProblemsWithDatacenterAddressForId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address
|
|
{
|
|
|
|
}
|
|
|
|
- (void)updateApiEnvironment:(MTApiEnvironment *(^)(MTApiEnvironment *))f {
|
|
[[MTContext contextQueue] dispatchOnQueue:^{
|
|
MTApiEnvironment *apiEnvironment = f(_apiEnvironment);
|
|
if (apiEnvironment != nil) {
|
|
_apiEnvironment = apiEnvironment;
|
|
|
|
NSArray *currentListeners = [[NSArray alloc] initWithArray:_changeListeners];
|
|
for (id<MTContextChangeListener> listener in currentListeners)
|
|
{
|
|
if ([listener respondsToSelector:@selector(contextApiEnvironmentUpdated:apiEnvironment:)]) {
|
|
[listener contextApiEnvironmentUpdated:self apiEnvironment:apiEnvironment];
|
|
}
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)updatePeriodicTasks
|
|
{
|
|
[[MTContext contextQueue] dispatchOnQueue:^
|
|
{
|
|
int64_t saltsRequiredAtLeastUntilMessageId = (int64_t)(([self globalTime] + 24 * 60.0 * 60.0) * 4294967296);
|
|
|
|
[_datacenterAuthInfoById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAuthInfo *authInfo, __unused BOOL *stop)
|
|
{
|
|
if ([authInfo authSaltForMessageId:saltsRequiredAtLeastUntilMessageId == 0])
|
|
{
|
|
#warning TODO
|
|
}
|
|
}];
|
|
}];
|
|
}
|
|
|
|
@end
|