mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
389 lines
16 KiB
Objective-C
389 lines
16 KiB
Objective-C
#import "TGSecretOutgoingQueueActor.h"
|
|
|
|
#import <MTProtoKit/MTContext.h>
|
|
#import <MTProtoKit/MTRequest.h>
|
|
|
|
#import "TGDatabase.h"
|
|
#import "TL/TLMetaScheme.h"
|
|
|
|
#import "ActionStage.h"
|
|
|
|
#import "TGTelegramNetworking.h"
|
|
|
|
#import "TGModernSendSecretMessageActor.h"
|
|
|
|
@interface TGSecretOutgoingRequest : MTRequest
|
|
|
|
@property (nonatomic) int32_t actionId;
|
|
@property (nonatomic) int32_t seqOut;
|
|
@property (nonatomic) bool isResend;
|
|
@property (nonatomic) NSUInteger layer;
|
|
|
|
@end
|
|
|
|
@implementation TGSecretOutgoingRequest
|
|
|
|
@end
|
|
|
|
@interface TGSecretOutgoingQueueActor ()
|
|
{
|
|
int64_t _peerId;
|
|
int64_t _encryptedChatId;
|
|
int64_t _accessHash;
|
|
bool _isCreator;
|
|
|
|
NSMutableSet *_executingRequestsActionIds;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGSecretOutgoingQueueActor
|
|
|
|
+ (void)load
|
|
{
|
|
[ASActor registerActorClass:self];
|
|
}
|
|
|
|
+ (NSString *)genericPath
|
|
{
|
|
return @"/tg/secret/outgoing/@";
|
|
}
|
|
|
|
- (void)execute:(NSDictionary *)options
|
|
{
|
|
_peerId = (int64_t)[options[@"peerId"] longLongValue];
|
|
_encryptedChatId = [TGDatabaseInstance() encryptedConversationIdForPeerId:_peerId];
|
|
_accessHash = [TGDatabaseInstance() encryptedConversationAccessHash:_peerId];
|
|
_isCreator = [TGDatabaseInstance() encryptedConversationIsCreator:_peerId];
|
|
|
|
if (_accessHash == 0)
|
|
[ActionStageInstance() actionFailed:self.path reason:-1];
|
|
else
|
|
{
|
|
_executingRequestsActionIds = [[NSMutableSet alloc] init];
|
|
|
|
[self _poll];
|
|
}
|
|
}
|
|
|
|
- (void)watcherJoined:(ASHandle *)watcherHandle options:(NSDictionary *)options waitingInActorQueue:(bool)waitingInActorQueue
|
|
{
|
|
[super watcherJoined:watcherHandle options:options waitingInActorQueue:waitingInActorQueue];
|
|
|
|
[self _poll];
|
|
}
|
|
|
|
- (void)_poll
|
|
{
|
|
[TGDatabaseInstance() dequeuePeerOutgoingActions:_peerId completion:^(NSArray *actions, NSArray *resendActions)
|
|
{
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
if ([TGDatabaseInstance() peerLayer:_peerId] >= 20)
|
|
{
|
|
int32_t keyUseCount = [TGDatabaseInstance() currentEncryptionKeyUseCount:_peerId];
|
|
int32_t maxKeyUseCount = 100;
|
|
if (keyUseCount >= maxKeyUseCount)
|
|
{
|
|
[TGModernSendSecretMessageActor maybeRekeyPeerId:_peerId];
|
|
}
|
|
}
|
|
|
|
NSMutableArray *requests = [[NSMutableArray alloc] init];
|
|
|
|
NSArray *mergedActions = [actions arrayByAddingObjectsFromArray:resendActions];
|
|
|
|
NSInteger index = -1;
|
|
for (TGStoredSecretActionWithSeq *action in mergedActions)
|
|
{
|
|
index++;
|
|
|
|
if ([_executingRequestsActionIds containsObject:@(action.actionId.value)])
|
|
continue;
|
|
|
|
TGSecretOutgoingRequest *request = [self requestForAction:action.action actionId:action.actionId.value seqIn:action.seqIn seqOut:action.seqOut isResend:index >= (NSInteger)actions.count];
|
|
if (request != nil)
|
|
{
|
|
__weak TGSecretOutgoingQueueActor *weakSelf = self;
|
|
int32_t seqOut = action.seqOut;
|
|
int32_t actionId = action.actionId.value;
|
|
int32_t actionSeqOut = request.seqOut;
|
|
bool isResend = request.isResend;
|
|
NSUInteger actionLayer = request.layer;
|
|
[request setCompleted:^(id result, NSTimeInterval timestamp, id error)
|
|
{
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
__strong TGSecretOutgoingQueueActor *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
{
|
|
if (error == nil)
|
|
{
|
|
[strongSelf actionCompletedWithId:actionId date:(int32_t)(timestamp * 4294967296.0) result:result actionSeqOut:actionSeqOut actionLayer:actionLayer isResend:isResend];
|
|
}
|
|
else
|
|
{
|
|
[strongSelf->_executingRequestsActionIds removeObject:@(actionId)];
|
|
}
|
|
}
|
|
}];
|
|
}];
|
|
|
|
[request setAcknowledgementReceived:^
|
|
{
|
|
__strong TGSecretOutgoingQueueActor *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf actionQuickAck:actionId];
|
|
}];
|
|
|
|
[request setShouldDependOnRequest:^bool(MTRequest *other)
|
|
{
|
|
if ([other isKindOfClass:[TGSecretOutgoingRequest class]])
|
|
{
|
|
return ((TGSecretOutgoingRequest *)other).seqOut < seqOut;
|
|
}
|
|
|
|
return false;
|
|
}];
|
|
|
|
[requests addObject:request];
|
|
}
|
|
}
|
|
|
|
for (TGSecretOutgoingRequest *request in requests)
|
|
{
|
|
#if defined(DEBUG) && 0
|
|
TLmessages_SentEncryptedMessage$messages_sentEncryptedMessage *sentEncryptedMessage = [[TLmessages_SentEncryptedMessage$messages_sentEncryptedMessage alloc] init];
|
|
sentEncryptedMessage.date = (int32_t)[[[TGTelegramNetworking instance] context] globalTime];
|
|
request.completed(sentEncryptedMessage, 0, nil);
|
|
#else
|
|
[[TGTelegramNetworking instance] addRequest:request];
|
|
#endif
|
|
[_executingRequestsActionIds addObject:@(request.actionId)];
|
|
}
|
|
}];
|
|
}];
|
|
}
|
|
|
|
- (void)actionCompletedWithId:(int32_t)actionId date:(int32_t)date result:(id)result actionSeqOut:(int32_t)actionSeqOut actionLayer:(NSUInteger)actionLayer isResend:(bool)isResend
|
|
{
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
if (isResend)
|
|
[TGDatabaseInstance() deletePeerOutgoingResendActions:_peerId actionIds:@[@(actionId)]];
|
|
else if (actionSeqOut >= 0 && actionLayer >= 17)
|
|
[TGDatabaseInstance() applyPeerSeqOut:_peerId seqOut:actionSeqOut];
|
|
else
|
|
[TGDatabaseInstance() deletePeerOutgoingActions:_peerId actionIds:@[@(actionId)]];
|
|
|
|
[ActionStageInstance() dispatchMessageToWatchers:self.path messageType:@"actionCompletedWithSeq" message:@{@"actionId": @(actionId), @"date": @(date), @"result": result}];
|
|
}];
|
|
}
|
|
|
|
- (void)actionQuickAck:(int32_t)actionId
|
|
{
|
|
[ActionStageInstance() dispatchMessageToWatchers:self.path messageType:@"actionQuickAck" message:@{@"actionId": @(actionId)}];
|
|
}
|
|
|
|
- (TGSecretOutgoingRequest *)requestForAction:(id)action actionId:(int32_t)actionId seqIn:(int32_t)seqIn seqOut:(int32_t)seqOut isResend:(bool)isResend
|
|
{
|
|
if ([action isKindOfClass:[TGStoredOutgoingMessageSecretAction class]])
|
|
{
|
|
TGStoredOutgoingMessageSecretAction *concreteAction = action;
|
|
|
|
NSData *messageData = nil;
|
|
NSData *actionData = nil;
|
|
|
|
for (TGModernSendSecretMessageActor *actor in [ActionStageInstance() executingActorsWithPathPrefix:@"/tg/sendSecretMessage/"])
|
|
{
|
|
if ([actor waitsForActionWithId:actionId])
|
|
{
|
|
actionData = concreteAction.data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (actionData == nil)
|
|
{
|
|
actionData = [TGModernSendSecretMessageActor decryptedServiceMessageActionWithLayer:concreteAction.layer deleteMessagesWithRandomIds:@[@(concreteAction.randomId)] randomId:concreteAction.randomId];
|
|
}
|
|
|
|
int64_t keyId = 0;
|
|
NSData *key = [TGDatabaseInstance() encryptionKeyForConversationId:_peerId requestedKeyFingerprint:concreteAction.keyId outKeyFingerprint:&keyId];
|
|
|
|
if (key == nil)
|
|
{
|
|
TGLog(@"missing key %" PRIx64, concreteAction.keyId);
|
|
}
|
|
|
|
if (concreteAction.layer >= 17)
|
|
{
|
|
NSMutableData *data = [[NSMutableData alloc] init];
|
|
int32_t constructorSignature = 0x1be31789;
|
|
[data appendBytes:&constructorSignature length:4];
|
|
|
|
uint8_t randomBytesLength = 15;
|
|
[data appendBytes:&randomBytesLength length:1];
|
|
|
|
uint8_t randomBytes[15];
|
|
arc4random_buf(randomBytes, 15);
|
|
[data appendBytes:randomBytes length:15];
|
|
|
|
int32_t layer = (int32_t)concreteAction.layer;
|
|
[data appendBytes:&layer length:4];
|
|
|
|
int32_t inSeqNo = seqIn * 2 + (_isCreator ? 0 : 1);
|
|
[data appendBytes:&inSeqNo length:4];
|
|
|
|
int32_t outSeqNo = seqOut * 2 + (_isCreator ? 1 : 0);
|
|
[data appendBytes:&outSeqNo length:4];
|
|
|
|
TGLog(@"preparing outgoing message request for seqOut: %d, seqIn: %d", seqOut, seqIn);
|
|
|
|
[data appendData:actionData];
|
|
|
|
messageData = [TGModernSendSecretMessageActor encryptMessage:data key:key keyId:keyId];
|
|
}
|
|
else
|
|
messageData = [TGModernSendSecretMessageActor encryptMessage:actionData key:key keyId:keyId];
|
|
|
|
TGSecretOutgoingRequest *request = [[TGSecretOutgoingRequest alloc] init];
|
|
request.actionId = actionId;
|
|
request.isResend = isResend;
|
|
request.seqOut = concreteAction.layer >= 17 ? seqOut : -1;
|
|
request.layer = concreteAction.layer;
|
|
|
|
if (concreteAction.fileInfo == nil)
|
|
{
|
|
TLRPCmessages_sendEncrypted$messages_sendEncrypted *sendEncrypted = [[TLRPCmessages_sendEncrypted$messages_sendEncrypted alloc] init];
|
|
|
|
TLInputEncryptedChat$inputEncryptedChat *inputEncryptedChat = [[TLInputEncryptedChat$inputEncryptedChat alloc] init];
|
|
inputEncryptedChat.chat_id = (int32_t)_encryptedChatId;
|
|
inputEncryptedChat.access_hash = _accessHash;
|
|
sendEncrypted.peer = inputEncryptedChat;
|
|
|
|
sendEncrypted.random_id = concreteAction.randomId;
|
|
sendEncrypted.data = messageData;
|
|
|
|
request.body = sendEncrypted;
|
|
}
|
|
else
|
|
{
|
|
TLRPCmessages_sendEncryptedFile$messages_sendEncryptedFile *sendEncrypted = [[TLRPCmessages_sendEncryptedFile$messages_sendEncryptedFile alloc] init];
|
|
|
|
TLInputEncryptedChat$inputEncryptedChat *inputEncryptedChat = [[TLInputEncryptedChat$inputEncryptedChat alloc] init];
|
|
inputEncryptedChat.chat_id = (int32_t)_encryptedChatId;
|
|
inputEncryptedChat.access_hash = _accessHash;
|
|
sendEncrypted.peer = inputEncryptedChat;
|
|
|
|
sendEncrypted.random_id = concreteAction.randomId;
|
|
sendEncrypted.data = messageData;
|
|
|
|
if ([concreteAction.fileInfo isKindOfClass:[TGStoredOutgoingMessageFileInfoUploaded class]])
|
|
{
|
|
TGStoredOutgoingMessageFileInfoUploaded *fileInfo = (TGStoredOutgoingMessageFileInfoUploaded *)concreteAction.fileInfo;
|
|
TLInputEncryptedFile$inputEncryptedFileUploaded *schemaFileInfo = [[TLInputEncryptedFile$inputEncryptedFileUploaded alloc] init];
|
|
schemaFileInfo.n_id = fileInfo.n_id;
|
|
schemaFileInfo.parts = fileInfo.parts;
|
|
//schemaFileInfo.md5_checksum = fileInfo.md5_checksum;
|
|
schemaFileInfo.md5_checksum = @"";
|
|
schemaFileInfo.key_fingerprint = fileInfo.key_fingerprint;
|
|
|
|
sendEncrypted.file = schemaFileInfo;
|
|
}
|
|
else if ([concreteAction.fileInfo isKindOfClass:[TGStoredOutgoingMessageFileInfoExisting class]])
|
|
{
|
|
TGStoredOutgoingMessageFileInfoExisting *fileInfo = (TGStoredOutgoingMessageFileInfoExisting *)concreteAction.fileInfo;
|
|
TLInputEncryptedFile$inputEncryptedFile *schemaFileInfo = [[TLInputEncryptedFile$inputEncryptedFile alloc] init];
|
|
schemaFileInfo.n_id = fileInfo.n_id;
|
|
schemaFileInfo.access_hash = fileInfo.access_hash;
|
|
|
|
sendEncrypted.file = schemaFileInfo;
|
|
}
|
|
else if ([concreteAction.fileInfo isKindOfClass:[TGStoredOutgoingMessageFileInfoBigUploaded class]])
|
|
{
|
|
TGStoredOutgoingMessageFileInfoBigUploaded *fileInfo = (TGStoredOutgoingMessageFileInfoBigUploaded *)concreteAction.fileInfo;
|
|
TLInputEncryptedFile$inputEncryptedFileBigUploaded *schemaFileInfo = [[TLInputEncryptedFile$inputEncryptedFileBigUploaded alloc] init];
|
|
schemaFileInfo.n_id = fileInfo.n_id;
|
|
schemaFileInfo.parts = fileInfo.parts;
|
|
schemaFileInfo.key_fingerprint = fileInfo.key_fingerprint;
|
|
|
|
sendEncrypted.file = schemaFileInfo;
|
|
}
|
|
|
|
request.body = sendEncrypted;
|
|
}
|
|
|
|
return request;
|
|
}
|
|
else if ([action isKindOfClass:[TGStoredOutgoingServiceMessageSecretAction class]])
|
|
{
|
|
TGStoredOutgoingServiceMessageSecretAction *concreteAction = action;
|
|
|
|
NSData *messageData = nil;
|
|
|
|
int64_t keyId = 0;
|
|
NSData *key = [TGDatabaseInstance() encryptionKeyForConversationId:_peerId requestedKeyFingerprint:concreteAction.keyId outKeyFingerprint:&keyId];
|
|
|
|
if (key == nil)
|
|
{
|
|
TGLog(@"missing key %" PRIx64, concreteAction.keyId);
|
|
}
|
|
|
|
if (concreteAction.layer >= 17)
|
|
{
|
|
NSMutableData *data = [[NSMutableData alloc] init];
|
|
int32_t constructorSignature = 0x1be31789;
|
|
[data appendBytes:&constructorSignature length:4];
|
|
|
|
uint8_t randomBytesLength = 15;
|
|
[data appendBytes:&randomBytesLength length:1];
|
|
|
|
uint8_t randomBytes[15];
|
|
arc4random_buf(randomBytes, 15);
|
|
[data appendBytes:randomBytes length:15];
|
|
|
|
int32_t layer = (int32_t)concreteAction.layer;
|
|
[data appendBytes:&layer length:4];
|
|
|
|
int32_t inSeqNo = seqIn * 2 + (_isCreator ? 0 : 1);
|
|
[data appendBytes:&inSeqNo length:4];
|
|
|
|
int32_t outSeqNo = seqOut * 2 + (_isCreator ? 1 : 0);
|
|
[data appendBytes:&outSeqNo length:4];
|
|
|
|
TGLog(@"preparing outgoing message request for (%lld) seqOut: %d, seqIn: %d", _peerId, seqOut, seqIn);
|
|
|
|
[data appendData:concreteAction.data];
|
|
|
|
messageData = [TGModernSendSecretMessageActor encryptMessage:data key:key keyId:keyId];
|
|
}
|
|
else
|
|
messageData = [TGModernSendSecretMessageActor encryptMessage:concreteAction.data key:key keyId:keyId];
|
|
|
|
TGSecretOutgoingRequest *request = [[TGSecretOutgoingRequest alloc] init];
|
|
request.actionId = actionId;
|
|
request.isResend = isResend;
|
|
request.seqOut = concreteAction.layer >= 17 ? seqOut : -1;
|
|
request.layer = concreteAction.layer;
|
|
|
|
TLRPCmessages_sendEncryptedService$messages_sendEncryptedService *sendEncryptedService = [[TLRPCmessages_sendEncryptedService$messages_sendEncryptedService alloc] init];
|
|
|
|
TLInputEncryptedChat$inputEncryptedChat *inputEncryptedChat = [[TLInputEncryptedChat$inputEncryptedChat alloc] init];
|
|
inputEncryptedChat.chat_id = (int32_t)_encryptedChatId;
|
|
inputEncryptedChat.access_hash = _accessHash;
|
|
sendEncryptedService.peer = inputEncryptedChat;
|
|
|
|
sendEncryptedService.random_id = concreteAction.randomId;
|
|
sendEncryptedService.data = messageData;
|
|
|
|
request.body = sendEncryptedService;
|
|
|
|
return request;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
@end
|