MTProto
protocol
MTProtoDelegate
// network availability changed
- (void)mtProtoNetworkAvailabilityChanged:(MTProto *)mtProto isNetworkAvailable:(bool)isNetworkAvailable;
// connection state changed
- (void)mtProtoConnectionStateChanged:(MTProto *)mtProto state:(MTProtoConnectionState *)state;
// context update state changed
- (void)mtProtoConnectionContextUpdateStateChanged:(MTProto *)mtProto isUpdatingConnectionContext:(bool)isUpdatingConnectionContext;
// service tasks state changed
- (void)mtProtoServiceTasksStateChanged:(MTProto *)mtProto isPerformingServiceTasks:(bool)isPerformingServiceTasks;
class
MTProto
// delegate
@property (nonatomic, weak) id<MTProtoDelegate> delegate;
// context
@property (nonatomic, strong, readonly) MTContext *context;
// api environment
@property (nonatomic, strong, readonly) MTApiEnvironment *apiEnvironment;
// datacenterId
@property (nonatomic) NSInteger datacenterId;
// should stay connected
@property (nonatomic) bool shouldStayConnected;
// use unauthorized mode
@property (nonatomic) bool useUnauthorizedMode;
// use temp auth keys
@property (nonatomic) bool useTempAuthKeys;
// prefer media
@property (nonatomic) bool media;
// enforce media
@property (nonatomic) bool enforceMedia;
// prefer cdn
@property (nonatomic) bool cdn;
// check for proxy connection issues
@property (nonatomic) bool checkForProxyConnectionIssues;
// auth token
@property (nonatomic) id requiredAuthToken;
// auth token master datacenter id
@property (nonatomic) NSInteger authTokenMasterDatacenterId;
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo;
- (void)pause;
1
2
3
4
5
6
7
8
9
10
| + (MTQueue *)managerQueue
{
static MTQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[MTQueue alloc] initWithName:"org.mtproto.managerQueue"];
});
return queue;
}
|
- (instancetype)initWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId usageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo
{
#ifdef DEBUG
NSAssert(context != nil, @"context should not be nil");
NSAssert(context.serialization != nil, @"context serialization should not be nil");
NSAssert(datacenterId != 0, @"datacenterId should not be 0");
#endif
self = [super init];
if (self != nil)
{
_context = context;
_datacenterId = datacenterId;
_usageCalculationInfo = usageCalculationInfo;
_apiEnvironment = context.apiEnvironment;
[_context addChangeListener:self];
_messageServices = [[NSMutableArray alloc] init];
_sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:_context];
_authInfo = [_context authInfoForDatacenterWithId:_datacenterId];
_shouldStayConnected = true;
}
return self;
}
// set network usage calculation info
- (void)setUsageCalculationInfo:(MTNetworkUsageCalculationInfo *)usageCalculationInfo {
[[MTProto managerQueue] dispatchOnQueue:^{
_usageCalculationInfo = usageCalculationInfo;
[_transport setUsageCalculationInfo:usageCalculationInfo];
}];
}
Network Availability Changed
- (void)transportNetworkAvailabilityChanged:(MTTransport *)transport isNetworkAvailable:(bool)isNetworkAvailable
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (transport != _transport)
return;
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p network state: %s]", self, _context, isNetworkAvailable ? "available" : "waiting");
}
// notify message services
for (id<MTMessageService> messageService in _messageServices)
{
if ([messageService respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)])
[messageService mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable];
}
// notify delegate
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)])
[delegate mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable];
}];
}
Connection Failed
- (void)transportConnectionFailed:(MTTransport *)transport scheme:(MTTransportScheme *)scheme {
[[MTProto managerQueue] dispatchOnQueue:^{
if (transport != _transport)
return;
// route to context
[_context reportTransportSchemeFailureForDatacenterId:_datacenterId transportScheme:scheme];
}];
}
Finalize Session
- (void)finalizeSession {
[[MTProto managerQueue] dispatchOnQueue:^{
if (_authInfo.authKeyId != 0 && !self.cdn) {
// schedule session cleanup for authkey id
[_context scheduleSessionCleanupForAuthKeyId:_authInfo.authKeyId sessionInfo:_sessionInfo];
}
}];
Pause
- (void)pause
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if ((_mtState & MTProtoStatePaused) == 0)
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p pause]", self, _context);
}
MTShortLog(@"[MTProto#%p@%p pause]", self, _context);
// set state paused
_mtState |= MTProtoStatePaused;
[self setMtState:_mtState | MTProtoStatePaused];
// set transport to nil
[self setTransport:nil];
}
}];
}
Resume
- (void)resume
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (_mtState & MTProtoStatePaused)
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p resume]", self, _context);
}
MTShortLog(@"[MTProto#%p@%p resume]", self, _context);
// set state resumed
[self setMtState:_mtState & (~MTProtoStatePaused)];
// reset tranport
[self resetTransport];
// request transport transaction
[self requestTransportTransaction];
}
}];
}
Stop
- (void)stop
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if ((_mtState & MTProtoStateStopped) == 0)
{
// set state stopped
[self setMtState:_mtState | MTProtoStateStopped];
if (_transport != nil)
{
// break tranport delegate
_transport.delegate = nil;
// stop transport
[_transport stop];
// set tranport to nil
[self setTransport:nil];
}
}
}];
Update Connection State
- (void)updateConnectionState
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (_transport != nil)
[_transport updateConnectionState];
else
{
id<MTProtoDelegate> delegate = _delegate;
// notify network availability changed
if ([delegate respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)])
[delegate mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:false];
// notify connection state changed
if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:state:)])
[delegate mtProtoConnectionStateChanged:self state:nil];
// notify connection context update state changed
if ([delegate respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[delegate mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:false];
}
}];
}
Set Transport
- (void)setTransport:(MTTransport *)transport
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p changing transport %@#%p to %@#%p]", self, _context, [_transport class] == nil ? @"" : NSStringFromClass([_transport class]), _transport, [transport class] == nil ? @"" : NSStringFromClass([transport class]), transport);
}
// notify all transactions may have failed
[self allTransactionsMayHaveFailed];
MTTransport *previousTransport = _transport;
[_transport activeTransactionIds:^(NSArray *transactionIds)
{
// notify current transactions in current transport may have failed
[self transportTransactionsMayHaveFailed:previousTransport transactionIds:transactionIds];
}];
_timeFixContext = nil;
_bindingTempAuthKeyContext = nil;
if (_transport != nil)
[self removeMessageService:_transport];
_transport = transport;
// stop current transport
[previousTransport stop];
if (_transport != nil && _useTempAuthKeys) {
assert(false);
/*MTDatacenterAuthTempKeyType tempAuthKeyType = MTDatacenterAuthTempKeyTypeMain;
if (_transport.scheme.address.preferForMedia) {
tempAuthKeyType = MTDatacenterAuthTempKeyTypeMedia;
}
MTDatacenterAuthKey *effectiveAuthKey = [_authInfo tempAuthKeyWithType:tempAuthKeyType];
if (effectiveAuthKey == nil) {
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p setTransport temp auth key missing]", self);
}
}*/
}
if (_transport != nil)
// add the new transport(message service)
[self addMessageService:_transport];
// update connection state
[self updateConnectionState];
}];
}
Reset SessionInfo
- (void)resetSessionInfo
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p resetting session]", self, _context);
}
MTShortLog(@"[MTProto#%p@%p resetting session]", self, _context);
if (_authInfo.authKeyId != 0 && !self.cdn) {
// clean up session for auth key
[_context scheduleSessionCleanupForAuthKeyId:_authInfo.authKeyId sessionInfo:_sessionInfo];
}
// new session info
_sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:_context];
_timeFixContext = nil;
_bindingTempAuthKeyContext = nil;
for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--)
{
// notify session changed
id<MTMessageService> messageService = _messageServices[(NSUInteger)i];
if ([messageService respondsToSelector:@selector(mtProtoDidChangeSession:)])
[messageService mtProtoDidChangeSession:self];
}
// reset transport
[self resetTransport];
// request transport transaction
[self requestTransportTransaction];
}];
}
Reset Transport
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
| - (void)resetTransport
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (_mtState & MTProtoStateStopped)
return;
if (_transport != nil)
{
_transport.delegate = nil;
[_transport stop];
[self setTransport:nil];
}
NSArray<MTTransportScheme *> *transportSchemes = [_context transportSchemesForDatacenterWithId:_datacenterId media:_media enforceMedia:_enforceMedia isProxy:_apiEnvironment.socksProxySettings != nil];
/*MTDatacenterAuthTempKeyType tempAuthKeyType = MTDatacenterAuthTempKeyTypeMain;
if (_transportScheme.address.preferForMedia) {
tempAuthKeyType = MTDatacenterAuthTempKeyTypeMedia;
}*/
if (transportSchemes.count == 0) {
// prepare schemes
if ((_mtState & MTProtoStateAwaitingDatacenterScheme) == 0) {
[self setMtState:_mtState | MTProtoStateAwaitingDatacenterScheme];
[_context transportSchemeForDatacenterWithIdRequired:_datacenterId media:_media];
}
} else if (!_useUnauthorizedMode && [_context authInfoForDatacenterWithId:_datacenterId] == nil) {
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p authInfoForDatacenterWithId:%d is nil]", self, _context, _datacenterId);
}
MTShortLog(@"[MTProto#%p@%p authInfoForDatacenterWithId:%d is nil]", self, _context, _datacenterId);
if ((_mtState & MTProtoStateAwaitingDatacenterAuthorization) == 0) {
[self setMtState:_mtState | MTProtoStateAwaitingDatacenterAuthorization];
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p requesting authInfo for %d]", self, _context, _datacenterId);
}
MTShortLog(@"[MTProto#%p@%p requesting authInfo for %d]", self, _context, _datacenterId);
// prepare auth info
[_context authInfoForDatacenterWithIdRequired:_datacenterId isCdn:_cdn];
}
}/* else if (!_useUnauthorizedMode && _useTempAuthKeys && [[_context authInfoForDatacenterWithId:_datacenterId] tempAuthKeyWithType:tempAuthKeyType] == nil) {
if ((_mtState & MTProtoStateAwaitingDatacenterTempAuthKey) == 0) {
[self setMtState:_mtState | MTProtoStateAwaitingDatacenterTempAuthKey];
[_context tempAuthKeyForDatacenterWithIdRequired:_datacenterId keyType:tempAuthKeyType];
}
}*/
else if (_requiredAuthToken != nil && !_useUnauthorizedMode && ![_requiredAuthToken isEqual:[_context authTokenForDatacenterWithId:_datacenterId]]) {
if ((_mtState & MTProtoStateAwaitingDatacenterAuthToken) == 0) {
[self setMtState:_mtState | MTProtoStateAwaitingDatacenterAuthToken];
// prepare auth token
[_context authTokenForDatacenterWithIdRequired:_datacenterId authToken:_requiredAuthToken masterDatacenterId:_authTokenMasterDatacenterId];
}
} else {
assert(transportSchemes.count != 0);
MTTransport *transport = [[MTTcpTransport alloc] initWithDelegate:self context:_context datacenterId:_datacenterId schemes:transportSchemes proxySettings:_context.apiEnvironment.socksProxySettings usageCalculationInfo:_usageCalculationInfo];
// set transport
[self setTransport:transport];
//[self checkTempAuthKeyBinding:transport.scheme.address];
}
}];
}
|
Set MtState
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| - (void)setMtState:(int)mtState
{
bool wasPerformingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts;
_mtState = mtState;
bool performingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts;
if (performingServiceTasks != wasPerformingServiceTasks)
{
bool haveResendMessagesPending = false;
for (id<MTMessageService> messageService in _messageServices)
{
if ([messageService isKindOfClass:[MTResendMessageService class]])
{
haveResendMessagesPending = true;
break;
}
}
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no");
}
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, haveResendMessagesPending ? "yes" : "no");
for (id<MTMessageService> messageService in _messageServices)
{
// notify service tasks state changed
if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)])
[messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:performingServiceTasks || haveResendMessagesPending];
}
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)])
[delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:performingServiceTasks || haveResendMessagesPending];
}
}
|
Add Message Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| - (void)addMessageService:(id<MTMessageService>)messageService
{
if ([messageService respondsToSelector:@selector(mtProtoWillAddService:)])
[messageService mtProtoWillAddService:self];
[[MTProto managerQueue] dispatchOnQueue:^
{
bool notifyAboutServiceTask = false;
if ([messageService isKindOfClass:[MTResendMessageService class]])
{
notifyAboutServiceTask = true;
for (id<MTMessageService> currentService in _messageServices)
{
if ([currentService isKindOfClass:[MTResendMessageService class]])
{
notifyAboutServiceTask = false;
break;
}
}
}
if (![_messageServices containsObject:messageService])
{
[_messageServices addObject:messageService];
// notify did add service
if ([messageService respondsToSelector:@selector(mtProtoDidAddService:)])
[messageService mtProtoDidAddService:self];
}
if (notifyAboutServiceTask)
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes");
}
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "yes");
for (id<MTMessageService> messageService in _messageServices)
{
// notify service tasks state changed
if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)])
[messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:true];
}
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)])
[delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:true];
}
}];
}
|
Remove Message Service
- (void)removeMessageService:(id<MTMessageService>)messageService
{
if (messageService == nil)
return;
[[MTProto managerQueue] dispatchOnQueue:^
{
if ([_messageServices containsObject:messageService])
{
[_messageServices removeObject:messageService];
// notify did remove service
if ([messageService respondsToSelector:@selector(mtProtoDidRemoveService:)])
[messageService mtProtoDidRemoveService:self];
bool notifyAboutServiceTask = false;
if ([messageService isKindOfClass:[MTResendMessageService class]])
{
notifyAboutServiceTask = true;
for (id<MTMessageService> currentService in _messageServices)
{
if ([currentService isKindOfClass:[MTResendMessageService class]])
{
notifyAboutServiceTask = false;
break;
}
}
}
if (notifyAboutServiceTask)
{
bool performingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts;
if (!performingServiceTasks)
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no");
}
MTShortLog(@"[MTProto#%p@%p service tasks state: %d, resend: %s]", self, _context, _mtState, "no");
for (id<MTMessageService> messageService in _messageServices)
{
// notify service tasks state changed
if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)])
[messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:false];
}
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)])
[delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:false];
}
}
}
}];
}
// message service queue
- (MTQueue *)messageServiceQueue
{
return [MTProto managerQueue];
}
Time Sync
- (void)initiateTimeSync
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if ((_mtState & MTProtoStateAwaitingTimeFixAndSalts) == 0)
{
[self setMtState:_mtState | MTProtoStateAwaitingTimeFixAndSalts];
[self requestTimeResync];
}
}];
}
- (void)completeTimeSync
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if ((_mtState & MTProtoStateAwaitingTimeFixAndSalts) != 0)
{
[self setMtState:_mtState & (~MTProtoStateAwaitingTimeFixAndSalts)];
for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--)
{
id<MTMessageService> messageService = _messageServices[(NSUInteger)i];
if ([messageService isKindOfClass:[MTTimeSyncMessageService class]])
{
((MTTimeSyncMessageService *)messageService).delegate = nil;
[self removeMessageService:messageService];
}
}
}
}];
}
- (void)requestTimeResync
{
[[MTProto managerQueue] dispatchOnQueue:^
{
bool alreadySyncing = false;
for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--)
{
id<MTMessageService> messageService = _messageServices[(NSUInteger)i];
if ([messageService isKindOfClass:[MTTimeSyncMessageService class]])
{
alreadySyncing = true;
break;
}
}
if (!alreadySyncing)
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p begin time sync]", self, _context);
}
MTShortLog(@"[MTProto#%p@%p begin time sync]", self, _context);
MTTimeSyncMessageService *timeSyncService = [[MTTimeSyncMessageService alloc] init];
timeSyncService.delegate = self;
[self addMessageService:timeSyncService];
}
}];
}
Request Message
- (void)requestMessageWithId:(int64_t)messageId
{
bool alreadyRequestingThisMessage = false;
for (id<MTMessageService> messageService in _messageServices)
{
if ([messageService isKindOfClass:[MTResendMessageService class]])
{
if (((MTResendMessageService *)messageService).messageId == messageId)
{
alreadyRequestingThisMessage = true;
break;
}
}
}
if (!alreadyRequestingThisMessage && ![_sessionInfo messageProcessed:messageId])
{
// resent if needed
MTResendMessageService *resendService = [[MTResendMessageService alloc] initWithMessageId:messageId];
resendService.delegate = self;
[self addMessageService:resendService];
}
}
// remove completed resent service
- (void)resendMessageServiceCompleted:(MTResendMessageService *)resendService
{
[[MTProto managerQueue] dispatchOnQueue:^
{
resendService.delegate = nil;
[self removeMessageService:resendService];
}];
}
RequestTransportTransaction
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| - (void)requestTransportTransaction
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (!_willRequestTransactionOnNextQueuePass)
{
_willRequestTransactionOnNextQueuePass = true;
dispatch_async([MTProto managerQueue].nativeQueue, ^
{
_willRequestTransactionOnNextQueuePass = false;
if ([self isStopped] || [self isPaused])
return;
if (_transport == nil)
// reset transport
[self resetTransport];
// notify transport to transaction in next queue pass
[_transport setDelegateNeedsTransaction];
});
}
}];
}
|
Request Secure TransportReset
(void)requestSecureTransportReset
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if ([self isStopped])
return;
if (_transport != nil)
[_transport reset];
}];
}
Can Ask for Transaction
- (bool)canAskForTransactions
{
return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterTempAuthKey | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateAwaitingTimeFixAndSalts | MTProtoStateBindingTempAuthKey | MTProtoStateStopped)) == 0;
}
Can Ask for Service Transaction
- (bool)canAskForServiceTransactions
{
return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterTempAuthKey | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateStopped)) == 0;
}
Time Fix or Salt Missing
- (bool)timeFixOrSaltsMissing
{
return _mtState & MTProtoStateAwaitingTimeFixAndSalts;
}
Binding Temp Auth Key
- (bool)bindingTempAuthKey
{
return _mtState & MTProtoStateBindingTempAuthKey;
}
Stopped
1
2
3
4
| - (bool)isStopped
{
return (_mtState & MTProtoStateStopped) != 0;
}
|
Paused
- (bool)isPaused
{
return (_mtState & MTProtoStatePaused) != 0;
}
Network Availability Changed
- (void)transportNetworkAvailabilityChanged:(MTTransport *)transport isNetworkAvailable:(bool)isNetworkAvailable
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (transport != _transport)
return;
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p network state: %s]", self, _context, isNetworkAvailable ? "available" : "waiting");
}
for (id<MTMessageService> messageService in _messageServices)
{
if ([messageService respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)])
[messageService mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable];
}
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)])
[delegate mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable];
}];
}
Connection Failed
- (void)transportConnectionFailed:(MTTransport *)transport scheme:(MTTransportScheme *)scheme {
[[MTProto managerQueue] dispatchOnQueue:^{
if (transport != _transport)
return;
[_context reportTransportSchemeFailureForDatacenterId:_datacenterId transportScheme:scheme];
}];
}
Connection State Changed
- (void)transportConnectionStateChanged:(MTTransport *)transport isConnected:(bool)isConnected proxySettings:(MTSocksProxySettings *)proxySettings
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (transport != _transport)
return;
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p transport #%p connection state: %s]", self, _context, transport, isConnected ? "connected" : "connecting");
}
for (id<MTMessageService> messageService in _messageServices)
{
if ([messageService respondsToSelector:@selector(mtProtoConnectionStateChanged:isConnected:)])
[messageService mtProtoConnectionStateChanged:self isConnected:isConnected];
}
MTProtoConnectionState *connectionState = [[MTProtoConnectionState alloc] initWithIsConnected:isConnected proxyAddress:proxySettings.ip proxyHasConnectionIssues:[_probingStatus boolValue]];
_connectionState = connectionState;
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:state:)]) {
[delegate mtProtoConnectionStateChanged:self state:connectionState];
}
}];
}
Connection Context State Updated
- (void)transportConnectionContextUpdateStateChanged:(MTTransport *)transport isUpdatingConnectionContext:(bool)isUpdatingConnectionContext
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (transport != _transport)
return;
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p connection context update state: %s]", self, _context, isUpdatingConnectionContext ? "updating" : "up to date");
}
for (id<MTMessageService> messageService in _messageServices)
{
if ([messageService respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[messageService mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:isUpdatingConnectionContext];
}
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)])
[delegate mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:isUpdatingConnectionContext];
}];
}
Connection Problems Status Changed
- (void)transportConnectionProblemsStatusChanged:(MTTransport *)transport scheme:(MTTransportScheme *)scheme hasConnectionProblems:(bool)hasConnectionProblems isProbablyHttp:(bool)isProbablyHttp
{
[[MTProto managerQueue] dispatchOnQueue:^ {
if (_transport != transport) {
return;
}
if (hasConnectionProblems) {
[_context reportTransportSchemeFailureForDatacenterId:_datacenterId transportScheme:scheme];
[_context invalidateTransportSchemeForDatacenterId:_datacenterId transportScheme:scheme isProbablyHttp:isProbablyHttp media:_media];
} else {
[_context revalidateTransportSchemeForDatacenterId:_datacenterId transportScheme:scheme media:_media];
}
if (!hasConnectionProblems || transport.proxySettings == nil || !_checkForProxyConnectionIssues) {
if (_isProbing) {
_isProbing = false;
[_probingDisposable setDisposable:nil];
if (_probingStatus != nil) {
_probingStatus = nil;
[self _updateConnectionIssuesStatus:false];
}
}
} else {
if (!_isProbing) {
_isProbing = true;
__weak MTProto *weakSelf = self;
MTSignal *checkSignal = [[MTConnectionProbing probeProxyWithContext:_context datacenterId:_datacenterId settings:transport.proxySettings] delay:5.0 onQueue:[MTQueue concurrentDefaultQueue]];
checkSignal = [[checkSignal then:[[MTSignal complete] delay:20.0 onQueue:[MTQueue concurrentDefaultQueue]]] restart];
[_probingDisposable setDisposable:[checkSignal startWithNext:^(NSNumber *next) {
[[MTProto managerQueue] dispatchOnQueue:^{
__strong MTProto *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
if (strongSelf->_isProbing) {
strongSelf->_probingStatus = next;
[strongSelf _updateConnectionIssuesStatus:[strongSelf->_probingStatus boolValue]];
}
}];
}]];
}
}
}];
}
Update Connection Issue Status
1
2
3
4
5
6
7
8
9
| - (void)_updateConnectionIssuesStatus:(bool)value {
if (_connectionState != nil) {
_connectionState = [[MTProtoConnectionState alloc] initWithIsConnected:_connectionState.isConnected proxyAddress:_connectionState.proxyAddress proxyHasConnectionIssues:value];
id<MTProtoDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:state:)]) {
[delegate mtProtoConnectionStateChanged:self state:_connectionState];
}
}
}
|
Outgoing Message Description
- (NSString *)outgoingMessageDescription:(MTOutgoingMessage *)message messageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo
{
return [[NSString alloc] initWithFormat:@"%@ (%" PRId64 "/%" PRId32 ")", message.metadata, message.messageId == 0 ? messageId : message.messageId, message.messageSeqNo == 0 ? message.messageSeqNo : messageSeqNo];
}
Outgoing Short Message Description
- (NSString *)outgoingShortMessageDescription:(MTOutgoingMessage *)message messageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo
{
return [[NSString alloc] initWithFormat:@"%@ (%" PRId64 "/%" PRId32 ")", message.shortMetadata, message.messageId == 0 ? messageId : message.messageId, message.messageSeqNo == 0 ? message.messageSeqNo : messageSeqNo];
}
Incoming Message Description
- (NSString *)incomingMessageDescription:(MTIncomingMessage *)message
{
return [[NSString alloc] initWithFormat:@"%@ (%" PRId64", %" PRId64"/%" PRId64")", message.body, message.messageId, message.authKeyId, message.sessionId];
}
Transport Ready for Transaction
- (void)transportReadyForTransaction:(MTTransport *)transport scheme:(MTTransportScheme *)scheme transportSpecificTransaction:(MTMessageTransaction *)transportSpecificTransaction forceConfirmations:(bool)forceConfirmations transactionReady:(void (^)(NSArray *))transactionReady
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (_transport != transport)
{
if (transactionReady)
transactionReady(nil);
return;
}
bool extendedPadding = false;
if (transport.proxySettings != nil && transport.proxySettings.secret != nil) {
MTProxySecret *parsedSecret = [MTProxySecret parseData:transport.proxySettings.secret];
if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) {
extendedPadding = true;
}
} else if (scheme.address.secret != nil) {
MTProxySecret *parsedSecret = [MTProxySecret parseData:scheme.address.secret];
if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) {
extendedPadding = true;
}
}
if ([self canAskForTransactions])
{
MTSessionInfo *transactionSessionInfo = _sessionInfo;
// prepare message transactions
NSMutableArray *messageTransactions = [[NSMutableArray alloc] init];
if (transportSpecificTransaction != nil)
{
if (!(transportSpecificTransaction.requiresEncryption && _useUnauthorizedMode) && (!transportSpecificTransaction.requiresEncryption || _authInfo != nil))
{
[messageTransactions addObject:transportSpecificTransaction];
}
}
bool anyTransactionHasHighPriorityMessages = false;
// collect message servcie transactions
NSMutableArray *messageServiceTransactions = [[NSMutableArray alloc] init];
for (id<MTMessageService> messageService in _messageServices)
{
if ([messageService respondsToSelector:@selector(mtProtoMessageTransaction:)])
{
MTMessageTransaction *messageTransaction = [messageService mtProtoMessageTransaction:self];
if (messageTransaction != nil)
{
for (MTOutgoingMessage *message in messageTransaction.messagePayload)
{
if (message.hasHighPriority)
{
anyTransactionHasHighPriorityMessages = true;
break;
}
}
[messageServiceTransactions addObject:messageTransaction];
}
}
}
// prepare Ack Message
if (forceConfirmations || !anyTransactionHasHighPriorityMessages || [transactionSessionInfo scheduledMessageConfirmationsExceedSize:MTMaxUnacknowledgedMessageSize orCount:MTMaxUnacknowledgedMessageCount])
{
NSArray *scheduledMessageConfirmations = [transactionSessionInfo scheduledMessageConfirmations];
if (scheduledMessageConfirmations.count != 0)
{
MTBuffer *msgsAckBuffer = [[MTBuffer alloc] init];
[msgsAckBuffer appendInt32:(int32_t)0x62d6b459];
[msgsAckBuffer appendInt32:481674261];
[msgsAckBuffer appendInt32:(int32_t)scheduledMessageConfirmations.count];
for (NSNumber *nMessageId in scheduledMessageConfirmations)
{
[msgsAckBuffer appendInt64:(int64_t)[nMessageId longLongValue]];
}
MTOutgoingMessage *outgoingMessage = [[MTOutgoingMessage alloc] initWithData:msgsAckBuffer.data metadata:@"msgsAck" shortMetadata:@"msgsAck"];
outgoingMessage.requiresConfirmation = false;
[messageTransactions addObject:[[MTMessageTransaction alloc] initWithMessagePayload:@[outgoingMessage] prepared:nil failed:nil completion:^(__unused NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId)
{
if (messageInternalIdToTransactionId[outgoingMessage.internalId] != nil && messageInternalIdToPreparedMessage[outgoingMessage.internalId] != nil)
{
[transactionSessionInfo assignTransactionId:messageInternalIdToTransactionId[outgoingMessage.internalId] toScheduledMessageConfirmationsWithIds:scheduledMessageConfirmations];
}
}]];
}
}
[messageTransactions addObjectsFromArray:messageServiceTransactions];
NSMutableArray *transactionMessageList = [[NSMutableArray alloc] init];
NSMutableDictionary *messageInternalIdToPreparedMessage = [[NSMutableDictionary alloc] init];
NSMutableDictionary *preparedMessageInternalIdToMessageInternalId = [[NSMutableDictionary alloc] init];
bool monotonityViolated = false;
bool saltSetEmpty = false;
int64_t messageSalt = 0;
if (!_useUnauthorizedMode)
{
messageSalt = [_authInfo authSaltForMessageId:[transactionSessionInfo actualClientMessagId]];
if (messageSalt == 0)
saltSetEmpty = true;
}
bool transactionNeedsQuickAck = false;
bool transactionExpectsDataInResponse = false;
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
for (MTOutgoingMessage *outgoingMessage in messageTransaction.messagePayload)
{
// prepare message data
NSData *messageData = outgoingMessage.data;
if (outgoingMessage.dynamicDecorator != nil)
{
// dynamicDecorator
id decoratedData = outgoingMessage.dynamicDecorator(messageData, messageInternalIdToPreparedMessage);
if (decoratedData != nil)
messageData = decoratedData;
}
NSData *data = messageData;
// prepare messageId & messageSeqNo
int64_t messageId = 0;
int32_t messageSeqNo = 0;
if (outgoingMessage.messageId == 0)
{
// generate client messageId
messageId = [transactionSessionInfo generateClientMessageId:&monotonityViolated];
// generate sequence no
messageSeqNo = [transactionSessionInfo takeSeqNo:outgoingMessage.requiresConfirmation];
}
else
{
messageId = outgoingMessage.messageId;
messageSeqNo = outgoingMessage.messageSeqNo;
}
if (MTLogEnabled()) {
NSString *messageDescription = [self outgoingMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo];
/*if ([messageDescription hasPrefix:@"updates.getDifference"]) {
static dispatch_once_t onceToken;
__block bool flag = false;
dispatch_once(&onceToken, ^{
flag = true;
});
if (flag) {
debugResetTransport = true;
}
}*/
MTLog(@"[MTProto#%p@%p preparing %@]", self, _context, messageDescription);
}
NSString *shortMessageDescription = [self outgoingShortMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo];
MTShortLog(@"[MTProto#%p@%p preparing %@]", self, _context, shortMessageDescription);
if (!monotonityViolated || _useUnauthorizedMode)
{
// construct preparedMessage into transactionMessageList
MTPreparedMessage *preparedMessage = [[MTPreparedMessage alloc] initWithData:data messageId:messageId seqNo:messageSeqNo salt:messageSalt requiresConfirmation:outgoingMessage.requiresConfirmation hasHighPriority:outgoingMessage.hasHighPriority inResponseToMessageId:outgoingMessage.inResponseToMessageId];
if (outgoingMessage.needsQuickAck)
transactionNeedsQuickAck = true;
if (outgoingMessage.requiresConfirmation)
transactionExpectsDataInResponse = true;
messageInternalIdToPreparedMessage[outgoingMessage.internalId] = preparedMessage;
preparedMessageInternalIdToMessageInternalId[preparedMessage.internalId] = outgoingMessage.internalId;
[transactionMessageList addObject:preparedMessage];
}
}
if ([transport needsParityCorrection] && !transactionExpectsDataInResponse)
transactionNeedsQuickAck = true;
}
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
if (messageTransaction.prepared) {
// notify prepared
messageTransaction.prepared(messageInternalIdToPreparedMessage);
}
}
if (monotonityViolated || saltSetEmpty)
{
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
// notify completed
if (messageTransaction.completion)
messageTransaction.completion(nil, nil, nil);
}
if (transactionReady != nil)
transactionReady(nil);
// monotonity violated
if (monotonityViolated)
{
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p client message id monotonity violated]", self, _context);
}
MTShortLog(@"[MTProto#%p@%p client message id monotonity violated]", self, _context);
// reset session info
[self resetSessionInfo];
}
else if (saltSetEmpty)
// sync time
[self initiateTimeSync];
}
else if (transactionReady != nil)
{
if (transactionMessageList.count != 0)
{
if (transactionMessageList.count != 1)
{
[transactionMessageList sortUsingComparator:^NSComparisonResult(MTPreparedMessage *message1, MTPreparedMessage *message2)
{
return message1.messageId < message2.messageId ? NSOrderedAscending : NSOrderedDescending;
}];
if (!forceConfirmations)
{
NSMutableArray *highPriorityMessages = nil;
for (NSInteger i = 0; i < (NSInteger)transactionMessageList.count; i++)
{
MTPreparedMessage *preparedMessage = transactionMessageList[(NSUInteger)i];
if (preparedMessage.hasHighPriority)
{
if (highPriorityMessages == nil)
highPriorityMessages = [[NSMutableArray alloc] init];
[highPriorityMessages addObject:preparedMessage];
[transactionMessageList removeObjectAtIndex:(NSUInteger)i];
i--;
}
}
if (highPriorityMessages != nil)
{
[highPriorityMessages addObjectsFromArray:transactionMessageList];
transactionMessageList = highPriorityMessages;
}
}
}
NSMutableArray *transactionPayloadList = [[NSMutableArray alloc] init];
NSMutableArray *messageInternalIdsByPayload = [[NSMutableArray alloc] init];
NSMutableArray *quickAckIdsByPayload = [[NSMutableArray alloc] init];
bool currentlyProcessingHighPriority = false;
for (NSUInteger i = 0; i < transactionMessageList.count; )
{
if (!_useUnauthorizedMode)
{
NSMutableArray *currentContainerMessages = [[NSMutableArray alloc] init];
NSUInteger currentContainerSize = 0;
// wrap message containers
for (NSUInteger j = i; j < transactionMessageList.count; j++, i++)
{
MTPreparedMessage *preparedMessage = transactionMessageList[j];
bool breakContainer = false;
if (!forceConfirmations)
{
if (preparedMessage.hasHighPriority)
currentlyProcessingHighPriority = true;
else if (currentlyProcessingHighPriority)
{
currentlyProcessingHighPriority = false;
breakContainer = true;
}
}
if (currentContainerSize + preparedMessage.data.length > MTMaxContainerSize || (breakContainer && currentContainerSize != 0))
{
if (currentContainerSize == 0)
{
[currentContainerMessages addObject:preparedMessage];
currentContainerSize += preparedMessage.data.length;
i++;
}
break;
}
else
{
[currentContainerMessages addObject:preparedMessage];
currentContainerSize += preparedMessage.data.length;
}
}
if (currentContainerMessages.count == 1)
{
int32_t quickAckId = 0;
NSData *messageData = [self _dataForEncryptedMessage:currentContainerMessages[0] sessionInfo:transactionSessionInfo quickAckId:&quickAckId address:scheme.address extendedPadding:extendedPadding];
if (messageData != nil)
{
[transactionPayloadList addObject:messageData];
[messageInternalIdsByPayload addObject:@[preparedMessageInternalIdToMessageInternalId[((MTPreparedMessage *)currentContainerMessages[0]).internalId]]];
[quickAckIdsByPayload addObject:@(quickAckId)];
}
}
else if (currentContainerMessages.count != 0)
{
int32_t quickAckId = 0;
NSData *containerData = [self _dataForEncryptedContainerWithMessages:currentContainerMessages sessionInfo:transactionSessionInfo quickAckId:&quickAckId address:scheme.address extendedPadding:extendedPadding];
if (containerData != nil)
{
[transactionPayloadList addObject:containerData];
NSMutableArray *messageInternalIds = [[NSMutableArray alloc] initWithCapacity:currentContainerMessages.count];
for (MTPreparedMessage *preparedMessage in currentContainerMessages)
{
[messageInternalIds addObject:preparedMessageInternalIdToMessageInternalId[preparedMessage.internalId]];
}
[messageInternalIdsByPayload addObject:messageInternalIds];
[quickAckIdsByPayload addObject:@(quickAckId)];
}
}
}
else
{
MTPreparedMessage *preparedMessage = transactionMessageList[i];
NSData *messageData = [self _dataForPlainMessage:preparedMessage extendedPadding:extendedPadding];
i++;
if (messageData != nil)
{
[transactionPayloadList addObject:messageData];
[messageInternalIdsByPayload addObject:@[preparedMessageInternalIdToMessageInternalId[preparedMessage.internalId]]];
[quickAckIdsByPayload addObject:@(0)];
}
}
}
// prepare transport transactions
if (transactionPayloadList.count != 0)
{
NSMutableArray *transportTransactions = [[NSMutableArray alloc] initWithCapacity:transactionPayloadList.count];
for (NSUInteger i = 0; i < transactionPayloadList.count; i++)
{
[transportTransactions addObject:[[MTTransportTransaction alloc] initWithPayload:transactionPayloadList[i] completion:^(bool success, id transactionId)
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (success)
{
NSMutableDictionary *messageInternalIdToTransactionId = [[NSMutableDictionary alloc] init];
NSMutableDictionary *messageInternalIdToQuickAckId = [[NSMutableDictionary alloc] init];
NSMutableDictionary *transactionMessageInternalIdToPreparedMessage = [[NSMutableDictionary alloc] init];
for (id messageInternalId in messageInternalIdsByPayload[i])
{
messageInternalIdToTransactionId[messageInternalId] = transactionId;
messageInternalIdToQuickAckId[messageInternalId] = quickAckIdsByPayload[i];
MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[messageInternalId];
if (preparedMessage != nil)
transactionMessageInternalIdToPreparedMessage[messageInternalId] = preparedMessage;
}
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
if (messageTransaction.completion)
messageTransaction.completion(messageInternalIdToTransactionId, transactionMessageInternalIdToPreparedMessage, messageInternalIdToQuickAckId);
}
}
else
{
NSMutableString *idsString = [[NSMutableString alloc] init];
for (id messageInternalId in messageInternalIdsByPayload[i])
{
MTOutgoingMessage *outgoingMessage = messageInternalIdToPreparedMessage[messageInternalId];
if (outgoingMessage != nil)
{
if (idsString.length != 0)
[idsString appendString:@", "];
[idsString appendFormat:@"%" PRId64 "", outgoingMessage.messageId];
}
}
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
if (messageTransaction.completion) {
messageTransaction.completion(nil, nil, nil);
}
}
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p transport did not accept transactions with messages (%@)]", self, _context, idsString);
}
}
}];
} needsQuickAck:transactionNeedsQuickAck expectsDataInResponse:transactionExpectsDataInResponse]];
}
transactionReady(transportTransactions);
}
else
{
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
if (messageTransaction.completion)
messageTransaction.completion(nil, nil, nil);
}
transactionReady(nil);
}
}
else {
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
if (messageTransaction.completion) {
messageTransaction.completion(nil, nil, nil);
}
}
transactionReady(nil);
}
} else {
for (MTMessageTransaction *messageTransaction in messageTransactions)
{
if (messageTransaction.completion) {
messageTransaction.completion(nil, nil, nil);
}
}
}
}
else if ([self timeFixOrSaltsMissing] && [self canAskForServiceTransactions] && (_timeFixContext == nil || _timeFixContext.transactionId == nil))
{
int64_t timeFixMessageId = [_sessionInfo generateClientMessageId:NULL];
int32_t timeFixSeqNo = [_sessionInfo takeSeqNo:false];
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
MTBuffer *pingBuffer = [[MTBuffer alloc] init];
[pingBuffer appendInt32:(int32_t)0x7abe77ec];
[pingBuffer appendInt64:randomId];
NSData *messageData = pingBuffer.data;
MTOutputStream *decryptedOs = [[MTOutputStream alloc] init];
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId);
}
MTShortLog(@"[MTProto#%p@%p sending time fix ping (%" PRId64 "/%" PRId32 ", %" PRId64 ")]", self, _context, timeFixMessageId, timeFixSeqNo, _sessionInfo.sessionId);
[decryptedOs writeInt64:[_authInfo authSaltForMessageId:timeFixMessageId]]; // salt
[decryptedOs writeInt64:_sessionInfo.sessionId];
[decryptedOs writeInt64:timeFixMessageId];
[decryptedOs writeInt32:timeFixSeqNo];
[decryptedOs writeInt32:(int32_t)messageData.length];
[decryptedOs writeData:messageData];
NSData *decryptedData = [self paddedData:[decryptedOs currentBytes] extendedPadding:extendedPadding];
MTDatacenterAuthKey *effectiveAuthKey;
if (_useTempAuthKeys) {
MTDatacenterAuthTempKeyType tempAuthKeyType = MTDatacenterAuthTempKeyTypeMain;
if (scheme.address.preferForMedia) {
tempAuthKeyType = MTDatacenterAuthTempKeyTypeMedia;
}
effectiveAuthKey = [_authInfo tempAuthKeyWithType:tempAuthKeyType];
NSAssert(effectiveAuthKey != nil, @"effectiveAuthKey == nil");
} else {
effectiveAuthKey = [[MTDatacenterAuthKey alloc] initWithAuthKey:_authInfo.authKey authKeyId:_authInfo.authKeyId notBound:false];
}
int xValue = 0;
NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init];
[msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32];
[msgKeyLargeData appendData:decryptedData];
NSData *msgKeyLarge = MTSha256(msgKeyLargeData);
NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)];
MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:false];
NSData *transactionData = nil;
if (encryptionKey != nil)
{
NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length];
[encryptedData appendData:decryptedData];
MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv);
int64_t authKeyId = effectiveAuthKey.authKeyId;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8];
[encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length];
transactionData = encryptedData;
}
if (transactionReady != nil)
{
if (transactionData != nil)
{
__weak MTProto *weakSelf = self;
transactionReady(@[[[MTTransportTransaction alloc] initWithPayload:transactionData completion:^(bool success, id transactionId)
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (success)
{
if (transactionId != nil)
{
_timeFixContext = [[MTTimeFixContext alloc] initWithMessageId:timeFixMessageId messageSeqNo:timeFixSeqNo transactionId:transactionId timeFixAbsoluteStartTime:MTAbsoluteSystemTime()];
}
}
else
{
__strong MTProto *strongSelf = weakSelf;
[strongSelf requestTransportTransaction];
}
}];
} needsQuickAck:false expectsDataInResponse:true]]);
}
else
transactionReady(nil);
}
}
else if (![self timeFixOrSaltsMissing] && [self bindingTempAuthKey] && [self canAskForServiceTransactions] && (_bindingTempAuthKeyContext == nil || _bindingTempAuthKeyContext.transactionId == nil))
{
MTDatacenterAuthTempKeyType tempAuthKeyType = MTDatacenterAuthTempKeyTypeMain;
if (scheme.address.preferForMedia) {
tempAuthKeyType = MTDatacenterAuthTempKeyTypeMedia;
}
MTDatacenterAuthKey *effectiveTempAuthKey = [_authInfo tempAuthKeyWithType:tempAuthKeyType];
NSAssert(effectiveTempAuthKey != nil, @"effectiveAuthKey == nil");
int64_t bindingMessageId = [_sessionInfo generateClientMessageId:NULL];
int32_t bindingSeqNo = [_sessionInfo takeSeqNo:true];
int32_t expiresAt = (int32_t)([_context globalTime] + 60 * 60 * 32);
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
int64_t nonce = 0;
arc4random_buf(&nonce, 8);
MTBuffer *decryptedMessage = [[MTBuffer alloc] init];
//bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner;
[decryptedMessage appendInt32:(int32_t)0x75a3f765];
[decryptedMessage appendInt64:nonce];
[decryptedMessage appendInt64:effectiveTempAuthKey.authKeyId];
[decryptedMessage appendInt64:_authInfo.authKeyId];
[decryptedMessage appendInt64:_sessionInfo.sessionId];
[decryptedMessage appendInt32:expiresAt];
NSData *encryptedMessage = [self _manuallyEncryptedMessage:[decryptedMessage data] messageId:bindingMessageId authKey:_authInfo.persistentAuthKey];
MTBuffer *bindRequestData = [[MTBuffer alloc] init];
//auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
[bindRequestData appendInt32:(int32_t)0xcdd42a05];
[bindRequestData appendInt64:_authInfo.persistentAuthKey.authKeyId];
[bindRequestData appendInt64:nonce];
[bindRequestData appendInt32:expiresAt];
[bindRequestData appendTLBytes:encryptedMessage];
NSData *messageData = bindRequestData.data;
MTOutputStream *decryptedOs = [[MTOutputStream alloc] init];
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p sending temp key binding message (%" PRId64 "/%" PRId32 ") for %lld (%d)]", self, _context, bindingMessageId, bindingSeqNo, effectiveTempAuthKey.authKeyId, (int)tempAuthKeyType);
}
MTShortLog(@"[MTProto#%p@%p sending temp key binding message (%" PRId64 "/%" PRId32 ") for %lld (%d)]", self, _context, bindingMessageId, bindingSeqNo, effectiveTempAuthKey.authKeyId, (int)tempAuthKeyType);
[decryptedOs writeInt64:[_authInfo authSaltForMessageId:bindingMessageId]];
[decryptedOs writeInt64:_sessionInfo.sessionId];
[decryptedOs writeInt64:bindingMessageId];
[decryptedOs writeInt32:bindingSeqNo];
[decryptedOs writeInt32:(int32_t)messageData.length];
[decryptedOs writeData:messageData];
NSData *decryptedData = [self paddedData:[decryptedOs currentBytes] extendedPadding:extendedPadding];
int xValue = 0;
NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init];
[msgKeyLargeData appendBytes:effectiveTempAuthKey.authKey.bytes + 88 + xValue length:32];
[msgKeyLargeData appendData:decryptedData];
NSData *msgKeyLarge = MTSha256(msgKeyLargeData);
NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)];
MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveTempAuthKey.authKey messageKey:messageKey toClient:false];
NSData *transactionData = nil;
if (encryptionKey != nil)
{
NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length];
[encryptedData appendData:decryptedData];
MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv);
int64_t authKeyId = effectiveTempAuthKey.authKeyId;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8];
[encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length];
transactionData = encryptedData;
}
if (transactionReady != nil)
{
if (transactionData != nil)
{
__weak MTProto *weakSelf = self;
transactionReady(@[[[MTTransportTransaction alloc] initWithPayload:transactionData completion:^(bool success, id transactionId) {
[[MTProto managerQueue] dispatchOnQueue:^{
if (success) {
if (transactionId != nil) {
_bindingTempAuthKeyContext = [[MTBindingTempAuthKeyContext alloc] initWithMessageId:bindingMessageId messageSeqNo:bindingSeqNo transactionId:transactionId];
}
}
else
{
__strong MTProto *strongSelf = weakSelf;
[strongSelf requestTransportTransaction];
}
}];
} needsQuickAck:false expectsDataInResponse:true]]);
}
else
transactionReady(nil);
}
}
else if (transactionReady != nil)
transactionReady(nil);
/*if (debugResetTransport) {
[self resetTransport];
[self requestTransportTransaction];
}*/
}];
}
Data for Encrypted Messages Container
Wrap for Message Container
- (NSData *)_dataForEncryptedContainerWithMessages:(NSArray *)preparedMessages sessionInfo:(MTSessionInfo *)sessionInfo quickAckId:(int32_t *)quickAckId address:(MTDatacenterAddress *)address extendedPadding:(bool)extendedPadding {
MTDatacenterAuthKey *effectiveAuthKey;
if (_useTempAuthKeys) {
MTDatacenterAuthTempKeyType tempAuthKeyType = MTDatacenterAuthTempKeyTypeMain;
if (address.preferForMedia) {
tempAuthKeyType = MTDatacenterAuthTempKeyTypeMedia;
}
effectiveAuthKey = [_authInfo tempAuthKeyWithType:tempAuthKeyType];
NSAssert(effectiveAuthKey != nil, @"effectiveAuthKey == nil");
} else {
effectiveAuthKey = [[MTDatacenterAuthKey alloc] initWithAuthKey:_authInfo.authKey authKeyId:_authInfo.authKeyId notBound:false];
}
NSMutableArray *containerMessageIds = [[NSMutableArray alloc] init];
MTOutputStream *containerOs = [[MTOutputStream alloc] init];
[containerOs writeInt32:0x73f1f8dc]; // msg_container
[containerOs writeInt32:(int32_t)preparedMessages.count];
int64_t salt = 0;
for (MTPreparedMessage *preparedMessage in preparedMessages) {
salt = preparedMessage.salt;
[containerOs writeInt64:preparedMessage.messageId];
[containerOs writeInt32:preparedMessage.seqNo];
[containerOs writeInt32:(int32_t)preparedMessage.data.length];
[containerOs writeData:preparedMessage.data];
if (preparedMessage.requiresConfirmation)
[containerMessageIds addObject:@(preparedMessage.messageId)];
}
NSData *containerData = [containerOs currentBytes];
MTOutputStream *decryptedOs = [[MTOutputStream alloc] init];
int64_t containerMessageId = [sessionInfo generateClientMessageId:NULL];
if (containerMessageIds.count != 0) {
[sessionInfo addContainerMessageIdMapping:containerMessageId childMessageIds:containerMessageIds];
NSMutableString *idsString = [[NSMutableString alloc] init];
for (NSNumber *nMessageId in containerMessageIds) {
if (idsString.length != 0)
[idsString appendString:@","];
[idsString appendFormat:@"%lld", [nMessageId longLongValue]];
}
if (MTLogEnabled()) {
MTLog(@" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId);
}
MTShortLog(@" container (%" PRId64 ") of (%@), in %" PRId64 "", containerMessageId, idsString, sessionInfo.sessionId);
}
[decryptedOs writeInt64:salt];
[decryptedOs writeInt64:sessionInfo.sessionId];
[decryptedOs writeInt64:containerMessageId];
[decryptedOs writeInt32:[sessionInfo takeSeqNo:false]];
[decryptedOs writeInt32:(int32_t)containerData.length];
[decryptedOs writeData:containerData];
NSData *decryptedData = [self paddedData:[decryptedOs currentBytes] extendedPadding:extendedPadding];
int xValue = 0;
NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init];
[msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32];
[msgKeyLargeData appendData:decryptedData];
NSData *msgKeyLarge = MTSha256(msgKeyLargeData);
NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)];
MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:false];
int32_t nQuickAckId = *((int32_t *)(msgKeyLarge.bytes));
nQuickAckId = nQuickAckId & 0x7fffffff;
if (quickAckId != NULL)
*quickAckId = nQuickAckId;
if (encryptionKey != nil)
{
NSMutableData *encryptedData = [[NSMutableData alloc] init];
[encryptedData appendData:decryptedData];
MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv);
int64_t authKeyId = effectiveAuthKey.authKeyId;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8];
[encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length];
return encryptedData;
}
return nil;
}
Data for Plain Message
Wrap Plain Message
- (NSData *)_dataForPlainMessage:(MTPreparedMessage *)preparedMessage extendedPadding:(bool)extendedPadding
{
MTOutputStream *os = [[MTOutputStream alloc] init];
[os writeInt64:0];
[os writeInt64:preparedMessage.messageId];
[os writeInt32:(int32_t)preparedMessage.data.length];
[os writeData:preparedMessage.data];
uint32_t paddingSize = 0;
if (extendedPadding) {
paddingSize = arc4random_uniform((256 - 16) / 4) * 4;
}
uint8_t padding[256];
if (paddingSize > 0) {
arc4random_buf(padding, paddingSize);
[os write:padding maxLength:paddingSize];
}
NSData *messageData = [os currentBytes];
return messageData;
}
PaddedDataV1
- (NSData *)paddedDataV1:(NSData *)data {
NSMutableData *padded = [[NSMutableData alloc] initWithData:data];
uint8_t randomBytes[128];
arc4random_buf(randomBytes, 128);
// append randombytes until data.length % 16 == 0
for (int i = 0; ((int)data.length + i) % 16 != 0; i++) {
[padded appendBytes:randomBytes + i length:1];
}
return padded;
}
Padded Data
- (NSData *)paddedData:(NSData *)data extendedPadding:(bool)extendedPadding {
NSMutableData *padded = [[NSMutableData alloc] initWithData:data];
uint8_t randomBytes[256];
arc4random_buf(randomBytes, 256);
int take = 0;
// append 12 random bytes
while (take < 12) {
[padded appendBytes:randomBytes + take length:1];
take++;
}
// until padded.length % 16 == 0
while (padded.length % 16 != 0) {
[padded appendBytes:randomBytes + take length:1];
take++;
}
uint32_t extraPaddingSize = 72;
if (extendedPadding) {
extraPaddingSize = 256;
}
int remainingCount = arc4random_uniform(extraPaddingSize + 1 - take);
// find remaining count stisfy remainingCount % 16 == 0
while (remainingCount % 16 != 0) {
remainingCount--;
}
// append remainingcount bytes
for (int i = 0; i < remainingCount; i++) {
[padded appendBytes:randomBytes + take length:1];
take++;
}
assert(padded.length % 16 == 0);
return padded;
}
ManuallyEncryptedData
- (NSData *)_manuallyEncryptedMessage:(NSData *)preparedData messageId:(int64_t)messageId authKey:(MTDatacenterAuthKey *)authKey {
MTOutputStream *decryptedOs = [[MTOutputStream alloc] init];
int64_t random1 = 0;
int64_t random2 = 0;
arc4random_buf(&random1, 8);
arc4random_buf(&random2, 8);
[decryptedOs writeInt64:random1];
[decryptedOs writeInt64:random2];
[decryptedOs writeInt64:messageId];
[decryptedOs writeInt32:0];
[decryptedOs writeInt32:(int32_t)preparedData.length];
[decryptedOs writeData:preparedData];
// decryptedData = f(random1, random2, messageid, preparedData)
NSData *decryptedData = [self paddedDataV1:[decryptedOs currentBytes]];
// message key full = sha1 of decrypted data
NSData *messageKeyFull = MTSubdataSha1(decryptedData, 0, 32 + preparedData.length);
// message key = f(message key full)
NSData *messageKey = [[NSData alloc] initWithBytes:(((int8_t *)messageKeyFull.bytes) + messageKeyFull.length - 16) length:16];
// encryption key = f(authkey.authkey, messagekey)
MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyForAuthKey:authKey.authKey messageKey:messageKey toClient:false];
if (encryptionKey != nil)
{
// encryptedData = f(descrypteddata, encryptionkey.key, encryptionkey.iv, authkey.id, messagekey)
NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length];
[encryptedData appendData:decryptedData];
MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv);
int64_t authKeyId = authKey.authKeyId;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8];
[encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length];
return encryptedData;
}
else
return nil;
}
Data for Encrypted Message
Message Encryption
- (NSData *)_dataForEncryptedMessage:(MTPreparedMessage *)preparedMessage sessionInfo:(MTSessionInfo *)sessionInfo quickAckId:(int32_t *)quickAckId address:(MTDatacenterAddress *)address extendedPadding:(bool)extendedPadding
{
// prepare auth key
MTDatacenterAuthKey *effectiveAuthKey;
if (_useTempAuthKeys) {
MTDatacenterAuthTempKeyType tempAuthKeyType = MTDatacenterAuthTempKeyTypeMain;
if (address.preferForMedia) {
tempAuthKeyType = MTDatacenterAuthTempKeyTypeMedia;
}
// pick auth key from auth info
effectiveAuthKey = [_authInfo tempAuthKeyWithType:tempAuthKeyType];
NSAssert(effectiveAuthKey != nil, @"effectiveAuthKey == nil");
} else {
// generate auth key
effectiveAuthKey = [[MTDatacenterAuthKey alloc] initWithAuthKey:_authInfo.authKey authKeyId:_authInfo.authKeyId notBound:false];
}
MTOutputStream *decryptedOs = [[MTOutputStream alloc] init];
[decryptedOs writeInt64:preparedMessage.salt];
[decryptedOs writeInt64:sessionInfo.sessionId];
[decryptedOs writeInt64:preparedMessage.messageId];
[decryptedOs writeInt32:preparedMessage.seqNo];
[decryptedOs writeInt32:(int32_t)preparedMessage.data.length];
[decryptedOs writeData:preparedMessage.data];
// decypted data = f(message.salt, sessionId, message.id, message.seqNo)
NSData *decryptedData = [self paddedData:[decryptedOs currentBytes] extendedPadding:extendedPadding];
// msgKeyLargeData = f(decrypteddata, authkey)
int xValue = 0;
NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init];
[msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32];
[msgKeyLargeData appendData:decryptedData];
// sha256 of msgKeyLargeData
NSData *msgKeyLarge = MTSha256(msgKeyLargeData);
// message key = f(sha256(msgKeyLargeData))
NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)];
// encryption key = f(authkey, messagekey)
MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:false];
if (encryptionKey != nil)
{
NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length];
// encrypted data = f(encrypted data, encrptionkey.key, encryptionkey.iv, authkey.id, messagekey.bytes)
[encryptedData appendData:decryptedData];
MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv);
int64_t authKeyId = effectiveAuthKey.authKeyId;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&authKeyId length:8];
[encryptedData replaceBytesInRange:NSMakeRange(8, 0) withBytes:messageKey.bytes length:messageKey.length];
return encryptedData;
}
else
return nil;
}
Transport Transaction May Have Failed
- (void)transportTransactionsMayHaveFailed:(MTTransport *)__unused transport transactionIds:(NSArray *)transactionIds
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if ([self isStopped])
return;
bool requestTransaction = false;
// check time fix context for request transaction
if (_timeFixContext != nil && _timeFixContext.transactionId != nil && [transactionIds containsObject:_timeFixContext.transactionId])
{
_timeFixContext = nil;
requestTransaction = true;
}
// check bingind temp auth key context for request transaction
if (_bindingTempAuthKeyContext != nil && _bindingTempAuthKeyContext.transactionId != nil && [transactionIds containsObject:_bindingTempAuthKeyContext.transactionId])
{
_bindingTempAuthKeyContext = nil;
requestTransaction = true;
}
for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--)
{
id<MTMessageService> messageService = _messageServices[(NSUInteger)i];
// notify to message service that all all transaction may have failed
if ([messageService respondsToSelector:@selector(mtProto:transactionsMayHaveFailed:)])
[messageService mtProto:self transactionsMayHaveFailed:transactionIds];
}
if (requestTransaction && ![self isPaused])
[self requestTransportTransaction];
}];
}
All Transactions May Have Failed
- (void)allTransactionsMayHaveFailed
{
if ([self isStopped])
return;
bool requestTransaction = false;
// check time fix context for request transaction
if (_timeFixContext != nil && _timeFixContext.transactionId != nil)
{
_timeFixContext = nil;
requestTransaction = true;
}
// check bingind temp auth key context for request transaction
if (_bindingTempAuthKeyContext != nil && _bindingTempAuthKeyContext.transactionId != nil)
{
_bindingTempAuthKeyContext = nil;
requestTransaction = true;
}
for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--)
{
id<MTMessageService> messageService = _messageServices[(NSUInteger)i];
// notify to message service that all all transaction may have failed
if ([messageService respondsToSelector:@selector(mtProtoAllTransactionsMayHaveFailed:)])
[messageService mtProtoAllTransactionsMayHaveFailed:self];
}
if (requestTransaction && ![self isPaused])
[self requestTransportTransaction];
}
Transport Received Quick Ack
- (void)transportReceivedQuickAck:(MTTransport *)transport quickAckId:(int32_t)quickAckId
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (_transport != transport || [self isStopped])
return;
for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--)
{
id<MTMessageService> messageService = _messageServices[(NSUInteger)i];
// notify to delegate
if ([messageService respondsToSelector:@selector(mtProto:receivedQuickAck:)])
[messageService mtProto:self receivedQuickAck:quickAckId];
}
}];
}
Transport Decode Progress Token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
| - (void)transportDecodeProgressToken:(MTTransport *)transport scheme:(MTTransportScheme *)scheme data:(NSData *)data token:(int64_t)token completion:(void (^)(int64_t token, id progressToken))completion
{
[[MTProto managerQueue] dispatchOnQueue:^
{
if (transport != _transport || completion == nil)
return;
MTDatacenterAuthKey *effectiveAuthKey;
if (_useTempAuthKeys) {
MTDatacenterAuthTempKeyType tempAuthKeyType = MTDatacenterAuthTempKeyTypeMain;
if (scheme.address.preferForMedia) {
tempAuthKeyType = MTDatacenterAuthTempKeyTypeMedia;
}
effectiveAuthKey = [_authInfo tempAuthKeyWithType:tempAuthKeyType];
NSAssert(effectiveAuthKey != nil, @"effectiveAuthKey == nil");
} else {
effectiveAuthKey = [[MTDatacenterAuthKey alloc] initWithAuthKey:_authInfo.authKey authKeyId:_authInfo.authKeyId notBound:false];
}
MTInputStream *is = [[MTInputStream alloc] initWithData:data];
int64_t keyId = [is readInt64];
if (keyId != 0 && _authInfo != nil)
{
NSData *messageKey = [is readData:16];
MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyV2ForAuthKey:effectiveAuthKey.authKey messageKey:messageKey toClient:true];
NSMutableData *encryptedMessageData = [is readMutableData:(data.length - 24)];
while (encryptedMessageData.length % 16 != 0) {
[encryptedMessageData setLength:encryptedMessageData.length - 1];
}
if (encryptedMessageData.length != 0)
{
NSData *decryptedData = MTAesDecrypt(encryptedMessageData, encryptionKey.key, encryptionKey.iv);
MTInputStream *messageIs = [[MTInputStream alloc] initWithData:decryptedData];
[messageIs readInt64];
[messageIs readInt64];
[messageIs readInt64];
[messageIs readInt32];
[messageIs readInt32];
bool stop = false;
int64_t reqMsgId = 0;
/*if (true)
{*/
while (!stop && reqMsgId == 0)
{
int32_t signature = [messageIs readInt32:&stop];
[self findReqMsgId:messageIs signature:signature reqMsgId:&reqMsgId failed:&stop];
}
/*}
else
{
int32_t signature = [messageIs readInt32];
if (signature == (int)0xf35c6d01)
reqMsgId = [messageIs readInt64];
else if (signature == (int)0x73f1f8dc)
{
int count = [messageIs readInt32];
if (count != 0)
{
[messageIs readInt64];
[messageIs readInt32];
[messageIs readInt32];
signature = [messageIs readInt32];
if (signature == (int)0xf35c6d01)
reqMsgId = [messageIs readInt64];
}
}
}*/
if (reqMsgId != 0)
completion(token, @(reqMsgId));
}
}
}];
}
|