/* * This is the source code of Telegram for iOS v. 1.1 * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Peter Iakovlev, 2013. */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import typedef enum { MTProtoStateAwaitingDatacenterScheme = 1, MTProtoStateAwaitingDatacenterAuthorization = 2, MTProtoStateAwaitingDatacenterAuthToken = 4, MTProtoStateAwaitingTimeFixAndSalts = 8, MTProtoStateAwaitingLostMessages = 16, MTProtoStateStopped = 32, MTProtoStatePaused = 64 } MTProtoState; static const NSUInteger MTMaxContainerSize = 3 * 1024; static const NSUInteger MTMaxUnacknowledgedMessageSize = 1 * 1024 * 1024; static const NSUInteger MTMaxUnacknowledgedMessageCount = 64; @interface MTProto () { NSMutableArray *_messageServices; MTDatacenterAuthInfo *_authInfo; MTSessionInfo *_sessionInfo; MTTimeFixContext *_timeFixContext; MTTransportScheme *_transportScheme; MTTransport *_transport; int _mtState; bool _willRequestTransactionOnNextQueuePass; } @end @implementation MTProto + (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 { #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; [_context addChangeListener:self]; _messageServices = [[NSMutableArray alloc] init]; _sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:_context]; _authInfo = [_context authInfoForDatacenterWithId:_datacenterId]; _shouldStayConnected = true; } return self; } - (void)dealloc { MTTransport *transport = _transport; _transport.delegate = nil; _transport = nil; [[MTProto managerQueue] dispatchOnQueue:^ { [transport stop]; }]; } - (void)pause { [[MTProto managerQueue] dispatchOnQueue:^ { if ((_mtState & MTProtoStatePaused) == 0) { MTLog(@"[MTProto#%p pause]", self); _mtState |= MTProtoStatePaused; [self setMtState:_mtState | MTProtoStatePaused]; [self setTransport:nil]; } }]; } - (void)resume { [[MTProto managerQueue] dispatchOnQueue:^ { if (_mtState & MTProtoStatePaused) { MTLog(@"[MTProto#%p resume]", self); [self setMtState:_mtState & (~MTProtoStatePaused)]; [self resetTransport]; [self requestTransportTransaction]; } }]; } - (void)stop { [[MTProto managerQueue] dispatchOnQueue:^ { if ((_mtState & MTProtoStateStopped) == 0) { [self setMtState:_mtState | MTProtoStateStopped]; if (_transport != nil) { _transport.delegate = nil; [_transport stop]; [self setTransport:nil]; } } }]; } - (void)updateConnectionState { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != nil) [_transport updateConnectionState]; else { id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)]) [delegate mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:false]; if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:isConnected:)]) [delegate mtProtoConnectionStateChanged:self isConnected:false]; if ([delegate respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)]) [delegate mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:false]; } }]; } - (void)setTransport:(MTTransport *)transport { [[MTProto managerQueue] dispatchOnQueue:^ { MTLog(@"[MTProto#%p changing transport %@#%p to %@#%p]", self, [_transport class] == nil ? @"" : NSStringFromClass([_transport class]), _transport, [transport class] == nil ? @"" : NSStringFromClass([transport class]), transport); [self allTransactionsMayHaveFailed]; MTTransport *previousTransport = _transport; [_transport activeTransactionIds:^(NSArray *transactionIds) { [self transportTransactionsMayHaveFailed:previousTransport transactionIds:transactionIds]; }]; _timeFixContext = nil; if (_transport != nil) [self removeMessageService:_transport]; _transport = transport; [previousTransport stop]; if (_transport != nil) [self addMessageService:_transport]; [self updateConnectionState]; }]; } - (void)resetTransport { [[MTProto managerQueue] dispatchOnQueue:^ { if (_mtState & MTProtoStateStopped) return; if (_transport != nil) { _transport.delegate = nil; [_transport stop]; [self setTransport:nil]; } _transportScheme = [_context transportSchemeForDatacenterWithid:_datacenterId]; if (_transportScheme == nil) { if ((_mtState & MTProtoStateAwaitingDatacenterScheme) == 0) { [self setMtState:_mtState | MTProtoStateAwaitingDatacenterScheme]; [_context transportSchemeForDatacenterWithIdRequired:_datacenterId]; } } else if (!_useUnauthorizedMode && [_context authInfoForDatacenterWithId:_datacenterId] == nil) { if ((_mtState & MTProtoStateAwaitingDatacenterAuthorization) == 0) { [self setMtState:_mtState | MTProtoStateAwaitingDatacenterAuthorization]; [_context authInfoForDatacenterWithIdRequired:_datacenterId]; } } else if (_requiredAuthToken != nil && !_useUnauthorizedMode && ![_requiredAuthToken isEqual:[_context authTokenForDatacenterWithId:_datacenterId]]) { if ((_mtState & MTProtoStateAwaitingDatacenterAuthToken) == 0) { [self setMtState:_mtState | MTProtoStateAwaitingDatacenterAuthToken]; [_context authTokenForDatacenterWithIdRequired:_datacenterId authToken:_requiredAuthToken masterDatacenterId:_authTokenMasterDatacenterId]; } } else { MTTransport *transport = nil; transport = [_transportScheme createTransportWithContext:_context datacenterId:_datacenterId delegate:self]; [self setTransport:transport]; } }]; } - (void)resetSessionInfo { [[MTProto managerQueue] dispatchOnQueue:^ { MTLog(@"[MTProto#%p resetting session]", self); _sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:_context]; _timeFixContext = nil; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProtoDidChangeSession:)]) [messageService mtProtoDidChangeSession:self]; } [self resetTransport]; [self requestTransportTransaction]; }]; } - (void)requestTimeResync { [[MTProto managerQueue] dispatchOnQueue:^ { bool alreadySyncing = false; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService isKindOfClass:[MTTimeSyncMessageService class]]) { alreadySyncing = true; break; } } if (!alreadySyncing) { MTLog(@"[MTProto#%p begin time sync]", self); MTTimeSyncMessageService *timeSyncService = [[MTTimeSyncMessageService alloc] init]; timeSyncService.delegate = self; [self addMessageService:timeSyncService]; } }]; } - (void)setMtState:(int)mtState { bool wasPerformingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts; _mtState = mtState; bool performingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts; if (performingServiceTasks != wasPerformingServiceTasks) { bool haveResendMessagesPending = false; for (id messageService in _messageServices) { if ([messageService isKindOfClass:[MTResendMessageService class]]) { haveResendMessagesPending = true; break; } } MTLog(@"[MTProto#%p service tasks state: %d, resend: %s]", self, _mtState, haveResendMessagesPending ? "yes" : "no"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:performingServiceTasks || haveResendMessagesPending]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:performingServiceTasks || haveResendMessagesPending]; } } - (void)addMessageService:(id)messageService { if ([messageService respondsToSelector:@selector(mtProtoWillAddService:)]) [messageService mtProtoWillAddService:self]; [[MTProto managerQueue] dispatchOnQueue:^ { bool notifyAboutServiceTask = false; if ([messageService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = true; for (id currentService in _messageServices) { if ([currentService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = false; break; } } } if (![_messageServices containsObject:messageService]) { [_messageServices addObject:messageService]; if ([messageService respondsToSelector:@selector(mtProtoDidAddService:)]) [messageService mtProtoDidAddService:self]; } if (notifyAboutServiceTask) { MTLog(@"[MTProto#%p service tasks state: %d, resend: %s]", self, _mtState, true ? "yes" : "no"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:true]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:true]; } }]; } - (void)removeMessageService:(id)messageService { if (messageService == nil) return; [[MTProto managerQueue] dispatchOnQueue:^ { if ([_messageServices containsObject:messageService]) { [_messageServices removeObject:messageService]; if ([messageService respondsToSelector:@selector(mtProtoDidRemoveService:)]) [messageService mtProtoDidRemoveService:self]; bool notifyAboutServiceTask = false; if ([messageService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = true; for (id currentService in _messageServices) { if ([currentService isKindOfClass:[MTResendMessageService class]]) { notifyAboutServiceTask = false; break; } } } if (notifyAboutServiceTask) { bool performingServiceTasks = _mtState & MTProtoStateAwaitingTimeFixAndSalts; if (!performingServiceTasks) { MTLog(@"[MTProto#%p service tasks state: %d, resend: %s]", self, _mtState, false ? "yes" : "no"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [messageService mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:false]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoServiceTasksStateChanged:isPerformingServiceTasks:)]) [delegate mtProtoServiceTasksStateChanged:self isPerformingServiceTasks:false]; } } } }]; } - (MTQueue *)messageServiceQueue { return [MTProto managerQueue]; } - (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 messageService = _messageServices[(NSUInteger)i]; if ([messageService isKindOfClass:[MTTimeSyncMessageService class]]) { ((MTTimeSyncMessageService *)messageService).delegate = nil; [self removeMessageService:messageService]; } } } }]; } - (void)requestMessageWithId:(int64_t)messageId { bool alreadyRequestingThisMessage = false; for (id messageService in _messageServices) { if ([messageService isKindOfClass:[MTResendMessageService class]]) { if (((MTResendMessageService *)messageService).messageId == messageId) { alreadyRequestingThisMessage = true; break; } } } if (!alreadyRequestingThisMessage && ![_sessionInfo messageProcessed:messageId]) { MTResendMessageService *resendService = [[MTResendMessageService alloc] initWithMessageId:messageId]; resendService.delegate = self; [self addMessageService:resendService]; } } - (void)resendMessageServiceCompleted:(MTResendMessageService *)resendService { [[MTProto managerQueue] dispatchOnQueue:^ { resendService.delegate = nil; [self removeMessageService:resendService]; }]; } - (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) [self resetTransport]; [_transport setDelegateNeedsTransaction]; }); } }]; } - (void)requestSecureTransportReset { [[MTProto managerQueue] dispatchOnQueue:^ { if ([self isStopped]) return; if (_transport != nil) [_transport reset]; }]; } - (bool)canAskForTransactions { return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateAwaitingTimeFixAndSalts | MTProtoStateStopped)) == 0; } - (bool)canAskForServiceTransactions { return (_mtState & (MTProtoStateAwaitingDatacenterScheme | MTProtoStateAwaitingDatacenterAuthorization | MTProtoStateAwaitingDatacenterAuthToken | MTProtoStateStopped)) == 0; } - (bool)timeFixOrSaltsMissing { return _mtState & MTProtoStateAwaitingTimeFixAndSalts; } - (bool)isStopped { return (_mtState & MTProtoStateStopped) != 0; } - (bool)isPaused { return (_mtState & MTProtoStatePaused) != 0; } - (void)transportNetworkAvailabilityChanged:(MTTransport *)transport isNetworkAvailable:(bool)isNetworkAvailable { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; MTLog(@"[MTProto#%p network state: %s]", self, isNetworkAvailable ? "available" : "waiting"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)]) [messageService mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoNetworkAvailabilityChanged:isNetworkAvailable:)]) [delegate mtProtoNetworkAvailabilityChanged:self isNetworkAvailable:isNetworkAvailable]; }]; } - (void)transportConnectionStateChanged:(MTTransport *)transport isConnected:(bool)isConnected { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; MTLog(@"[MTProto#%p connection state: %s]", self, isConnected ? "connected" : "connecting"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoConnectionStateChanged:isConnected:)]) [messageService mtProtoConnectionStateChanged:self isConnected:isConnected]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoConnectionStateChanged:isConnected:)]) [delegate mtProtoConnectionStateChanged:self isConnected:isConnected]; }]; } - (void)transportConnectionContextUpdateStateChanged:(MTTransport *)transport isUpdatingConnectionContext:(bool)isUpdatingConnectionContext { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; MTLog(@"[MTProto#%p connection context update state: %s]", self, isUpdatingConnectionContext ? "updating" : "up to date"); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)]) [messageService mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:isUpdatingConnectionContext]; } id delegate = _delegate; if ([delegate respondsToSelector:@selector(mtProtoConnectionContextUpdateStateChanged:isUpdatingConnectionContext:)]) [delegate mtProtoConnectionContextUpdateStateChanged:self isUpdatingConnectionContext:isUpdatingConnectionContext]; }]; } - (void)transportConnectionProblemsStatusChanged:(MTTransport *)transport hasConnectionProblems:(bool)hasConnectionProblems isProbablyHttp:(bool)isProbablyHttp { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != transport || _transportScheme == nil) return; if (hasConnectionProblems) [_context invalidateTransportSchemeForDatacenterId:_datacenterId transportScheme:_transportScheme isProbablyHttp:isProbablyHttp]; else [_context revalidateTransportSchemeForDatacenterId:_datacenterId transportScheme:_transportScheme]; }]; } - (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]; } - (NSString *)incomingMessageDescription:(MTIncomingMessage *)message { return [[NSString alloc] initWithFormat:@"%@ (%" PRId64")", message.body, message.messageId]; } - (void)transportReadyForTransaction:(MTTransport *)transport transportSpecificTransaction:(MTMessageTransaction *)transportSpecificTransaction forceConfirmations:(bool)forceConfirmations transactionReady:(void (^)(NSArray *))transactionReady { [[MTProto managerQueue] dispatchOnQueue:^ { if (_authInfo.authKey != nil && !_sessionInfo.scheduledForCleanup) { _sessionInfo.scheduledForCleanup = true; [_context scheduleSessionCleanupForAuthKeyId:_authInfo.authKeyId sessionInfo:_sessionInfo]; } if (_transport != transport) { if (transactionReady) transactionReady(nil); } else if ([self canAskForTransactions]) { MTSessionInfo *transactionSessionInfo = _sessionInfo; NSMutableArray *messageTransactions = [[NSMutableArray alloc] init]; if (transportSpecificTransaction != nil) { if (!(transportSpecificTransaction.requiresEncryption && _useUnauthorizedMode) && (!transportSpecificTransaction.requiresEncryption || _authInfo != nil)) { [messageTransactions addObject:transportSpecificTransaction]; } } bool anyTransactionHasHighPriorityMessages = false; NSMutableArray *messageServiceTransactions = [[NSMutableArray alloc] init]; for (id 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]; } } } 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"]; outgoingMessage.requiresConfirmation = false; [messageTransactions addObject:[[MTMessageTransaction alloc] initWithMessagePayload:@[outgoingMessage] 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) { NSData *messageData = outgoingMessage.data; if (outgoingMessage.dynamicDecorator != nil) { id decoratedData = outgoingMessage.dynamicDecorator(messageData, messageInternalIdToPreparedMessage); if (decoratedData != nil) messageData = decoratedData; } NSData *data = messageData; int64_t messageId = 0; int32_t messageSeqNo = 0; if (outgoingMessage.messageId == 0) { messageId = [transactionSessionInfo generateClientMessageId:&monotonityViolated]; messageSeqNo = [transactionSessionInfo takeSeqNo:outgoingMessage.requiresConfirmation]; } else { messageId = outgoingMessage.messageId; messageSeqNo = outgoingMessage.messageSeqNo; } MTLog(@"[MTProto#%p preparing %@]", self, [self outgoingMessageDescription:outgoingMessage messageId:messageId messageSeqNo:messageSeqNo]); if (!monotonityViolated || _useUnauthorizedMode) { 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; } if (monotonityViolated || saltSetEmpty) { for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) messageTransaction.completion(nil, nil, nil); } if (transactionReady != nil) transactionReady(nil); if (monotonityViolated) { MTLog(@"[MTProto#%p client message id monotonity violated]", self); [self resetSessionInfo]; } else if (saltSetEmpty) [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; 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]; 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]; 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]; i++; if (messageData != nil) { [transactionPayloadList addObject:messageData]; [messageInternalIdsByPayload addObject:@[preparedMessageInternalIdToMessageInternalId[preparedMessage.internalId]]]; [quickAckIdsByPayload addObject:@(0)]; } } } 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]; } } MTLog(@"[MTProto#%p transport did not accept transactions with messages (%@)]", self, idsString); } }]; } needsQuickAck:transactionNeedsQuickAck expectsDataInResponse:transactionExpectsDataInResponse]]; } transactionReady(transportTransactions); } else { for (MTMessageTransaction *messageTransaction in messageTransactions) { if (messageTransaction.completion) messageTransaction.completion(nil, nil, nil); } transactionReady(nil); } } else transactionReady(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]; MTLog(@"[MTProto#%x sending time fix ping (%" PRId64 "/%" PRId32 ")]", self, timeFixMessageId, timeFixSeqNo); [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]; uint8_t randomBytes[15]; arc4random_buf(randomBytes, 15); for (int i = 0; ((int)messageData.length + i) % 16 != 0; i++) { [decryptedOs write:&randomBytes[i] maxLength:1]; } NSData *decryptedData = [decryptedOs currentBytes]; NSData *messageKeyFull = MTSubdataSha1(decryptedData, 0, 32 + messageData.length); NSData *messageKey = [[NSData alloc] initWithBytes:(((int8_t *)messageKeyFull.bytes) + messageKeyFull.length - 16) length:16]; NSData *transactionData = nil; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyForAuthKey:_authInfo.authKey messageKey:messageKey toClient:false]; if (encryptionKey != nil) { NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length]; [encryptedData appendData:decryptedData]; MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv); int64_t authKeyId = _authInfo.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]; } }]; }]]); } else transactionReady(nil); } } else if (transactionReady != nil) transactionReady(nil); }]; } - (NSData *)_dataForEncryptedContainerWithMessages:(NSArray *)preparedMessages sessionInfo:(MTSessionInfo *)sessionInfo quickAckId:(int32_t *)quickAckId { 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]; #ifdef DEBUG NSMutableString *idsString = [[NSMutableString alloc] init]; for (NSNumber *nMessageId in containerMessageIds) { if (idsString.length != 0) [idsString appendString:@","]; [idsString appendFormat:@"%lld", [nMessageId longLongValue]]; } MTLog(@" container (%" PRId64 ") of (%@)", containerMessageId, idsString); #endif } [decryptedOs writeInt64:salt]; [decryptedOs writeInt64:sessionInfo.sessionId]; [decryptedOs writeInt64:containerMessageId]; [decryptedOs writeInt32:[sessionInfo takeSeqNo:false]]; [decryptedOs writeInt32:(int32_t)containerData.length]; [decryptedOs writeData:containerData]; uint8_t randomBytes[15]; arc4random_buf(randomBytes, 15); for (int i = 0; ((int)containerData.length + i) % 16 != 0; i++) { [decryptedOs write:&randomBytes[i] maxLength:1]; } NSData *decryptedData = [decryptedOs currentBytes]; NSData *messageKeyFull = MTSubdataSha1(decryptedData, 0, 32 + containerData.length); NSData *messageKey = [[NSData alloc] initWithBytes:(((int8_t *)messageKeyFull.bytes) + messageKeyFull.length - 16) length:16]; int32_t nQuickAckId = *((int32_t *)(messageKeyFull.bytes)); nQuickAckId = nQuickAckId & 0x7fffffff; if (quickAckId != NULL) *quickAckId = nQuickAckId; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyForAuthKey:_authInfo.authKey messageKey:messageKey toClient:false]; if (encryptionKey != nil) { NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length]; [encryptedData appendData:decryptedData]; MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv); int64_t authKeyId = _authInfo.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; } - (NSData *)_dataForPlainMessage:(MTPreparedMessage *)preparedMessage { MTOutputStream *os = [[MTOutputStream alloc] init]; [os writeInt64:0]; [os writeInt64:preparedMessage.messageId]; [os writeInt32:(int32_t)preparedMessage.data.length]; [os writeData:preparedMessage.data]; NSData *messageData = [os currentBytes]; return messageData; } - (NSData *)_dataForEncryptedMessage:(MTPreparedMessage *)preparedMessage sessionInfo:(MTSessionInfo *)sessionInfo quickAckId:(int32_t *)quickAckId { 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]; uint8_t randomBytes[15]; arc4random_buf(randomBytes, 15); for (int i = 0; ((int)preparedMessage.data.length + i) % 16 != 0; i++) { [decryptedOs write:&randomBytes[i] maxLength:1]; } NSData *decryptedData = [decryptedOs currentBytes]; NSData *messageKeyFull = MTSubdataSha1(decryptedData, 0, 32 + preparedMessage.data.length); NSData *messageKey = [[NSData alloc] initWithBytes:(((int8_t *)messageKeyFull.bytes) + messageKeyFull.length - 16) length:16]; int32_t nQuickAckId = *((int32_t *)(messageKeyFull.bytes)); nQuickAckId = nQuickAckId & 0x7fffffff; if (quickAckId != NULL) *quickAckId = nQuickAckId; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyForAuthKey:_authInfo.authKey messageKey:messageKey toClient:false]; if (encryptionKey != nil) { NSMutableData *encryptedData = [[NSMutableData alloc] initWithCapacity:14 + decryptedData.length]; [encryptedData appendData:decryptedData]; MTAesEncryptInplace(encryptedData, encryptionKey.key, encryptionKey.iv); int64_t authKeyId = _authInfo.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; } - (void)transportTransactionsMayHaveFailed:(MTTransport *)__unused transport transactionIds:(NSArray *)transactionIds { [[MTProto managerQueue] dispatchOnQueue:^ { if ([self isStopped]) return; bool requestTransaction = false; if (_timeFixContext != nil && _timeFixContext.transactionId != nil && [transactionIds containsObject:_timeFixContext.transactionId]) { _timeFixContext = nil; requestTransaction = true; } for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:transactionsMayHaveFailed:)]) [messageService mtProto:self transactionsMayHaveFailed:transactionIds]; } if (requestTransaction && ![self isPaused]) [self requestTransportTransaction]; }]; } - (void)allTransactionsMayHaveFailed { if ([self isStopped]) return; bool requestTransaction = false; if (_timeFixContext != nil && _timeFixContext.transactionId != nil) { _timeFixContext = nil; requestTransaction = true; } for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProtoAllTransactionsMayHaveFailed:)]) [messageService mtProtoAllTransactionsMayHaveFailed:self]; } if (requestTransaction && ![self isPaused]) [self requestTransportTransaction]; } - (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 messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:receivedQuickAck:)]) [messageService mtProto:self receivedQuickAck:quickAckId]; } }]; } - (void)transportDecodeProgressToken:(MTTransport *)transport data:(NSData *)data token:(int64_t)token completion:(void (^)(int64_t token, id progressToken))completion { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport || completion == nil) return; MTInputStream *is = [[MTInputStream alloc] initWithData:data]; int64_t keyId = [is readInt64]; if (keyId != 0 && _authInfo != nil) { NSData *messageKey = [is readData:16]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyForAuthKey:_authInfo.authKey messageKey:messageKey toClient:true]; NSMutableData *messageData = [is readMutableData:(data.length - 24)]; while (messageData.length % 16 != 0) [messageData setLength:messageData.length - 1]; if (messageData.length != 0) { MTAesDecryptInplace(messageData, encryptionKey.key, encryptionKey.iv); MTInputStream *messageIs = [[MTInputStream alloc] initWithData:messageData]; [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)); } } }]; } - (void)findReqMsgId:(MTInputStream *)is signature:(int32_t)signature reqMsgId:(int64_t *)reqMsgId failed:(bool *)failed { if (signature == (int)0x73f1f8dc) //msg_container { int count = [is readInt32:failed]; if (*failed) return; for (int i = 0; i < count; i++) { [is readInt64:failed]; [is readInt32:failed]; [is readInt32:failed]; if (*failed) return; int innerSignature = [is readInt32:failed]; if (*failed) return; [self findReqMsgId:is signature:innerSignature reqMsgId:reqMsgId failed:failed]; if (*failed || *reqMsgId != 0) return; } } else if (signature == (int)0xf35c6d01) //rpc_result { int64_t value = [is readInt64:failed]; if (*failed) return; *reqMsgId = value; } else if (signature == (int)0x62d6b459) // msgs_ack { [is readInt32:failed]; if (*failed) return; int count = [is readInt32:failed]; if (*failed) return; for (int i = 0; i < count; i++) { [is readInt32:failed]; if (*failed) return; } } else if (signature == (int)0x347773c5) // pong { [is readInt64:failed]; [is readInt64:failed]; if (*failed) return; } } - (void)transportUpdatedDataReceiveProgress:(MTTransport *)transport progressToken:(id)progressToken packetLength:(NSInteger)packetLength progress:(float)progress { [[MTProto managerQueue] dispatchOnQueue:^ { if (transport != _transport) return; for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProto:updateReceiveProgressForToken:progress:packetLength:)]) [messageService mtProto:self updateReceiveProgressForToken:progressToken progress:progress packetLength:packetLength]; } }]; } - (void)transportTransactionsSucceeded:(NSArray *)transactionIds { [[MTProto managerQueue] dispatchOnQueue:^ { [_sessionInfo removeScheduledMessageConfirmationsWithTransactionIds:transactionIds]; }]; } - (void)transportHasIncomingData:(MTTransport *)transport data:(NSData *)data transactionId:(id)transactionId requestTransactionAfterProcessing:(bool)requestTransactionAfterProcessing decodeResult:(void (^)(id transactionId, bool success))decodeResult { [[MTProto managerQueue] dispatchOnQueue:^ { if (_transport != transport || [self isStopped]) return; _transport.simultaneousTransactionsEnabled = true; if (data.length == 4) { int32_t protocolErrorCode = 0; [data getBytes:&protocolErrorCode range:NSMakeRange(0, 4)]; MTLog(@"[MTProto#%p protocol error %" PRId32 "", self, protocolErrorCode); if (decodeResult != nil) decodeResult(transactionId, false); id currentTransport = _transport; [self transportTransactionsMayHaveFailed:transport transactionIds:@[transactionId]]; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:protocolErrorReceived:)]) [messageService mtProto:self protocolErrorReceived:protocolErrorCode]; } if (currentTransport == _transport) [self requestSecureTransportReset]; return; } NSData *decryptedData = nil; if (_useUnauthorizedMode) decryptedData = data; else decryptedData = [self _decryptIncomingTransportData:data]; if (decryptedData != nil) { if (decodeResult != nil) decodeResult(transactionId, true); int64_t dataMessageId = 0; bool parseError = false; NSArray *parsedMessages = [self _parseIncomingMessages:decryptedData dataMessageId:&dataMessageId parseError:&parseError]; if (parseError) { MTLog(@"[MTProto#%p incoming data parse error]", self); [self transportTransactionsMayHaveFailed:transport transactionIds:@[transactionId]]; [self requestSecureTransportReset]; } else { [self transportTransactionsSucceeded:@[transactionId]]; for (MTIncomingMessage *incomingMessage in parsedMessages) { [self _processIncomingMessage:incomingMessage withTransactionId:transactionId]; } if (requestTransactionAfterProcessing) [self requestTransportTransaction]; } } else { MTLog(@"[MTProto#%p couldn't decrypt incoming data]", self); if (decodeResult != nil) decodeResult(transactionId, false); [self transportTransactionsMayHaveFailed:transport transactionIds:@[transactionId]]; [self requestSecureTransportReset]; } }]; } - (NSData *)_decryptIncomingTransportData:(NSData *)data { if (_authInfo == nil) return nil; if (data.length < 24 + 36) return nil; int64_t authKeyId = 0; [data getBytes:&authKeyId range:NSMakeRange(0, 8)]; if (authKeyId != _authInfo.authKeyId) return nil; NSData *embeddedMessageKey = [data subdataWithRange:NSMakeRange(8, 16)]; MTMessageEncryptionKey *encryptionKey = [MTMessageEncryptionKey messageEncryptionKeyForAuthKey:_authInfo.authKey messageKey:embeddedMessageKey toClient:true]; if (encryptionKey == nil) return nil; NSData *decryptedData = MTAesDecrypt([data subdataWithRange:NSMakeRange(24, data.length - 24)], encryptionKey.key, encryptionKey.iv); int32_t messageDataLength = 0; [decryptedData getBytes:&messageDataLength range:NSMakeRange(28, 4)]; if (messageDataLength < 0 || messageDataLength < (int32_t)decryptedData.length - 32 - 16 || messageDataLength > (int32_t)decryptedData.length - 32) return nil; NSData *messageKeyFull = MTSubdataSha1(decryptedData, 0, 32 + messageDataLength); NSData *messageKey = [[NSData alloc] initWithBytes:(((int8_t *)messageKeyFull.bytes) + messageKeyFull.length - 16) length:16]; if (![messageKey isEqualToData:embeddedMessageKey]) return nil; return decryptedData; } - (id)parseMessage:(NSData *)data { NSData *unwrappedData = [MTInternalMessageParser unwrapMessage:data]; id internalMessage = [MTInternalMessageParser parseMessage:unwrappedData]; if (internalMessage != nil) return internalMessage; return [_context.serialization parseMessage:unwrappedData]; } - (NSArray *)_parseIncomingMessages:(NSData *)data dataMessageId:(out int64_t *)dataMessageId parseError:(out bool *)parseError { MTInputStream *is = [[MTInputStream alloc] initWithData:data]; bool readError = false; int64_t embeddedMessageId = 0; int32_t embeddedSeqNo = 0; int64_t embeddedSalt = 0; int32_t topMessageSize = 0; if (_useUnauthorizedMode) { int64_t authKeyId = [is readInt64]; if (authKeyId != 0) { if (parseError != NULL) *parseError = true; return nil; } embeddedMessageId = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } topMessageSize = [is readInt32:&readError]; if (readError || topMessageSize < 4) { if (parseError != NULL) *parseError = true; return nil; } if (dataMessageId != 0) *dataMessageId = embeddedMessageId; } else { embeddedSalt = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } int64_t embeddedSessionId = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } if (embeddedSessionId != _sessionInfo.sessionId) { if (parseError != NULL) *parseError = true; return nil; } embeddedMessageId = [is readInt64:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } embeddedSeqNo = [is readInt32:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } [is readInt32:&readError]; if (readError) { if (parseError != NULL) *parseError = true; return nil; } } NSMutableData *topMessageData = [[NSMutableData alloc] init]; uint8_t buffer[128]; while (true) { NSInteger readBytes = [[is wrappedInputStream] read:buffer maxLength:128]; if (readBytes <= 0) break; [topMessageData appendBytes:buffer length:readBytes]; } id topObject = [self parseMessage:topMessageData]; if (topObject == nil) { if (parseError != NULL) *parseError = true; return nil; } #warning check message id NSMutableArray *messages = [[NSMutableArray alloc] init]; NSTimeInterval timestamp = embeddedMessageId / 4294967296.0; if ([topObject isKindOfClass:[MTMsgContainerMessage class]]) { for (MTMessage *subMessage in ((MTMsgContainerMessage *)topObject).messages) { id subObject = [self parseMessage:subMessage.data]; if (subObject == nil) { if (parseError != NULL) *parseError = true; return nil; } int64_t subMessageId = subMessage.messageId; int32_t subMessageSeqNo = subMessage.seqNo; int32_t subMessageLength = (int32_t)subMessage.data.length; [messages addObject:[[MTIncomingMessage alloc] initWithMessageId:subMessageId seqNo:subMessageSeqNo salt:embeddedSalt timestamp:timestamp size:subMessageLength body:subObject]]; } } else if ([topObject isKindOfClass:[MTMessage class]]) { MTMessage *message = topObject; id subObject = [self parseMessage:message.data]; if (subObject == nil) { if (parseError != NULL) *parseError = true; return nil; } int64_t subMessageId = message.messageId; int32_t subMessageSeqNo = message.seqNo; int32_t subMessageLength = (int32_t)message.data.length; [messages addObject:[[MTIncomingMessage alloc] initWithMessageId:subMessageId seqNo:subMessageSeqNo salt:embeddedSalt timestamp:timestamp size:subMessageLength body:subObject]]; } else [messages addObject:[[MTIncomingMessage alloc] initWithMessageId:embeddedMessageId seqNo:embeddedSeqNo salt:embeddedSalt timestamp:timestamp size:topMessageSize body:topObject]]; return messages; } - (void)_processIncomingMessage:(MTIncomingMessage *)incomingMessage withTransactionId:(id)transactionId { if ([_sessionInfo messageProcessed:incomingMessage.messageId]) { MTLog(@"[MTProto#%p received duplicate message %" PRId64 "]", self, incomingMessage.messageId); [_sessionInfo scheduleMessageConfirmation:incomingMessage.messageId size:incomingMessage.size]; if ([_sessionInfo scheduledMessageConfirmationsExceedSize:MTMaxUnacknowledgedMessageSize orCount:MTMaxUnacknowledgedMessageCount]) [self requestTransportTransaction]; return; } MTLog(@"[MTProto#%p received %@]", self, [self incomingMessageDescription:incomingMessage]); [_sessionInfo setMessageProcessed:incomingMessage.messageId]; if (!_useUnauthorizedMode && incomingMessage.seqNo % 2 != 0) { [_sessionInfo scheduleMessageConfirmation:incomingMessage.messageId size:incomingMessage.size]; if ([_sessionInfo scheduledMessageConfirmationsExceedSize:MTMaxUnacknowledgedMessageSize orCount:MTMaxUnacknowledgedMessageCount]) [self requestTransportTransaction]; } if (!_useUnauthorizedMode && [incomingMessage.body isKindOfClass:[MTBadMsgNotificationMessage class]]) { MTBadMsgNotificationMessage *badMsgNotification = incomingMessage.body; int64_t badMessageId = badMsgNotification.badMessageId; NSArray *containerMessageIds = [_sessionInfo messageIdsInContainer:badMessageId]; if ([badMsgNotification isKindOfClass:[MTBadServerSaltNotificationMessage class]]) { if (_timeFixContext != nil && badMessageId == _timeFixContext.messageId) { _timeFixContext = nil; __block bool test = false; #if TARGET_IPHONE_SIMULATOR static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { test = true; }); #endif /*if (ABS(MTAbsoluteSystemTime() - _timeFixContext.timeFixAbsoluteStartTime) > 5.0 || test) { [self initiateTimeSync]; } else*/ { int64_t validSalt = ((MTBadServerSaltNotificationMessage *)badMsgNotification).nextServerSalt; NSTimeInterval timeDifference = incomingMessage.messageId / 4294967296.0 - [[NSDate date] timeIntervalSince1970]; [self completeTimeSync]; [self timeSyncInfoChanged:timeDifference saltList:@[[[MTDatacenterSaltInfo alloc] initWithSalt:validSalt firstValidMessageId:incomingMessage.messageId lastValidMessageId:incomingMessage.messageId + (4294967296 * 30 * 60)]]]; } } else [self initiateTimeSync]; } else { switch (badMsgNotification.errorCode) { case 16: case 17: { if (_timeFixContext != nil && badMessageId == _timeFixContext.messageId) { _timeFixContext = nil; NSTimeInterval timeDifference = incomingMessage.messageId / 4294967296.0 - [[NSDate date] timeIntervalSince1970]; [self completeTimeSync]; [self timeSyncInfoChanged:timeDifference saltList:nil]; } else [self initiateTimeSync]; break; } case 32: case 33: { [self resetSessionInfo]; [self initiateTimeSync]; break; } case 48: { [self initiateTimeSync]; break; } default: break; } } for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:messageDeliveryFailed:)]) { [messageService mtProto:self messageDeliveryFailed:badMessageId]; if (containerMessageIds != nil) { for (NSNumber *nMessageId in containerMessageIds) [messageService mtProto:self messageDeliveryFailed:(int64_t)[nMessageId longLongValue]]; } } } if ([self canAskForTransactions] || [self canAskForServiceTransactions]) [self requestTransportTransaction]; } else if ([incomingMessage.body isKindOfClass:[MTMsgsAckMessage class]]) { NSArray *messageIds = ((MTMsgsAckMessage *)incomingMessage.body).messageIds; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:messageDeliveryConfirmed:)]) [messageService mtProto:self messageDeliveryConfirmed:messageIds]; } } else if ([incomingMessage.body isKindOfClass:[MTMsgDetailedInfoMessage class]]) { MTMsgDetailedInfoMessage *detailedInfoMessage = incomingMessage.body; bool shouldRequest = false; if ([detailedInfoMessage isKindOfClass:[MTMsgDetailedResponseInfoMessage class]]) { int64_t requestMessageId = ((MTMsgDetailedResponseInfoMessage *)detailedInfoMessage).requestMessageId; MTLog(@"[MTProto#%p detailed info %" PRId64 " is for %" PRId64 "", self, incomingMessage.messageId, requestMessageId); for (id messageService in _messageServices) { if ([messageService respondsToSelector:@selector(mtProto:shouldRequestMessageInResponseToMessageId:currentTransactionId:)]) { if ([messageService mtProto:self shouldRequestMessageInResponseToMessageId:requestMessageId currentTransactionId:transactionId]) { shouldRequest = true; break; } } } } else shouldRequest = true; if (shouldRequest) { [self requestMessageWithId:detailedInfoMessage.responseMessageId]; MTLog(@"[MTProto#%p will request message %" PRId64 "", self, detailedInfoMessage.responseMessageId); } else { [_sessionInfo scheduleMessageConfirmation:detailedInfoMessage.responseMessageId size:(NSInteger)detailedInfoMessage.responseLength]; [self requestTransportTransaction]; } } else if ([incomingMessage.body isKindOfClass:[MTNewSessionCreatedMessage class]]) { int64_t firstValidMessageId = ((MTNewSessionCreatedMessage *)incomingMessage.body).firstMessageId; for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProtoServerDidChangeSession:firstValidMessageId:otherValidMessageIds:)]) [messageService mtProtoServerDidChangeSession:self firstValidMessageId:firstValidMessageId otherValidMessageIds:[_sessionInfo messageIdsInContainersAfterMessageId:firstValidMessageId]]; } } else { for (NSInteger i = (NSInteger)_messageServices.count - 1; i >= 0; i--) { id messageService = _messageServices[(NSUInteger)i]; if ([messageService respondsToSelector:@selector(mtProto:receivedMessage:)]) [messageService mtProto:self receivedMessage:incomingMessage]; } if (_timeFixContext != nil && [incomingMessage.body isKindOfClass:[MTPongMessage class]] && ((MTPongMessage *)incomingMessage.body).messageId == _timeFixContext.messageId) { _timeFixContext = nil; [self completeTimeSync]; if ([self canAskForTransactions] || [self canAskForServiceTransactions]) [self requestTransportTransaction]; } } } - (void)contextDatacenterTransportSchemeUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme { [[MTProto managerQueue] dispatchOnQueue:^ { if (context == _context && datacenterId == _datacenterId && ![self isStopped] && (_transportScheme == nil || ![_transportScheme isEqualToScheme:transportScheme])) { if (_mtState & MTProtoStateAwaitingDatacenterScheme) [self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterScheme)]; [self resetTransport]; [self requestTransportTransaction]; } }]; } - (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo { [[MTProto managerQueue] dispatchOnQueue:^ { if (!_useUnauthorizedMode && context == _context && datacenterId == _datacenterId && authInfo != nil) { _authInfo = authInfo; if (_mtState & MTProtoStateAwaitingDatacenterAuthorization) { [self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterAuthorization)]; [self resetTransport]; [self requestTransportTransaction]; } } }]; } - (void)contextDatacenterAuthTokenUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authToken:(id)authToken { [[MTProto managerQueue] dispatchOnQueue:^ { if (!_useUnauthorizedMode && context == _context && datacenterId == _datacenterId && _requiredAuthToken != nil && [_requiredAuthToken isEqual:authToken]) { if (_mtState & MTProtoStateAwaitingDatacenterAuthToken) { [self setMtState:_mtState & (~MTProtoStateAwaitingDatacenterAuthToken)]; [self resetTransport]; [self requestTransportTransaction]; } } }]; } - (void)timeSyncServiceCompleted:(MTTimeSyncMessageService *)timeSyncService timeDifference:(NSTimeInterval)timeDifference saltList:(NSArray *)saltList { if ([_messageServices containsObject:timeSyncService]) { [self completeTimeSync]; [_messageServices removeObject:timeSyncService]; [self timeSyncInfoChanged:timeDifference saltList:saltList]; } } - (void)timeSyncInfoChanged:(NSTimeInterval)timeDifference saltList:(NSArray *)saltList { [_context setGlobalTimeDifference:timeDifference]; if (saltList != nil) { MTDatacenterAuthInfo *authInfo = [_context authInfoForDatacenterWithId:_datacenterId]; if (authInfo != nil) { MTDatacenterAuthInfo *updatedAuthInfo = [authInfo mergeSaltSet:saltList forTimestamp:[_context globalTime]]; [_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:updatedAuthInfo]; } } if ([self canAskForTransactions] || [self canAskForServiceTransactions]) [self requestTransportTransaction]; } @end