mirror of https://github.com/procxx/kepka.git
API scheme updated to layer 66.
Support CDN file download.
This commit is contained in:
parent
7dd24a30b5
commit
8d28d0691f
|
@ -201,7 +201,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL
|
||||||
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
|
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
|
||||||
|
|
||||||
userEmpty#200250ba id:int = User;
|
userEmpty#200250ba id:int = User;
|
||||||
user#d10d979a flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string = User;
|
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
|
||||||
|
|
||||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||||
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
|
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
|
||||||
|
@ -357,6 +357,8 @@ inputMessagesFilterVoice#50f5c392 = MessagesFilter;
|
||||||
inputMessagesFilterMusic#3751b49e = MessagesFilter;
|
inputMessagesFilterMusic#3751b49e = MessagesFilter;
|
||||||
inputMessagesFilterChatPhotos#3a20ecb8 = MessagesFilter;
|
inputMessagesFilterChatPhotos#3a20ecb8 = MessagesFilter;
|
||||||
inputMessagesFilterPhoneCalls#80c99768 flags:# missed:flags.0?true = MessagesFilter;
|
inputMessagesFilterPhoneCalls#80c99768 flags:# missed:flags.0?true = MessagesFilter;
|
||||||
|
inputMessagesFilterRoundVoice#7a7c17a4 = MessagesFilter;
|
||||||
|
inputMessagesFilterRoundVideo#b549da53 = MessagesFilter;
|
||||||
|
|
||||||
updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
|
updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
|
||||||
updateMessageID#4e90bfd6 id:int random_id:long = Update;
|
updateMessageID#4e90bfd6 id:int random_id:long = Update;
|
||||||
|
@ -440,8 +442,9 @@ photos.photosSlice#15051f54 count:int photos:Vector<Photo> users:Vector<User> =
|
||||||
photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
|
photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
|
||||||
|
|
||||||
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
|
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
|
||||||
|
upload.fileCdnRedirect#1508485a dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes = upload.File;
|
||||||
|
|
||||||
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true id:int ip_address:string port:int = DcOption;
|
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true id:int ip_address:string port:int = DcOption;
|
||||||
|
|
||||||
config#cb601684 flags:# phonecalls_enabled:flags.1?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string disabled_features:Vector<DisabledFeature> = Config;
|
config#cb601684 flags:# phonecalls_enabled:flags.1?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string disabled_features:Vector<DisabledFeature> = Config;
|
||||||
|
|
||||||
|
@ -501,6 +504,8 @@ sendMessageUploadDocumentAction#aa0cd9e4 progress:int = SendMessageAction;
|
||||||
sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
|
sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
|
||||||
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
|
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
|
||||||
sendMessageGamePlayAction#dd6a8f48 = SendMessageAction;
|
sendMessageGamePlayAction#dd6a8f48 = SendMessageAction;
|
||||||
|
sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
|
||||||
|
sendMessageUploadRoundAction#bb718624 = SendMessageAction;
|
||||||
|
|
||||||
contacts.found#1aa1f784 results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
|
contacts.found#1aa1f784 results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
|
||||||
|
|
||||||
|
@ -533,7 +538,7 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
|
||||||
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
|
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
|
||||||
documentAttributeAnimated#11b58939 = DocumentAttribute;
|
documentAttributeAnimated#11b58939 = DocumentAttribute;
|
||||||
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
|
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
|
||||||
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
|
documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true duration:int w:int h:int = DocumentAttribute;
|
||||||
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
|
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
|
||||||
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
|
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
|
||||||
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
|
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
|
||||||
|
@ -853,6 +858,13 @@ phoneCallProtocol#a2bb35cb flags:# udp_p2p:flags.0?true udp_reflector:flags.1?tr
|
||||||
|
|
||||||
phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector<User> = phone.PhoneCall;
|
phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector<User> = phone.PhoneCall;
|
||||||
|
|
||||||
|
upload.cdnFileReuploadNeeded#eea8e46e request_token:bytes = upload.CdnFile;
|
||||||
|
upload.cdnFile#a99fca4f bytes:bytes = upload.CdnFile;
|
||||||
|
|
||||||
|
cdnPublicKey#c982eaba dc_id:int public_key:string = CdnPublicKey;
|
||||||
|
|
||||||
|
cdnConfig#5725e40a public_keys:Vector<CdnPublicKey> = CdnConfig;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
|
@ -1024,6 +1036,8 @@ upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
|
||||||
upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File;
|
upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File;
|
||||||
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
|
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
|
||||||
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
|
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
|
||||||
|
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
|
||||||
|
upload.reuploadCdnFile#2e7a2020 file_token:bytes request_token:bytes = Bool;
|
||||||
|
|
||||||
help.getConfig#c4f9186b = Config;
|
help.getConfig#c4f9186b = Config;
|
||||||
help.getNearestDc#1fb33026 = NearestDc;
|
help.getNearestDc#1fb33026 = NearestDc;
|
||||||
|
@ -1034,6 +1048,7 @@ help.getSupport#9cdf08cd = help.Support;
|
||||||
help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
|
help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
|
||||||
help.getTermsOfService#350170f3 = help.TermsOfService;
|
help.getTermsOfService#350170f3 = help.TermsOfService;
|
||||||
help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
|
help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
|
||||||
|
help.getCdnConfig#52029342 = CdnConfig;
|
||||||
|
|
||||||
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
||||||
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
||||||
|
@ -1082,4 +1097,4 @@ phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDisc
|
||||||
phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates;
|
phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates;
|
||||||
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
|
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
|
||||||
|
|
||||||
// LAYER 65
|
// LAYER 66
|
||||||
|
|
|
@ -46,8 +46,6 @@ enum {
|
||||||
MTPTcpConnectionWaitTimeout = 2000, // 2 seconds waiting for tcp, until we accept http
|
MTPTcpConnectionWaitTimeout = 2000, // 2 seconds waiting for tcp, until we accept http
|
||||||
MTPIPv4ConnectionWaitTimeout = 1000, // 1 seconds waiting for ipv4, until we accept ipv6
|
MTPIPv4ConnectionWaitTimeout = 1000, // 1 seconds waiting for ipv4, until we accept ipv6
|
||||||
|
|
||||||
MTPUploadSessionsCount = 2, // max 2 upload sessions is created
|
|
||||||
MTPDownloadSessionsCount = 2, // max 2 download sessions is created
|
|
||||||
MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill
|
MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill
|
||||||
|
|
||||||
MTPDebugBufferSize = 1024 * 1024, // 1 mb start size
|
MTPDebugBufferSize = 1024 * 1024, // 1 mb start size
|
||||||
|
@ -320,8 +318,6 @@ enum {
|
||||||
FileLoaderQueueStopTimeout = 5000,
|
FileLoaderQueueStopTimeout = 5000,
|
||||||
|
|
||||||
UseBigFilesFrom = 10 * 1024 * 1024, // mtp big files methods used for files greater than 10mb
|
UseBigFilesFrom = 10 * 1024 * 1024, // mtp big files methods used for files greater than 10mb
|
||||||
MaxFileQueries = 16, // max 16 file parts downloaded at the same time
|
|
||||||
MaxWebFileQueries = 8, // max 8 http[s] files downloaded at the same time
|
|
||||||
|
|
||||||
UploadPartSize = 32 * 1024, // 32kb for photo
|
UploadPartSize = 32 * 1024, // 32kb for photo
|
||||||
DocumentMaxPartsCount = 3000, // no more than 3000 parts
|
DocumentMaxPartsCount = 3000, // no more than 3000 parts
|
||||||
|
@ -330,7 +326,6 @@ enum {
|
||||||
DocumentUploadPartSize2 = 128 * 1024, // 128kb for small document ( <= 375mb )
|
DocumentUploadPartSize2 = 128 * 1024, // 128kb for small document ( <= 375mb )
|
||||||
DocumentUploadPartSize3 = 256 * 1024, // 256kb for medium document ( <= 750mb )
|
DocumentUploadPartSize3 = 256 * 1024, // 256kb for medium document ( <= 750mb )
|
||||||
DocumentUploadPartSize4 = 512 * 1024, // 512kb for large document ( <= 1500mb )
|
DocumentUploadPartSize4 = 512 * 1024, // 512kb for large document ( <= 1500mb )
|
||||||
MaxUploadFileParallelSize = MTPUploadSessionsCount * 512 * 1024, // max 512kb uploaded at the same time in each session
|
|
||||||
UploadRequestInterval = 500, // one part each half second, if not uploaded faster
|
UploadRequestInterval = 500, // one part each half second, if not uploaded faster
|
||||||
|
|
||||||
MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created
|
MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created
|
||||||
|
|
|
@ -219,7 +219,7 @@ inline void copy_bytes(byte_span destination, const_byte_span source) {
|
||||||
#define for_const(range_declaration, range_expression) for (range_declaration : std::as_const(range_expression))
|
#define for_const(range_declaration, range_expression) for (range_declaration : std::as_const(range_expression))
|
||||||
|
|
||||||
template <typename Enum>
|
template <typename Enum>
|
||||||
inline QFlags<Enum> qFlags(Enum v) {
|
inline constexpr QFlags<Enum> qFlags(Enum v) {
|
||||||
return QFlags<Enum>(v);
|
return QFlags<Enum>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ MTPVector<MTPDocumentAttribute> composeDocumentAttributes(DocumentData *document
|
||||||
if (document->dimensions.width() > 0 && document->dimensions.height() > 0) {
|
if (document->dimensions.width() > 0 && document->dimensions.height() > 0) {
|
||||||
int32 duration = document->duration();
|
int32 duration = document->duration();
|
||||||
if (duration >= 0) {
|
if (duration >= 0) {
|
||||||
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height())));
|
attributes.push_back(MTP_documentAttributeVideo(MTP_flags(0), MTP_int(duration), MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height())));
|
||||||
} else {
|
} else {
|
||||||
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height())));
|
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height())));
|
||||||
}
|
}
|
||||||
|
|
|
@ -335,14 +335,16 @@ void Result::createDocument() {
|
||||||
QString mime = _content_type;
|
QString mime = _content_type;
|
||||||
|
|
||||||
QVector<MTPDocumentAttribute> attributes;
|
QVector<MTPDocumentAttribute> attributes;
|
||||||
QSize dimensions(_width, _height);
|
auto dimensions = QSize(_width, _height);
|
||||||
if (_type == Type::Gif) {
|
if (_type == Type::Gif) {
|
||||||
const char *filename = (mime == qstr("video/mp4") ? "animation.gif.mp4" : "animation.gif");
|
auto filename = (mime == qstr("video/mp4") ? "animation.gif.mp4" : "animation.gif");
|
||||||
attributes.push_back(MTP_documentAttributeFilename(MTP_string(filename)));
|
attributes.push_back(MTP_documentAttributeFilename(MTP_string(filename)));
|
||||||
attributes.push_back(MTP_documentAttributeAnimated());
|
attributes.push_back(MTP_documentAttributeAnimated());
|
||||||
attributes.push_back(MTP_documentAttributeVideo(MTP_int(_duration), MTP_int(_width), MTP_int(_height)));
|
auto flags = MTPDdocumentAttributeVideo::Flags(0);
|
||||||
|
attributes.push_back(MTP_documentAttributeVideo(MTP_flags(flags), MTP_int(_duration), MTP_int(_width), MTP_int(_height)));
|
||||||
} else if (_type == Type::Video) {
|
} else if (_type == Type::Video) {
|
||||||
attributes.push_back(MTP_documentAttributeVideo(MTP_int(_duration), MTP_int(_width), MTP_int(_height)));
|
auto flags = MTPDdocumentAttributeVideo::Flags(0);
|
||||||
|
attributes.push_back(MTP_documentAttributeVideo(MTP_flags(flags), MTP_int(_duration), MTP_int(_width), MTP_int(_height)));
|
||||||
} else if (_type == Type::Audio) {
|
} else if (_type == Type::Audio) {
|
||||||
auto flags = MTPDdocumentAttributeAudio::Flags(0);
|
auto flags = MTPDdocumentAttributeAudio::Flags(0);
|
||||||
if (mime == qstr("audio/ogg")) {
|
if (mime == qstr("audio/ogg")) {
|
||||||
|
|
|
@ -348,7 +348,7 @@ void MainWindow::sendServiceHistoryRequest() {
|
||||||
UserData *user = App::userLoaded(ServiceUserId);
|
UserData *user = App::userLoaded(ServiceUserId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
auto userFlags = MTPDuser::Flag::f_first_name | MTPDuser::Flag::f_phone | MTPDuser::Flag::f_status | MTPDuser::Flag::f_verified;
|
auto userFlags = MTPDuser::Flag::f_first_name | MTPDuser::Flag::f_phone | MTPDuser::Flag::f_status | MTPDuser::Flag::f_verified;
|
||||||
user = App::feedUsers(MTP_vector<MTPUser>(1, MTP_user(MTP_flags(userFlags), MTP_int(ServiceUserId), MTPlong(), MTP_string("Telegram"), MTPstring(), MTPstring(), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently(), MTPint(), MTPstring(), MTPstring())));
|
user = App::feedUsers(MTP_vector<MTPUser>(1, MTP_user(MTP_flags(userFlags), MTP_int(ServiceUserId), MTPlong(), MTP_string("Telegram"), MTPstring(), MTPstring(), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently(), MTPint(), MTPstring(), MTPstring(), MTPstring())));
|
||||||
}
|
}
|
||||||
_serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(1), MTP_int(0), MTP_int(0)), _main->rpcDone(&MainWidget::serviceHistoryDone), _main->rpcFail(&MainWidget::serviceHistoryFail));
|
_serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(1), MTP_int(0), MTP_int(0)), _main->rpcDone(&MainWidget::serviceHistoryDone), _main->rpcFail(&MainWidget::serviceHistoryFail));
|
||||||
}
|
}
|
||||||
|
|
|
@ -481,17 +481,17 @@ void Messenger::peerClearPhoto(PeerId id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Messenger::killDownloadSessionsStart(int32 dc) {
|
void Messenger::killDownloadSessionsStart(MTP::DcId dcId) {
|
||||||
if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) {
|
if (killDownloadSessionTimes.constFind(dcId) == killDownloadSessionTimes.cend()) {
|
||||||
killDownloadSessionTimes.insert(dc, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout);
|
killDownloadSessionTimes.insert(dcId, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout);
|
||||||
}
|
}
|
||||||
if (!killDownloadSessionsTimer.isActive()) {
|
if (!killDownloadSessionsTimer.isActive()) {
|
||||||
killDownloadSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout + 5);
|
killDownloadSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout + 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Messenger::killDownloadSessionsStop(int32 dc) {
|
void Messenger::killDownloadSessionsStop(MTP::DcId dcId) {
|
||||||
killDownloadSessionTimes.remove(dc);
|
killDownloadSessionTimes.remove(dcId);
|
||||||
if (killDownloadSessionTimes.isEmpty() && killDownloadSessionsTimer.isActive()) {
|
if (killDownloadSessionTimes.isEmpty() && killDownloadSessionsTimer.isActive()) {
|
||||||
killDownloadSessionsTimer.stop();
|
killDownloadSessionsTimer.stop();
|
||||||
}
|
}
|
||||||
|
@ -543,7 +543,7 @@ void Messenger::killDownloadSessions() {
|
||||||
auto ms = getms(), left = static_cast<TimeMs>(MTPAckSendWaiting) + MTPKillFileSessionTimeout;
|
auto ms = getms(), left = static_cast<TimeMs>(MTPAckSendWaiting) + MTPKillFileSessionTimeout;
|
||||||
for (auto i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
|
for (auto i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
|
||||||
if (i.value() <= ms) {
|
if (i.value() <= ms) {
|
||||||
for (int j = 0; j < MTPDownloadSessionsCount; ++j) {
|
for (int j = 0; j < MTP::kDownloadSessionsCount; ++j) {
|
||||||
MTP::stopSession(MTP::downloadDcId(i.key(), j));
|
MTP::stopSession(MTP::downloadDcId(i.key(), j));
|
||||||
}
|
}
|
||||||
i = killDownloadSessionTimes.erase(i);
|
i = killDownloadSessionTimes.erase(i);
|
||||||
|
|
|
@ -108,8 +108,8 @@ public:
|
||||||
|
|
||||||
void writeUserConfigIn(TimeMs ms);
|
void writeUserConfigIn(TimeMs ms);
|
||||||
|
|
||||||
void killDownloadSessionsStart(int32 dc);
|
void killDownloadSessionsStart(MTP::DcId dcId);
|
||||||
void killDownloadSessionsStop(int32 dc);
|
void killDownloadSessionsStop(MTP::DcId dcId);
|
||||||
|
|
||||||
void checkLocalTime();
|
void checkLocalTime();
|
||||||
void checkMapVersion();
|
void checkMapVersion();
|
||||||
|
@ -145,7 +145,7 @@ private:
|
||||||
|
|
||||||
QMap<FullMsgId, PeerId> photoUpdates;
|
QMap<FullMsgId, PeerId> photoUpdates;
|
||||||
|
|
||||||
QMap<int32, TimeMs> killDownloadSessionTimes;
|
QMap<MTP::DcId, TimeMs> killDownloadSessionTimes;
|
||||||
SingleTimer killDownloadSessionsTimer;
|
SingleTimer killDownloadSessionsTimer;
|
||||||
|
|
||||||
// Some fields are just moved from the declaration.
|
// Some fields are just moved from the declaration.
|
||||||
|
|
|
@ -340,34 +340,13 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QMap<uint64, RSAPublicKey> RSAPublicKeys;
|
|
||||||
RSAPublicKeys InitRSAPublicKeys() {
|
|
||||||
DEBUG_LOG(("MTP Info: RSA public keys list creation"));
|
|
||||||
|
|
||||||
RSAPublicKeys result;
|
|
||||||
|
|
||||||
int keysCount;
|
|
||||||
const char **keys = cPublicRSAKeys(keysCount);
|
|
||||||
for (int i = 0; i < keysCount; ++i) {
|
|
||||||
RSAPublicKey key(keys[i]);
|
|
||||||
if (key.isValid()) {
|
|
||||||
result.insert(key.getFingerPrint(), key);
|
|
||||||
} else {
|
|
||||||
LOG(("MTP Error: could not read this public RSA key:"));
|
|
||||||
LOG((keys[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUG_LOG(("MTP Info: read %1 public RSA keys").arg(result.size()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Connection::Connection(Instance *instance) : _instance(instance) {
|
Connection::Connection(Instance *instance) : _instance(instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) {
|
void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) {
|
||||||
t_assert(thread == nullptr && data == nullptr);
|
Expects(thread == nullptr && data == nullptr);
|
||||||
|
|
||||||
thread = std::make_unique<Thread>();
|
thread = std::make_unique<Thread>();
|
||||||
auto newData = std::make_unique<ConnectionPrivate>(_instance, thread.get(), this, sessionData, shiftedDcId);
|
auto newData = std::make_unique<ConnectionPrivate>(_instance, thread.get(), this, sessionData, shiftedDcId);
|
||||||
|
@ -378,14 +357,14 @@ void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::kill() {
|
void Connection::kill() {
|
||||||
t_assert(data != nullptr && thread != nullptr);
|
Expects(data != nullptr && thread != nullptr);
|
||||||
data->stop();
|
data->stop();
|
||||||
data = nullptr;
|
data = nullptr;
|
||||||
thread->quit();
|
thread->quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::waitTillFinish() {
|
void Connection::waitTillFinish() {
|
||||||
t_assert(data == nullptr && thread != nullptr);
|
Expects(data == nullptr && thread != nullptr);
|
||||||
|
|
||||||
DEBUG_LOG(("Waiting for connectionThread to finish"));
|
DEBUG_LOG(("Waiting for connectionThread to finish"));
|
||||||
thread->wait();
|
thread->wait();
|
||||||
|
@ -393,19 +372,19 @@ void Connection::waitTillFinish() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 Connection::state() const {
|
int32 Connection::state() const {
|
||||||
t_assert(data != nullptr && thread != nullptr);
|
Expects(data != nullptr && thread != nullptr);
|
||||||
|
|
||||||
return data->getState();
|
return data->getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Connection::transport() const {
|
QString Connection::transport() const {
|
||||||
t_assert(data != nullptr && thread != nullptr);
|
Expects(data != nullptr && thread != nullptr);
|
||||||
|
|
||||||
return data->transport();
|
return data->transport();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection::~Connection() {
|
Connection::~Connection() {
|
||||||
t_assert(data == nullptr);
|
Expects(data == nullptr);
|
||||||
if (thread) {
|
if (thread) {
|
||||||
waitTillFinish();
|
waitTillFinish();
|
||||||
}
|
}
|
||||||
|
@ -477,10 +456,10 @@ ConnectionPrivate::ConnectionPrivate(Instance *instance, QThread *thread, Connec
|
||||||
retryTimer.moveToThread(thread);
|
retryTimer.moveToThread(thread);
|
||||||
moveToThread(thread);
|
moveToThread(thread);
|
||||||
|
|
||||||
t_assert(_shiftedDcId != 0);
|
Expects(_shiftedDcId != 0);
|
||||||
|
|
||||||
connect(thread, SIGNAL(started()), this, SLOT(socketStart()));
|
connect(thread, &QThread::started, this, [this] { connectToServer(); });
|
||||||
connect(thread, SIGNAL(finished()), this, SLOT(doFinish()));
|
connect(thread, &QThread::finished, this, [this] { finishAndDestroy(); });
|
||||||
connect(this, SIGNAL(finished(internal::Connection*)), _instance, SLOT(connectionFinished(internal::Connection*)), Qt::QueuedConnection);
|
connect(this, SIGNAL(finished(internal::Connection*)), _instance, SLOT(connectionFinished(internal::Connection*)), Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(&retryTimer, SIGNAL(timeout()), this, SLOT(retryByTimer()));
|
connect(&retryTimer, SIGNAL(timeout()), this, SLOT(retryByTimer()));
|
||||||
|
@ -515,7 +494,11 @@ ConnectionPrivate::ConnectionPrivate(Instance *instance, QThread *thread, Connec
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::onConfigLoaded() {
|
void ConnectionPrivate::onConfigLoaded() {
|
||||||
socketStart(true);
|
connectToServer(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionPrivate::onCDNConfigLoaded() {
|
||||||
|
restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 ConnectionPrivate::getShiftedDcId() const {
|
int32 ConnectionPrivate::getShiftedDcId() const {
|
||||||
|
@ -881,12 +864,14 @@ void ConnectionPrivate::tryToSend() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MTPInitConnection<mtpRequest> initWrapperImpl, *initWrapper = &initWrapperImpl;
|
MTPInitConnection<mtpRequest> initWrapper;
|
||||||
int32 initSize = 0, initSizeInInts = 0;
|
int32 initSize = 0, initSizeInInts = 0;
|
||||||
if (needsLayer) {
|
if (needsLayer) {
|
||||||
auto langCode = (cLang() == languageTest || cLang() == languageDefault) ? Sandbox::LangSystemISO() : str_const_toString(LanguageCodes[cLang()]);
|
auto langCode = (cLang() == languageTest || cLang() == languageDefault) ? Sandbox::LangSystemISO() : str_const_toString(LanguageCodes[cLang()]);
|
||||||
initWrapperImpl = MTPInitConnection<mtpRequest>(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(langCode), mtpRequest());
|
auto deviceModel = (_dcType == DcType::Cdn) ? "n/a" : cApiDeviceModel();
|
||||||
initSizeInInts = (initWrapper->innerLength() >> 2) + 2;
|
auto systemVersion = (_dcType == DcType::Cdn) ? "n/a" : cApiSystemVersion();
|
||||||
|
initWrapper = MTPInitConnection<mtpRequest>(MTP_int(ApiId), MTP_string(deviceModel), MTP_string(systemVersion), MTP_string(cApiAppVersion()), MTP_string(langCode), mtpRequest());
|
||||||
|
initSizeInInts = (initWrapper.innerLength() >> 2) + 2;
|
||||||
initSize = initSizeInInts * sizeof(mtpPrime);
|
initSize = initSizeInInts * sizeof(mtpPrime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -946,7 +931,7 @@ void ConnectionPrivate::tryToSend() {
|
||||||
memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length
|
memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length
|
||||||
wrappedRequest->push_back(mtpc_invokeWithLayer);
|
wrappedRequest->push_back(mtpc_invokeWithLayer);
|
||||||
wrappedRequest->push_back(MTP::internal::CurrentLayer);
|
wrappedRequest->push_back(MTP::internal::CurrentLayer);
|
||||||
initWrapper->write(*wrappedRequest);
|
initWrapper.write(*wrappedRequest);
|
||||||
wrappedRequest->resize(wrappedRequest->size() + noWrapSize);
|
wrappedRequest->resize(wrappedRequest->size() + noWrapSize);
|
||||||
memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime));
|
memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime));
|
||||||
toSendRequest = wrappedRequest;
|
toSendRequest = wrappedRequest;
|
||||||
|
@ -978,7 +963,7 @@ void ConnectionPrivate::tryToSend() {
|
||||||
initSerialized.reserve(initSizeInInts);
|
initSerialized.reserve(initSizeInInts);
|
||||||
initSerialized.push_back(mtpc_invokeWithLayer);
|
initSerialized.push_back(mtpc_invokeWithLayer);
|
||||||
initSerialized.push_back(MTP::internal::CurrentLayer);
|
initSerialized.push_back(MTP::internal::CurrentLayer);
|
||||||
initWrapper->write(initSerialized);
|
initWrapper.write(initSerialized);
|
||||||
}
|
}
|
||||||
toSendRequest = mtpRequestData::prepare(containerSize, containerSize + 3 * toSend.size()); // prepare container + each in invoke after
|
toSendRequest = mtpRequestData::prepare(containerSize, containerSize + 3 * toSend.size()); // prepare container + each in invoke after
|
||||||
toSendRequest->push_back(mtpc_msg_container);
|
toSendRequest->push_back(mtpc_msg_container);
|
||||||
|
@ -1082,7 +1067,7 @@ void ConnectionPrivate::retryByTimer() {
|
||||||
}
|
}
|
||||||
keyId = 0;
|
keyId = 0;
|
||||||
}
|
}
|
||||||
socketStart();
|
connectToServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::restartNow() {
|
void ConnectionPrivate::restartNow() {
|
||||||
|
@ -1091,27 +1076,31 @@ void ConnectionPrivate::restartNow() {
|
||||||
restart();
|
restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::socketStart(bool afterConfig) {
|
void ConnectionPrivate::connectToServer(bool afterConfig) {
|
||||||
if (_finished) {
|
if (_finished) {
|
||||||
DEBUG_LOG(("MTP Error: socketStart() called for finished connection!"));
|
DEBUG_LOG(("MTP Error: connectToServer() called for finished connection!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto dcType = DcOptions::DcType::Regular;
|
auto bareDc = bareDcId(_shiftedDcId);
|
||||||
auto isDownloadDc = isDownloadDcId(_shiftedDcId);
|
_dcType = Messenger::Instance().dcOptions()->dcType(_shiftedDcId);
|
||||||
if (isDownloadDc) { // using media_only addresses only if key for this dc is already created
|
if (_dcType == DcType::MediaDownload) { // using media_only addresses only if key for this dc is already created
|
||||||
QReadLocker lockFinished(&sessionDataMutex);
|
QReadLocker lockFinished(&sessionDataMutex);
|
||||||
if (!sessionData || sessionData->getKey()) {
|
if (!sessionData || !sessionData->getKey()) {
|
||||||
dcType = DcOptions::DcType::MediaDownload;
|
_dcType = DcType::Regular;
|
||||||
|
}
|
||||||
|
} else if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) {
|
||||||
|
if (!Messenger::Instance().dcOptions()->hasCDNKeysForDc(bareDc)) {
|
||||||
|
requestCDNConfig();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto bareDc = bareDcId(_shiftedDcId);
|
|
||||||
|
|
||||||
using Variants = DcOptions::Variants;
|
using Variants = DcOptions::Variants;
|
||||||
auto kIPv4 = Variants::IPv4;
|
auto kIPv4 = Variants::IPv4;
|
||||||
auto kIPv6 = Variants::IPv6;
|
auto kIPv6 = Variants::IPv6;
|
||||||
auto kTcp = Variants::Tcp;
|
auto kTcp = Variants::Tcp;
|
||||||
auto kHttp = Variants::Http;
|
auto kHttp = Variants::Http;
|
||||||
auto variants = Messenger::Instance().dcOptions()->lookup(bareDc, dcType);
|
auto variants = Messenger::Instance().dcOptions()->lookup(bareDc, _dcType);
|
||||||
auto noIPv4 = (variants.data[kIPv4][kHttp].port == 0);
|
auto noIPv4 = (variants.data[kIPv4][kHttp].port == 0);
|
||||||
auto noIPv6 = (!Global::TryIPv6() || (variants.data[kIPv6][kHttp].port == 0));
|
auto noIPv6 = (!Global::TryIPv6() || (variants.data[kIPv6][kHttp].port == 0));
|
||||||
if (noIPv4 && noIPv6) {
|
if (noIPv4 && noIPv6) {
|
||||||
|
@ -1128,7 +1117,7 @@ void ConnectionPrivate::socketStart(bool afterConfig) {
|
||||||
DEBUG_LOG(("MTP Info: DC %1 options for IPv4 over HTTP not found, waiting for config").arg(_shiftedDcId));
|
DEBUG_LOG(("MTP Info: DC %1 options for IPv4 over HTTP not found, waiting for config").arg(_shiftedDcId));
|
||||||
if (Global::TryIPv6() && noIPv6) DEBUG_LOG(("MTP Info: DC %1 options for IPv6 over HTTP not found, waiting for config").arg(_shiftedDcId));
|
if (Global::TryIPv6() && noIPv6) DEBUG_LOG(("MTP Info: DC %1 options for IPv6 over HTTP not found, waiting for config").arg(_shiftedDcId));
|
||||||
connect(_instance, SIGNAL(configLoaded()), this, SLOT(onConfigLoaded()), Qt::UniqueConnection);
|
connect(_instance, SIGNAL(configLoaded()), this, SLOT(onConfigLoaded()), Qt::UniqueConnection);
|
||||||
QMetaObject::invokeMethod(_instance, "configLoadRequest", Qt::QueuedConnection);
|
InvokeQueued(_instance, [instance = _instance] { instance->configLoadRequest(); });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1201,18 +1190,18 @@ void ConnectionPrivate::restart() {
|
||||||
|
|
||||||
void ConnectionPrivate::onSentSome(uint64 size) {
|
void ConnectionPrivate::onSentSome(uint64 size) {
|
||||||
if (!_waitForReceivedTimer.isActive()) {
|
if (!_waitForReceivedTimer.isActive()) {
|
||||||
uint64 remain = _waitForReceived;
|
auto remain = static_cast<uint64>(_waitForReceived);
|
||||||
if (!oldConnection) {
|
if (!oldConnection) {
|
||||||
uint64 remainBySize = size * _waitForReceived / 8192; // 8kb / sec, so 512 kb give 64 sec
|
auto remainBySize = size * _waitForReceived / 8192; // 8kb / sec, so 512 kb give 64 sec
|
||||||
remain = snap(remainBySize, remain, uint64(MTPMaxReceiveDelay));
|
remain = snap(remainBySize, remain, uint64(MTPMaxReceiveDelay));
|
||||||
if (remain != _waitForReceived) {
|
if (remain != _waitForReceived) {
|
||||||
DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain));
|
DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isUploadDcId(_shiftedDcId)) {
|
if (isUploadDcId(_shiftedDcId)) {
|
||||||
remain *= MTPUploadSessionsCount;
|
remain *= kUploadSessionsCount;
|
||||||
} else if (isDownloadDcId(_shiftedDcId)) {
|
} else if (isDownloadDcId(_shiftedDcId)) {
|
||||||
remain *= MTPDownloadSessionsCount;
|
remain *= kDownloadSessionsCount;
|
||||||
}
|
}
|
||||||
_waitForReceivedTimer.start(remain);
|
_waitForReceivedTimer.start(remain);
|
||||||
}
|
}
|
||||||
|
@ -1276,7 +1265,7 @@ void ConnectionPrivate::onWaitReceivedFailed() {
|
||||||
if (retryTimer.isActive()) return;
|
if (retryTimer.isActive()) return;
|
||||||
|
|
||||||
DEBUG_LOG(("MTP Info: immediate restart!"));
|
DEBUG_LOG(("MTP Info: immediate restart!"));
|
||||||
QTimer::singleShot(0, this, SLOT(socketStart()));
|
InvokeQueued(this, [this] { connectToServer(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::onWaitConnectedFailed() {
|
void ConnectionPrivate::onWaitConnectedFailed() {
|
||||||
|
@ -1287,7 +1276,7 @@ void ConnectionPrivate::onWaitConnectedFailed() {
|
||||||
restarted = true;
|
restarted = true;
|
||||||
|
|
||||||
DEBUG_LOG(("MTP Info: immediate restart!"));
|
DEBUG_LOG(("MTP Info: immediate restart!"));
|
||||||
QTimer::singleShot(0, this, SLOT(socketStart()));
|
InvokeQueued(this, [this] { connectToServer(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::onWaitIPv4Failed() {
|
void ConnectionPrivate::onWaitIPv4Failed() {
|
||||||
|
@ -1319,13 +1308,18 @@ void ConnectionPrivate::doDisconnect() {
|
||||||
restarted = false;
|
restarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::doFinish() {
|
void ConnectionPrivate::finishAndDestroy() {
|
||||||
doDisconnect();
|
doDisconnect();
|
||||||
_finished = true;
|
_finished = true;
|
||||||
emit finished(_owner);
|
emit finished(_owner);
|
||||||
deleteLater();
|
deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectionPrivate::requestCDNConfig() {
|
||||||
|
connect(_instance, SIGNAL(cdnConfigLoaded()), this, SLOT(onCDNConfigLoaded()), Qt::UniqueConnection);
|
||||||
|
InvokeQueued(_instance, [instance = _instance] { instance->cdnConfigLoadRequest(); });
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::handleReceived() {
|
void ConnectionPrivate::handleReceived() {
|
||||||
QReadLocker lockFinished(&sessionDataMutex);
|
QReadLocker lockFinished(&sessionDataMutex);
|
||||||
if (!sessionData) return;
|
if (!sessionData) return;
|
||||||
|
@ -2046,16 +2040,20 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
|
||||||
return HandleResult::ResetSession;
|
return HandleResult::ResetSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtpBuffer update(end - from);
|
if (_dcType == DcType::Regular) {
|
||||||
if (end > from) memcpy(update.data(), from, (end - from) * sizeof(mtpPrime));
|
mtpBuffer update(end - from);
|
||||||
|
if (end > from) memcpy(update.data(), from, (end - from) * sizeof(mtpPrime));
|
||||||
|
|
||||||
QWriteLocker locker(sessionData->haveReceivedMutex());
|
QWriteLocker locker(sessionData->haveReceivedMutex());
|
||||||
mtpResponseMap &haveReceived(sessionData->haveReceivedMap());
|
mtpResponseMap &haveReceived(sessionData->haveReceivedMap());
|
||||||
mtpRequestId fakeRequestId = sessionData->nextFakeRequestId();
|
mtpRequestId fakeRequestId = sessionData->nextFakeRequestId();
|
||||||
haveReceived.insert(fakeRequestId, mtpResponse(update)); // notify main process about new updates
|
haveReceived.insert(fakeRequestId, mtpResponse(update)); // notify main process about new updates
|
||||||
|
|
||||||
if (cons != mtpc_updatesTooLong && cons != mtpc_updateShortMessage && cons != mtpc_updateShortChatMessage && cons != mtpc_updateShortSentMessage && cons != mtpc_updateShort && cons != mtpc_updatesCombined && cons != mtpc_updates) {
|
if (cons != mtpc_updatesTooLong && cons != mtpc_updateShortMessage && cons != mtpc_updateShortChatMessage && cons != mtpc_updateShortSentMessage && cons != mtpc_updateShort && cons != mtpc_updatesCombined && cons != mtpc_updates) {
|
||||||
LOG(("Message Error: unknown constructor %1").arg(cons)); // maybe new api?..
|
LOG(("Message Error: unknown constructor %1").arg(cons)); // maybe new api?..
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(("Message Error: unexpected updates in dcType: %1").arg(static_cast<int>(_dcType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return HandleResult::Success;
|
return HandleResult::Success;
|
||||||
|
@ -2430,27 +2428,17 @@ void ConnectionPrivate::pqAnswered() {
|
||||||
return restart();
|
return restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
static MTP::internal::RSAPublicKeys RSAKeys = MTP::internal::InitRSAPublicKeys();
|
auto rsaKey = internal::RSAPublicKey();
|
||||||
const MTP::internal::RSAPublicKey *rsaKey = nullptr;
|
if (!Messenger::Instance().dcOptions()->getDcRSAKey(bareDcId(_shiftedDcId), res_pq.c_resPQ().vserver_public_key_fingerprints.v, &rsaKey)) {
|
||||||
auto &fingerPrints = res_pq.c_resPQ().vserver_public_key_fingerprints.v;
|
if (_dcType == DcType::Cdn) {
|
||||||
for (auto &fingerPrint : fingerPrints) {
|
LOG(("Warning: CDN public RSA key not found"));
|
||||||
auto it = RSAKeys.constFind(static_cast<uint64>(fingerPrint.v));
|
requestCDNConfig();
|
||||||
if (it != RSAKeys.cend()) {
|
return;
|
||||||
rsaKey = &it.value();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
LOG(("AuthKey Error: could not choose public RSA key"));
|
||||||
if (!rsaKey) {
|
|
||||||
QStringList suggested, my;
|
|
||||||
for (auto &fingerPrint : fingerPrints) {
|
|
||||||
suggested.push_back(QString("%1").arg(fingerPrint.v));
|
|
||||||
}
|
|
||||||
for (auto i = RSAKeys.cbegin(), e = RSAKeys.cend(); i != e; ++i) {
|
|
||||||
my.push_back(QString("%1").arg(i.key()));
|
|
||||||
}
|
|
||||||
LOG(("AuthKey Error: could not choose public RSA key, suggested fingerprints: %1, my fingerprints: %2").arg(suggested.join(", ")).arg(my.join(", ")));
|
|
||||||
return restart();
|
return restart();
|
||||||
}
|
}
|
||||||
|
t_assert(rsaKey.isValid());
|
||||||
|
|
||||||
_authKeyData->server_nonce = res_pq_data.vserver_nonce;
|
_authKeyData->server_nonce = res_pq_data.vserver_nonce;
|
||||||
_authKeyData->new_nonce = rand_value<MTPint256>();
|
_authKeyData->new_nonce = rand_value<MTPint256>();
|
||||||
|
@ -2477,14 +2465,14 @@ void ConnectionPrivate::pqAnswered() {
|
||||||
MTPReq_DH_params req_DH_params;
|
MTPReq_DH_params req_DH_params;
|
||||||
req_DH_params.vnonce = _authKeyData->nonce;
|
req_DH_params.vnonce = _authKeyData->nonce;
|
||||||
req_DH_params.vserver_nonce = _authKeyData->server_nonce;
|
req_DH_params.vserver_nonce = _authKeyData->server_nonce;
|
||||||
req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey->getFingerPrint());
|
req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey.getFingerPrint());
|
||||||
req_DH_params.vp = p_q_inner.c_p_q_inner_data().vp;
|
req_DH_params.vp = p_q_inner.c_p_q_inner_data().vp;
|
||||||
req_DH_params.vq = p_q_inner.c_p_q_inner_data().vq;
|
req_DH_params.vq = p_q_inner.c_p_q_inner_data().vq;
|
||||||
req_DH_params.vencrypted_data = MTP_string(std::move(dhEncString));
|
req_DH_params.vencrypted_data = MTP_string(std::move(dhEncString));
|
||||||
sendRequestNotSecure(req_DH_params);
|
sendRequestNotSecure(req_DH_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey *key) {
|
std::string ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key) {
|
||||||
auto p_q_inner_size = data.innerLength();
|
auto p_q_inner_size = data.innerLength();
|
||||||
auto encSize = (p_q_inner_size >> 2) + 6;
|
auto encSize = (p_q_inner_size >> 2) + 6;
|
||||||
if (encSize >= 65) {
|
if (encSize >= 65) {
|
||||||
|
@ -2509,7 +2497,7 @@ std::string ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data,
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dhEncString = std::string();
|
auto dhEncString = std::string();
|
||||||
if (!key->encrypt(reinterpret_cast<const char*>(&encBuffer[0]) + 3, dhEncString)) {
|
if (!key.encrypt(reinterpret_cast<const char*>(&encBuffer[0]) + 3, dhEncString)) {
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
return dhEncString;
|
return dhEncString;
|
||||||
|
@ -2872,17 +2860,7 @@ void ConnectionPrivate::onError4(qint32 errorCode) {
|
||||||
LOG(("Protocol Error: -429 flood code returned!"));
|
LOG(("Protocol Error: -429 flood code returned!"));
|
||||||
}
|
}
|
||||||
if (_conn || !_conn6) {
|
if (_conn || !_conn6) {
|
||||||
destroyConn();
|
handleError(errorCode);
|
||||||
_waitForConnectedTimer.stop();
|
|
||||||
|
|
||||||
if (errorCode == -404 && _instance->isKeysDestroyer()) {
|
|
||||||
LOG(("MTP Info: -404 error received on destroying key %1, assuming it is destroyed.").arg(_shiftedDcId));
|
|
||||||
emit _instance->keyDestroyed(_shiftedDcId);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
MTP_LOG(_shiftedDcId, ("Restarting after error in IPv4 connection, error code: %1...").arg(errorCode));
|
|
||||||
return restart();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
destroyConn(&_conn4);
|
destroyConn(&_conn4);
|
||||||
}
|
}
|
||||||
|
@ -2895,22 +2873,32 @@ void ConnectionPrivate::onError6(qint32 errorCode) {
|
||||||
LOG(("Protocol Error: -429 flood code returned!"));
|
LOG(("Protocol Error: -429 flood code returned!"));
|
||||||
}
|
}
|
||||||
if (_conn || !_conn4) {
|
if (_conn || !_conn4) {
|
||||||
destroyConn();
|
handleError(errorCode);
|
||||||
_waitForConnectedTimer.stop();
|
|
||||||
|
|
||||||
if (errorCode == -404 && _instance->isKeysDestroyer()) {
|
|
||||||
LOG(("MTP Info: -404 error received on destroying key %1, assuming it is destroyed.").arg(_shiftedDcId));
|
|
||||||
emit _instance->keyDestroyed(_shiftedDcId);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
MTP_LOG(_shiftedDcId, ("Restarting after error in IPv6 connection, error code: %1...").arg(errorCode));
|
|
||||||
return restart();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
destroyConn(&_conn6);
|
destroyConn(&_conn6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectionPrivate::handleError(int errorCode) {
|
||||||
|
destroyConn();
|
||||||
|
_waitForConnectedTimer.stop();
|
||||||
|
|
||||||
|
if (errorCode == -404) {
|
||||||
|
if (_instance->isKeysDestroyer()) {
|
||||||
|
LOG(("MTP Info: -404 error received on destroying key %1, assuming it is destroyed.").arg(_shiftedDcId));
|
||||||
|
emit _instance->keyDestroyed(_shiftedDcId);
|
||||||
|
return;
|
||||||
|
} else if (_dcType == DcType::Cdn) {
|
||||||
|
LOG(("MTP Info: -404 error received in CDN dc %1, assuming it was destroyed, recreating.").arg(_shiftedDcId));
|
||||||
|
clearMessages();
|
||||||
|
keyId = kRecreateKeyId;
|
||||||
|
return restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MTP_LOG(_shiftedDcId, ("Restarting after error in connection, error code: %1...").arg(errorCode));
|
||||||
|
return restart();
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::onReadyData() {
|
void ConnectionPrivate::onReadyData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "mtproto/core_types.h"
|
#include "mtproto/core_types.h"
|
||||||
#include "mtproto/auth_key.h"
|
#include "mtproto/auth_key.h"
|
||||||
|
#include "mtproto/dc_options.h"
|
||||||
#include "core/single_timer.h"
|
#include "core/single_timer.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
@ -128,7 +129,6 @@ public slots:
|
||||||
void onReceivedSome();
|
void onReceivedSome();
|
||||||
|
|
||||||
void onReadyData();
|
void onReadyData();
|
||||||
void socketStart(bool afterConfig = false);
|
|
||||||
|
|
||||||
void onConnected4();
|
void onConnected4();
|
||||||
void onConnected6();
|
void onConnected6();
|
||||||
|
@ -137,8 +137,6 @@ public slots:
|
||||||
void onError4(qint32 errorCode);
|
void onError4(qint32 errorCode);
|
||||||
void onError6(qint32 errorCode);
|
void onError6(qint32 errorCode);
|
||||||
|
|
||||||
void doFinish();
|
|
||||||
|
|
||||||
// Auth key creation packet receive slots
|
// Auth key creation packet receive slots
|
||||||
void pqAnswered();
|
void pqAnswered();
|
||||||
void dhParamsAnswered();
|
void dhParamsAnswered();
|
||||||
|
@ -153,10 +151,15 @@ public slots:
|
||||||
void updateAuthKey();
|
void updateAuthKey();
|
||||||
|
|
||||||
void onConfigLoaded();
|
void onConfigLoaded();
|
||||||
|
void onCDNConfigLoaded();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void connectToServer(bool afterConfig = false);
|
||||||
void doDisconnect();
|
void doDisconnect();
|
||||||
void restart();
|
void restart();
|
||||||
|
void finishAndDestroy();
|
||||||
|
void requestCDNConfig();
|
||||||
|
void handleError(int errorCode);
|
||||||
|
|
||||||
void createConn(bool createIPv4, bool createIPv6);
|
void createConn(bool createIPv4, bool createIPv6);
|
||||||
void destroyConn(AbstractConnection **conn = 0); // 0 - destory all
|
void destroyConn(AbstractConnection **conn = 0); // 0 - destory all
|
||||||
|
@ -182,10 +185,11 @@ private:
|
||||||
|
|
||||||
bool setState(int32 state, int32 ifState = Connection::UpdateAlways);
|
bool setState(int32 state, int32 ifState = Connection::UpdateAlways);
|
||||||
|
|
||||||
std::string encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey *key);
|
std::string encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key);
|
||||||
std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data);
|
std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data);
|
||||||
|
|
||||||
Instance *_instance = nullptr;
|
Instance *_instance = nullptr;
|
||||||
|
DcType _dcType = DcType::Regular;
|
||||||
|
|
||||||
mutable QReadWriteLock stateConnMutex;
|
mutable QReadWriteLock stateConnMutex;
|
||||||
int32 _state = DisconnectedState;
|
int32 _state = DisconnectedState;
|
||||||
|
|
|
@ -20,12 +20,55 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#include "mtproto/dc_options.h"
|
#include "mtproto/dc_options.h"
|
||||||
|
|
||||||
|
#include "storage/serialize_common.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
|
class DcOptions::WriteLocker {
|
||||||
|
public:
|
||||||
|
WriteLocker(DcOptions *that) : _that(that), _lock(&_that->_useThroughLockers) {
|
||||||
|
}
|
||||||
|
~WriteLocker() {
|
||||||
|
_that->computeCdnDcIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
gsl::not_null<DcOptions*> _that;
|
||||||
|
QWriteLocker _lock;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class DcOptions::ReadLocker {
|
||||||
|
public:
|
||||||
|
ReadLocker(const DcOptions *that) : _lock(&that->_useThroughLockers) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QReadLocker _lock;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void DcOptions::readBuiltInPublicKeys() {
|
||||||
|
auto keysCount = 0;
|
||||||
|
auto keys = cPublicRSAKeys(keysCount);
|
||||||
|
for (auto i = 0; i != keysCount; ++i) {
|
||||||
|
auto keyBytes = gsl::as_bytes(gsl::make_span(keys[i], keys[i] + strlen(keys[i])));
|
||||||
|
auto key = internal::RSAPublicKey(keyBytes);
|
||||||
|
if (key.isValid()) {
|
||||||
|
_publicKeys.emplace(key.getFingerPrint(), std::move(key));
|
||||||
|
} else {
|
||||||
|
LOG(("MTP Error: could not read this public RSA key:"));
|
||||||
|
LOG((keys[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DcOptions::constructFromBuiltIn() {
|
void DcOptions::constructFromBuiltIn() {
|
||||||
QWriteLocker lock(&_mutex);
|
WriteLocker lock(this);
|
||||||
_data.clear();
|
_data.clear();
|
||||||
|
|
||||||
|
readBuiltInPublicKeys();
|
||||||
|
|
||||||
auto bdcs = builtInDcs();
|
auto bdcs = builtInDcs();
|
||||||
for (auto i = 0, l = builtInDcsCount(); i != l; ++i) {
|
for (auto i = 0, l = builtInDcsCount(); i != l; ++i) {
|
||||||
auto flags = MTPDdcOption::Flags(0);
|
auto flags = MTPDdcOption::Flags(0);
|
||||||
|
@ -53,7 +96,7 @@ void DcOptions::processFromList(const QVector<MTPDcOption> &options, bool overwr
|
||||||
auto shiftedIdsProcessed = std::vector<ShiftedDcId>();
|
auto shiftedIdsProcessed = std::vector<ShiftedDcId>();
|
||||||
shiftedIdsProcessed.reserve(options.size());
|
shiftedIdsProcessed.reserve(options.size());
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_mutex);
|
WriteLocker lock(this);
|
||||||
if (overwrite) {
|
if (overwrite) {
|
||||||
idsChanged.reserve(_data.size());
|
idsChanged.reserve(_data.size());
|
||||||
}
|
}
|
||||||
|
@ -107,22 +150,22 @@ void DcOptions::addFromList(const MTPVector<MTPDcOption> &options) {
|
||||||
processFromList(options.v, false);
|
processFromList(options.v, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DcOptions::addFromOther(const DcOptions &options) {
|
void DcOptions::addFromOther(DcOptions &&options) {
|
||||||
if (this == &options || _immutable) {
|
if (this == &options || _immutable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto idsChanged = std::vector<DcId>();
|
auto idsChanged = std::vector<DcId>();
|
||||||
{
|
{
|
||||||
QReadLocker lock(&options._mutex);
|
ReadLocker lock(&options);
|
||||||
if (options._data.empty()) {
|
if (options._data.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
idsChanged.reserve(options._data.size());
|
idsChanged.reserve(options._data.size());
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_mutex);
|
WriteLocker lock(this);
|
||||||
for (auto &item : options._data) {
|
for (auto &item : base::take(options._data)) {
|
||||||
auto dcId = item.second.id;
|
auto dcId = item.second.id;
|
||||||
auto flags = item.second.flags;
|
auto flags = item.second.flags;
|
||||||
auto &ip = item.second.ip;
|
auto &ip = item.second.ip;
|
||||||
|
@ -133,6 +176,11 @@ void DcOptions::addFromOther(const DcOptions &options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto &keysForDc : options._cdnPublicKeys) {
|
||||||
|
for (auto &entry : keysForDc.second) {
|
||||||
|
_cdnPublicKeys[keysForDc.first].insert(std::move(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +190,7 @@ void DcOptions::addFromOther(const DcOptions &options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DcOptions::constructAddOne(int id, MTPDdcOption::Flags flags, const std::string &ip, int port) {
|
void DcOptions::constructAddOne(int id, MTPDdcOption::Flags flags, const std::string &ip, int port) {
|
||||||
QWriteLocker lock(&_mutex);
|
WriteLocker lock(this);
|
||||||
applyOneGuarded(bareDcId(id), flags, ip, port);
|
applyOneGuarded(bareDcId(id), flags, ip, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +215,7 @@ QByteArray DcOptions::serialize() const {
|
||||||
return DcOptions().serialize();
|
return DcOptions().serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
QReadLocker lock(&_mutex);
|
ReadLocker lock(this);
|
||||||
|
|
||||||
auto size = sizeof(qint32);
|
auto size = sizeof(qint32);
|
||||||
for (auto &item : _data) {
|
for (auto &item : _data) {
|
||||||
|
@ -175,6 +223,24 @@ QByteArray DcOptions::serialize() const {
|
||||||
size += sizeof(qint32) + item.second.ip.size();
|
size += sizeof(qint32) + item.second.ip.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto count = 0;
|
||||||
|
for (auto &keysInDc : _cdnPublicKeys) {
|
||||||
|
count += keysInDc.second.size();
|
||||||
|
}
|
||||||
|
struct SerializedPublicKey {
|
||||||
|
DcId dcId;
|
||||||
|
QByteArray n;
|
||||||
|
QByteArray e;
|
||||||
|
};
|
||||||
|
std::vector<SerializedPublicKey> publicKeys;
|
||||||
|
publicKeys.reserve(count);
|
||||||
|
for (auto &keysInDc : _cdnPublicKeys) {
|
||||||
|
for (auto &entry : keysInDc.second) {
|
||||||
|
publicKeys.push_back({ keysInDc.first, entry.second.getN(), entry.second.getE() });
|
||||||
|
size += sizeof(qint32) + Serialize::bytearraySize(publicKeys.back().n) + Serialize::bytearraySize(publicKeys.back().e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto result = QByteArray();
|
auto result = QByteArray();
|
||||||
result.reserve(size);
|
result.reserve(size);
|
||||||
{
|
{
|
||||||
|
@ -192,6 +258,10 @@ QByteArray DcOptions::serialize() const {
|
||||||
stream << qint32(item.second.ip.size());
|
stream << qint32(item.second.ip.size());
|
||||||
stream.writeRawData(item.second.ip.data(), item.second.ip.size());
|
stream.writeRawData(item.second.ip.data(), item.second.ip.size());
|
||||||
}
|
}
|
||||||
|
stream << qint32(publicKeys.size());
|
||||||
|
for (auto &key : publicKeys) {
|
||||||
|
stream << qint32(key.dcId) << key.n << key.e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -205,14 +275,14 @@ void DcOptions::constructFromSerialized(const QByteArray &serialized) {
|
||||||
}
|
}
|
||||||
QDataStream stream(&buffer);
|
QDataStream stream(&buffer);
|
||||||
stream.setVersion(QDataStream::Qt_5_1);
|
stream.setVersion(QDataStream::Qt_5_1);
|
||||||
qint32 count = 0;
|
auto count = qint32(0);
|
||||||
stream >> count;
|
stream >> count;
|
||||||
if (stream.status() != QDataStream::Ok) {
|
if (stream.status() != QDataStream::Ok) {
|
||||||
LOG(("MTP Error: Bad data for DcOptions::constructFromSerialized()"));
|
LOG(("MTP Error: Bad data for DcOptions::constructFromSerialized()"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWriteLocker lock(&_mutex);
|
WriteLocker lock(this);
|
||||||
_data.clear();
|
_data.clear();
|
||||||
for (auto i = 0; i != count; ++i) {
|
for (auto i = 0; i != count; ++i) {
|
||||||
qint32 id = 0, flags = 0, port = 0, ipSize = 0;
|
qint32 id = 0, flags = 0, port = 0, ipSize = 0;
|
||||||
|
@ -227,15 +297,42 @@ void DcOptions::constructFromSerialized(const QByteArray &serialized) {
|
||||||
|
|
||||||
applyOneGuarded(DcId(id), MTPDdcOption::Flags(flags), ip, port);
|
applyOneGuarded(DcId(id), MTPDdcOption::Flags(flags), ip, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read CDN config
|
||||||
|
if (!stream.atEnd()) {
|
||||||
|
auto count = qint32(0);
|
||||||
|
stream >> count;
|
||||||
|
if (stream.status() != QDataStream::Ok) {
|
||||||
|
LOG(("MTP Error: Bad data for CDN config in DcOptions::constructFromSerialized()"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i != count; ++i) {
|
||||||
|
qint32 dcId = 0;
|
||||||
|
QByteArray n, e;
|
||||||
|
stream >> dcId >> n >> e;
|
||||||
|
if (stream.status() != QDataStream::Ok) {
|
||||||
|
LOG(("MTP Error: Bad data for CDN config inside DcOptions::constructFromSerialized()"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto key = internal::RSAPublicKey(n, e);
|
||||||
|
if (key.isValid()) {
|
||||||
|
_cdnPublicKeys[dcId].emplace(key.getFingerPrint(), std::move(key));
|
||||||
|
} else {
|
||||||
|
LOG(("MTP Error: Could not read valid CDN public key."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DcOptions::Ids DcOptions::sortedDcIds() const {
|
DcOptions::Ids DcOptions::configEnumDcIds() const {
|
||||||
auto result = Ids();
|
auto result = Ids();
|
||||||
{
|
{
|
||||||
QReadLocker lock(&_mutex);
|
ReadLocker lock(this);
|
||||||
result.reserve(_data.size());
|
result.reserve(_data.size());
|
||||||
for (auto &item : _data) {
|
for (auto &item : _data) {
|
||||||
if (!base::contains(result, item.second.id)) {
|
if (!isCdnDc(item.second.flags) && !base::contains(result, item.second.id)) {
|
||||||
result.push_back(item.second.id);
|
result.push_back(item.second.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,50 +341,171 @@ DcOptions::Ids DcOptions::sortedDcIds() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DcId DcOptions::getDefaultDcId() const {
|
DcType DcOptions::dcType(ShiftedDcId shiftedDcId) const {
|
||||||
auto result = sortedDcIds();
|
ReadLocker lock(this);
|
||||||
t_assert(!result.empty());
|
if (_cdnDcIds.find(bareDcId(shiftedDcId)) != _cdnDcIds.cend()) {
|
||||||
|
return DcType::Cdn;
|
||||||
|
}
|
||||||
|
if (isDownloadDcId(shiftedDcId)) {
|
||||||
|
return DcType::MediaDownload;
|
||||||
|
}
|
||||||
|
return DcType::Regular;
|
||||||
|
}
|
||||||
|
|
||||||
return result[0];
|
void DcOptions::setCDNConfig(const MTPDcdnConfig &config) {
|
||||||
|
WriteLocker lock(this);
|
||||||
|
_cdnPublicKeys.clear();
|
||||||
|
for_const (auto &publicKey, config.vpublic_keys.v) {
|
||||||
|
Expects(publicKey.type() == mtpc_cdnPublicKey);
|
||||||
|
auto &keyData = publicKey.c_cdnPublicKey();
|
||||||
|
auto keyBytes = gsl::as_bytes(gsl::make_span(keyData.vpublic_key.v));
|
||||||
|
auto key = internal::RSAPublicKey(keyBytes);
|
||||||
|
if (key.isValid()) {
|
||||||
|
_cdnPublicKeys[keyData.vdc_id.v].emplace(key.getFingerPrint(), std::move(key));
|
||||||
|
} else {
|
||||||
|
LOG(("MTP Error: could not read this public RSA key:"));
|
||||||
|
LOG((qs(keyData.vpublic_key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DcOptions::hasCDNKeysForDc(DcId dcId) const {
|
||||||
|
ReadLocker lock(this);
|
||||||
|
return _cdnPublicKeys.find(dcId) != _cdnPublicKeys.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DcOptions::getDcRSAKey(DcId dcId, const QVector<MTPlong> &fingerprints, internal::RSAPublicKey *result) const {
|
||||||
|
auto findKey = [&fingerprints, &result](const std::map<uint64, internal::RSAPublicKey> &keys) {
|
||||||
|
for_const (auto &fingerprint, fingerprints) {
|
||||||
|
auto it = keys.find(static_cast<uint64>(fingerprint.v));
|
||||||
|
if (it != keys.cend()) {
|
||||||
|
*result = it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
{
|
||||||
|
ReadLocker lock(this);
|
||||||
|
auto it = _cdnPublicKeys.find(dcId);
|
||||||
|
if (it != _cdnPublicKeys.cend()) {
|
||||||
|
return findKey(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return findKey(_publicKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const {
|
DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const {
|
||||||
auto isMediaDownload = (type == DcType::MediaDownload);
|
auto lookupDesiredFlags = [type](int address, int protocol) -> std::vector<MTPDdcOption::Flags> {
|
||||||
int shifts[2][2][4] = {
|
switch (type) {
|
||||||
{ // IPv4
|
case DcType::Regular: {
|
||||||
{ // TCP IPv4
|
switch (address) {
|
||||||
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only) : -1,
|
case Variants::IPv4: {
|
||||||
qFlags(MTPDdcOption::Flag::f_tcpo_only),
|
switch (protocol) {
|
||||||
isMediaDownload ? qFlags(MTPDdcOption::Flag::f_media_only) : -1,
|
case Variants::Tcp: return {
|
||||||
0
|
// Regular TCP IPv4
|
||||||
}, { // HTTP IPv4
|
qFlags(MTPDdcOption::Flag::f_tcpo_only),
|
||||||
-1,
|
MTPDdcOption::Flags(0)
|
||||||
-1,
|
};
|
||||||
isMediaDownload ? qFlags(MTPDdcOption::Flag::f_media_only) : -1,
|
case Variants::Http: return {
|
||||||
0
|
// Regular HTTP IPv4
|
||||||
},
|
MTPDdcOption::Flags(0),
|
||||||
}, { // IPv6
|
};
|
||||||
{ // TCP IPv6
|
}
|
||||||
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6) : -1,
|
} break;
|
||||||
MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6,
|
case Variants::IPv6: {
|
||||||
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1,
|
switch (protocol) {
|
||||||
qFlags(MTPDdcOption::Flag::f_ipv6)
|
case Variants::Tcp: return {
|
||||||
}, { // HTTP IPv6
|
// Regular TCP IPv6
|
||||||
-1,
|
(MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6),
|
||||||
-1,
|
qFlags(MTPDdcOption::Flag::f_ipv6),
|
||||||
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1,
|
};
|
||||||
qFlags(MTPDdcOption::Flag::f_ipv6)
|
case Variants::Http: return {
|
||||||
},
|
// Regular HTTP IPv6
|
||||||
},
|
qFlags(MTPDdcOption::Flag::f_ipv6),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case DcType::MediaDownload: {
|
||||||
|
switch (address) {
|
||||||
|
case Variants::IPv4: {
|
||||||
|
switch (protocol) {
|
||||||
|
case Variants::Tcp: return {
|
||||||
|
// Media download TCP IPv4
|
||||||
|
(MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only),
|
||||||
|
qFlags(MTPDdcOption::Flag::f_tcpo_only),
|
||||||
|
qFlags(MTPDdcOption::Flag::f_media_only),
|
||||||
|
MTPDdcOption::Flags(0),
|
||||||
|
};
|
||||||
|
case Variants::Http: return {
|
||||||
|
// Media download HTTP IPv4
|
||||||
|
qFlags(MTPDdcOption::Flag::f_media_only),
|
||||||
|
MTPDdcOption::Flags(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Variants::IPv6: {
|
||||||
|
switch (protocol) {
|
||||||
|
case Variants::Tcp: return {
|
||||||
|
// Media download TCP IPv6
|
||||||
|
(MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6),
|
||||||
|
(MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6),
|
||||||
|
(MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6),
|
||||||
|
qFlags(MTPDdcOption::Flag::f_ipv6)
|
||||||
|
};
|
||||||
|
case Variants::Http: return {
|
||||||
|
// Media download HTTP IPv6
|
||||||
|
(MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6),
|
||||||
|
qFlags(MTPDdcOption::Flag::f_ipv6),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case DcType::Cdn: {
|
||||||
|
switch (address) {
|
||||||
|
case Variants::IPv4: {
|
||||||
|
switch (protocol) {
|
||||||
|
case Variants::Tcp: return {
|
||||||
|
// CDN TCP IPv4
|
||||||
|
(MTPDdcOption::Flag::f_cdn | MTPDdcOption::Flag::f_tcpo_only),
|
||||||
|
qFlags(MTPDdcOption::Flag::f_cdn),
|
||||||
|
};
|
||||||
|
case Variants::Http: return {
|
||||||
|
// CDN HTTP IPv4
|
||||||
|
qFlags(MTPDdcOption::Flag::f_cdn),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Variants::IPv6: {
|
||||||
|
switch (protocol) {
|
||||||
|
case Variants::Tcp: return {
|
||||||
|
// CDN TCP IPv6
|
||||||
|
(MTPDdcOption::Flag::f_cdn | MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6),
|
||||||
|
(MTPDdcOption::Flag::f_cdn | MTPDdcOption::Flag::f_ipv6),
|
||||||
|
};
|
||||||
|
case Variants::Http: return {
|
||||||
|
// CDN HTTP IPv6
|
||||||
|
(MTPDdcOption::Flag::f_cdn | MTPDdcOption::Flag::f_ipv6),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
Unexpected("Bad type / address / protocol");
|
||||||
};
|
};
|
||||||
|
|
||||||
auto result = Variants();
|
auto result = Variants();
|
||||||
{
|
{
|
||||||
QReadLocker lock(&_mutex);
|
ReadLocker lock(this);
|
||||||
for (auto address = 0; address != Variants::AddressTypeCount; ++address) {
|
for (auto address = 0; address != Variants::AddressTypeCount; ++address) {
|
||||||
for (auto protocol = 0; protocol != Variants::ProtocolCount; ++protocol) {
|
for (auto protocol = 0; protocol != Variants::ProtocolCount; ++protocol) {
|
||||||
for (auto variant = 0; variant != base::array_size(shifts[address][protocol]); ++variant) {
|
auto desiredFlags = lookupDesiredFlags(address, protocol);
|
||||||
auto shift = shifts[address][protocol][variant];
|
for (auto flags : desiredFlags) {
|
||||||
|
auto shift = static_cast<int>(flags);
|
||||||
if (shift < 0) continue;
|
if (shift < 0) continue;
|
||||||
|
|
||||||
auto it = _data.find(shiftDcId(dcId, shift));
|
auto it = _data.find(shiftDcId(dcId, shift));
|
||||||
|
@ -304,6 +522,15 @@ DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DcOptions::computeCdnDcIds() {
|
||||||
|
_cdnDcIds.clear();
|
||||||
|
for (auto &item : _data) {
|
||||||
|
if (item.second.flags & MTPDdcOption::Flag::f_cdn) {
|
||||||
|
_cdnDcIds.insert(item.second.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool DcOptions::loadFromFile(const QString &path) {
|
bool DcOptions::loadFromFile(const QString &path) {
|
||||||
QVector<MTPDcOption> options;
|
QVector<MTPDcOption> options;
|
||||||
|
|
||||||
|
@ -372,7 +599,7 @@ bool DcOptions::writeToFile(const QString &path) const {
|
||||||
QTextStream stream(&f);
|
QTextStream stream(&f);
|
||||||
stream.setCodec("UTF-8");
|
stream.setCodec("UTF-8");
|
||||||
|
|
||||||
QReadLocker lock(&_mutex);
|
ReadLocker lock(this);
|
||||||
for (auto &item : _data) {
|
for (auto &item : _data) {
|
||||||
auto &endpoint = item.second;
|
auto &endpoint = item.second;
|
||||||
stream << endpoint.id << ' ' << QString::fromStdString(endpoint.ip) << ' ' << endpoint.port;
|
stream << endpoint.id << ' ' << QString::fromStdString(endpoint.ip) << ' ' << endpoint.port;
|
||||||
|
|
|
@ -21,12 +21,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/observer.h"
|
#include "base/observer.h"
|
||||||
|
#include "mtproto/rsa_public_key.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
|
enum class DcType {
|
||||||
|
Regular,
|
||||||
|
MediaDownload,
|
||||||
|
Cdn,
|
||||||
|
};
|
||||||
class DcOptions {
|
class DcOptions {
|
||||||
public:
|
public:
|
||||||
// construct methods don't notify "changed" subscribers.
|
// construct methods don't notify "changed" subscribers.
|
||||||
|
@ -41,10 +47,9 @@ public:
|
||||||
}
|
}
|
||||||
void setFromList(const MTPVector<MTPDcOption> &options);
|
void setFromList(const MTPVector<MTPDcOption> &options);
|
||||||
void addFromList(const MTPVector<MTPDcOption> &options);
|
void addFromList(const MTPVector<MTPDcOption> &options);
|
||||||
void addFromOther(const DcOptions &options);
|
void addFromOther(DcOptions &&options);
|
||||||
|
|
||||||
Ids sortedDcIds() const;
|
Ids configEnumDcIds() const;
|
||||||
DcId getDefaultDcId() const;
|
|
||||||
|
|
||||||
struct Endpoint {
|
struct Endpoint {
|
||||||
std::string ip;
|
std::string ip;
|
||||||
|
@ -64,11 +69,12 @@ public:
|
||||||
};
|
};
|
||||||
Endpoint data[AddressTypeCount][ProtocolCount];
|
Endpoint data[AddressTypeCount][ProtocolCount];
|
||||||
};
|
};
|
||||||
enum class DcType {
|
|
||||||
Regular,
|
|
||||||
MediaDownload,
|
|
||||||
};
|
|
||||||
Variants lookup(DcId dcId, DcType type) const;
|
Variants lookup(DcId dcId, DcType type) const;
|
||||||
|
DcType dcType(ShiftedDcId shiftedDcId) const;
|
||||||
|
|
||||||
|
void setCDNConfig(const MTPDcdnConfig &config);
|
||||||
|
bool hasCDNKeysForDc(DcId dcId) const;
|
||||||
|
bool getDcRSAKey(DcId dcId, const QVector<MTPlong> &fingerprints, internal::RSAPublicKey *result) const;
|
||||||
|
|
||||||
// Debug feature for now.
|
// Debug feature for now.
|
||||||
bool loadFromFile(const QString &path);
|
bool loadFromFile(const QString &path);
|
||||||
|
@ -87,9 +93,21 @@ private:
|
||||||
bool applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std::string &ip, int port);
|
bool applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std::string &ip, int port);
|
||||||
|
|
||||||
void processFromList(const QVector<MTPDcOption> &options, bool overwrite);
|
void processFromList(const QVector<MTPDcOption> &options, bool overwrite);
|
||||||
|
void computeCdnDcIds();
|
||||||
|
|
||||||
std::map<int, Option> _data;
|
void readBuiltInPublicKeys();
|
||||||
mutable QReadWriteLock _mutex;
|
|
||||||
|
class WriteLocker;
|
||||||
|
friend class WriteLocker;
|
||||||
|
|
||||||
|
class ReadLocker;
|
||||||
|
friend class ReadLocker;
|
||||||
|
|
||||||
|
std::map<ShiftedDcId, Option> _data;
|
||||||
|
std::set<DcId> _cdnDcIds;
|
||||||
|
std::map<uint64, internal::RSAPublicKey> _publicKeys;
|
||||||
|
std::map<DcId, std::map<uint64, internal::RSAPublicKey>> _cdnPublicKeys;
|
||||||
|
mutable QReadWriteLock _useThroughLockers;
|
||||||
|
|
||||||
mutable base::Observable<Ids> _changed;
|
mutable base::Observable<Ids> _changed;
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ void ConfigLoader::load() {
|
||||||
sendRequest(_instance->mainDcId());
|
sendRequest(_instance->mainDcId());
|
||||||
_enumDCTimer.start(kEnumerateDcTimeout);
|
_enumDCTimer.start(kEnumerateDcTimeout);
|
||||||
} else {
|
} else {
|
||||||
auto ids = _instance->dcOptions()->sortedDcIds();
|
auto ids = _instance->dcOptions()->configEnumDcIds();
|
||||||
t_assert(!ids.empty());
|
t_assert(!ids.empty());
|
||||||
_enumCurrent = ids.front();
|
_enumCurrent = ids.front();
|
||||||
enumDC();
|
enumDC();
|
||||||
|
@ -108,7 +108,7 @@ void ConfigLoader::enumDC() {
|
||||||
} else {
|
} else {
|
||||||
_instance->killSession(MTP::configDcId(_enumCurrent));
|
_instance->killSession(MTP::configDcId(_enumCurrent));
|
||||||
}
|
}
|
||||||
auto ids = _instance->dcOptions()->sortedDcIds();
|
auto ids = _instance->dcOptions()->configEnumDcIds();
|
||||||
t_assert(!ids.empty());
|
t_assert(!ids.empty());
|
||||||
|
|
||||||
auto i = std::find(ids.cbegin(), ids.cend(), _enumCurrent);
|
auto i = std::find(ids.cbegin(), ids.cend(), _enumCurrent);
|
||||||
|
|
|
@ -86,10 +86,13 @@ constexpr ShiftedDcId logoutDcId(DcId dcId) {
|
||||||
return shiftDcId(dcId, internal::kLogoutDcShift);
|
return shiftDcId(dcId, internal::kLogoutDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr auto kDownloadSessionsCount = 2;
|
||||||
|
constexpr auto kUploadSessionsCount = 2;
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
constexpr ShiftedDcId downloadDcId(DcId dcId, int index) {
|
constexpr ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||||
static_assert(MTPDownloadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!");
|
static_assert(kDownloadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!");
|
||||||
return shiftDcId(dcId, internal::kBaseDownloadDcShift + index);
|
return shiftDcId(dcId, internal::kBaseDownloadDcShift + index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,18 +100,22 @@ constexpr ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||||
|
|
||||||
// send(req, callbacks, MTP::downloadDcId(dc, index)) - for download shifted dc id
|
// send(req, callbacks, MTP::downloadDcId(dc, index)) - for download shifted dc id
|
||||||
inline ShiftedDcId downloadDcId(DcId dcId, int index) {
|
inline ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||||
t_assert(index >= 0 && index < MTPDownloadSessionsCount);
|
Expects(index >= 0 && index < kDownloadSessionsCount);
|
||||||
return internal::downloadDcId(dcId, index);
|
return internal::downloadDcId(dcId, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) {
|
inline constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) {
|
||||||
return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, MTPDownloadSessionsCount - 1) + internal::kDcShift);
|
return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, kDownloadSessionsCount - 1) + internal::kDcShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isCdnDc(MTPDdcOption::Flags flags) {
|
||||||
|
return (flags & MTPDdcOption::Flag::f_cdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
||||||
static_assert(MTPUploadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPUploadSessionsCount!");
|
static_assert(kUploadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPUploadSessionsCount!");
|
||||||
return shiftDcId(dcId, internal::kBaseUploadDcShift + index);
|
return shiftDcId(dcId, internal::kBaseUploadDcShift + index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -117,12 +124,12 @@ constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
||||||
// send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id
|
// send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id
|
||||||
// uploading always to the main dc so bareDcId == 0
|
// uploading always to the main dc so bareDcId == 0
|
||||||
inline ShiftedDcId uploadDcId(int index) {
|
inline ShiftedDcId uploadDcId(int index) {
|
||||||
t_assert(index >= 0 && index < MTPUploadSessionsCount);
|
Expects(index >= 0 && index < kUploadSessionsCount);
|
||||||
return internal::uploadDcId(0, index);
|
return internal::uploadDcId(0, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) {
|
constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) {
|
||||||
return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, MTPUploadSessionsCount - 1) + internal::kDcShift);
|
return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, kUploadSessionsCount - 1) + internal::kDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) {
|
inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) {
|
||||||
|
|
|
@ -25,10 +25,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
#include "mtproto/connection.h"
|
#include "mtproto/connection.h"
|
||||||
|
#include "mtproto/sender.h"
|
||||||
|
#include "mtproto/rsa_public_key.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
class Instance::Private {
|
class Instance::Private : public Sender {
|
||||||
public:
|
public:
|
||||||
Private(Instance *instance, DcOptions *options, Instance::Mode mode);
|
Private(Instance *instance, DcOptions *options, Instance::Mode mode);
|
||||||
|
|
||||||
|
@ -45,6 +47,7 @@ public:
|
||||||
DcOptions *dcOptions();
|
DcOptions *dcOptions();
|
||||||
|
|
||||||
void configLoadRequest();
|
void configLoadRequest();
|
||||||
|
void cdnConfigLoadRequest();
|
||||||
|
|
||||||
void restart();
|
void restart();
|
||||||
void restart(ShiftedDcId shiftedDcId);
|
void restart(ShiftedDcId shiftedDcId);
|
||||||
|
@ -116,6 +119,9 @@ private:
|
||||||
void configLoadDone(const MTPConfig &result);
|
void configLoadDone(const MTPConfig &result);
|
||||||
bool configLoadFail(const RPCError &error);
|
bool configLoadFail(const RPCError &error);
|
||||||
|
|
||||||
|
void cdnConfigLoadDone(const MTPCdnConfig &result);
|
||||||
|
bool cdnConfigLoadFail(const RPCError &error);
|
||||||
|
|
||||||
void checkDelayedRequests();
|
void checkDelayedRequests();
|
||||||
|
|
||||||
Instance *_instance = nullptr;
|
Instance *_instance = nullptr;
|
||||||
|
@ -133,6 +139,7 @@ private:
|
||||||
base::set_of_unique_ptr<internal::Connection> _quittingConnections;
|
base::set_of_unique_ptr<internal::Connection> _quittingConnections;
|
||||||
|
|
||||||
std::unique_ptr<internal::ConfigLoader> _configLoader;
|
std::unique_ptr<internal::ConfigLoader> _configLoader;
|
||||||
|
mtpRequestId _cdnConfigLoadRequestId = 0;
|
||||||
|
|
||||||
std::map<DcId, AuthKeyPtr> _keysForWrite;
|
std::map<DcId, AuthKeyPtr> _keysForWrite;
|
||||||
mutable QReadWriteLock _keysForWriteLock;
|
mutable QReadWriteLock _keysForWriteLock;
|
||||||
|
@ -174,7 +181,7 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Instance::Private::Private(Instance *instance, DcOptions *options, Instance::Mode mode) : _instance(instance)
|
Instance::Private::Private(Instance *instance, DcOptions *options, Instance::Mode mode) : Sender(instance), _instance(instance)
|
||||||
, _dcOptions(options)
|
, _dcOptions(options)
|
||||||
, _mode(mode) {
|
, _mode(mode) {
|
||||||
}
|
}
|
||||||
|
@ -188,7 +195,6 @@ void Instance::Private::start(Config &&config) {
|
||||||
|
|
||||||
for (auto &key : config.keys) {
|
for (auto &key : config.keys) {
|
||||||
auto dcId = key->dcId();
|
auto dcId = key->dcId();
|
||||||
|
|
||||||
auto shiftedDcId = dcId;
|
auto shiftedDcId = dcId;
|
||||||
if (isKeysDestroyer()) {
|
if (isKeysDestroyer()) {
|
||||||
shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
|
shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
|
||||||
|
@ -273,6 +279,22 @@ void Instance::Private::configLoadRequest() {
|
||||||
_configLoader->load();
|
_configLoader->load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Instance::Private::cdnConfigLoadRequest() {
|
||||||
|
if (_cdnConfigLoadRequestId || _mainDcId == Config::kNoneMainDc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_cdnConfigLoadRequestId = request(MTPhelp_GetCdnConfig()).done([this](const MTPCdnConfig &result) {
|
||||||
|
_cdnConfigLoadRequestId = 0;
|
||||||
|
|
||||||
|
Expects(result.type() == mtpc_cdnConfig);
|
||||||
|
dcOptions()->setCDNConfig(result.c_cdnConfig());
|
||||||
|
|
||||||
|
Local::writeSettings();
|
||||||
|
|
||||||
|
emit _instance->cdnConfigLoaded();
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void Instance::Private::restart() {
|
void Instance::Private::restart() {
|
||||||
for (auto &session : _sessions) {
|
for (auto &session : _sessions) {
|
||||||
session.second->restart();
|
session.second->restart();
|
||||||
|
@ -423,7 +445,7 @@ void Instance::Private::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFai
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto dcId : dcIds) {
|
for (auto dcId : dcIds) {
|
||||||
if (dcId != mainDcId()) {
|
if (dcId != mainDcId() && dcOptions()->dcType(dcId) != DcType::Cdn) {
|
||||||
auto shiftedDcId = MTP::logoutDcId(dcId);
|
auto shiftedDcId = MTP::logoutDcId(dcId);
|
||||||
auto requestId = _instance->send(MTPauth_LogOut(), rpcDone([this](mtpRequestId requestId) {
|
auto requestId = _instance->send(MTPauth_LogOut(), rpcDone([this](mtpRequestId requestId) {
|
||||||
logoutGuestDone(requestId);
|
logoutGuestDone(requestId);
|
||||||
|
@ -1204,6 +1226,8 @@ void Instance::Private::prepareToDestroy() {
|
||||||
// It accesses Instance in destructor, so it should be destroyed first.
|
// It accesses Instance in destructor, so it should be destroyed first.
|
||||||
_configLoader.reset();
|
_configLoader.reset();
|
||||||
|
|
||||||
|
requestCancellingDiscard();
|
||||||
|
|
||||||
for (auto &session : base::take(_sessions)) {
|
for (auto &session : base::take(_sessions)) {
|
||||||
session.second->kill();
|
session.second->kill();
|
||||||
}
|
}
|
||||||
|
@ -1233,6 +1257,10 @@ void Instance::configLoadRequest() {
|
||||||
_private->configLoadRequest();
|
_private->configLoadRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Instance::cdnConfigLoadRequest() {
|
||||||
|
_private->cdnConfigLoadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
void Instance::connectionFinished(internal::Connection *connection) {
|
void Instance::connectionFinished(internal::Connection *connection) {
|
||||||
_private->connectionFinished(connection);
|
_private->connectionFinished(connection);
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,14 +119,18 @@ public:
|
||||||
bool isKeysDestroyer() const;
|
bool isKeysDestroyer() const;
|
||||||
void scheduleKeyDestroy(ShiftedDcId shiftedDcId);
|
void scheduleKeyDestroy(ShiftedDcId shiftedDcId);
|
||||||
|
|
||||||
|
void configLoadRequest();
|
||||||
|
|
||||||
|
void cdnConfigLoadRequest();
|
||||||
|
|
||||||
~Instance();
|
~Instance();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void configLoadRequest();
|
|
||||||
void connectionFinished(internal::Connection *connection);
|
void connectionFinished(internal::Connection *connection);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void configLoaded();
|
void configLoaded();
|
||||||
|
void cdnConfigLoaded();
|
||||||
void keyDestroyed(qint32 shiftedDcId);
|
void keyDestroyed(qint32 shiftedDcId);
|
||||||
void allKeysDestroyed();
|
void allKeysDestroyed();
|
||||||
|
|
||||||
|
|
|
@ -25,55 +25,110 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include <openssl/bio.h>
|
#include <openssl/bio.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct RSAPublicKey::Impl {
|
class RSAPublicKey::Private {
|
||||||
Impl(const char *key) : rsa(PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(key), -1), 0, 0, 0)) {
|
public:
|
||||||
|
Private(base::const_byte_span key) : _rsa(PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<gsl::byte*>(key.data()), key.size()), 0, 0, 0)) {
|
||||||
|
if (_rsa) {
|
||||||
|
computeFingerprint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
~Impl() {
|
Private(const QByteArray &n, const QByteArray &e) : _rsa(RSA_new()) {
|
||||||
RSA_free(rsa);
|
if (_rsa) {
|
||||||
|
_rsa->n = BN_bin2bn((const uchar*)n.data(), n.size(), _rsa->n);
|
||||||
|
_rsa->e = BN_bin2bn((const uchar*)e.data(), e.size(), _rsa->e);
|
||||||
|
if (!_rsa->n || !_rsa->e) {
|
||||||
|
RSA_free(base::take(_rsa));
|
||||||
|
} else {
|
||||||
|
computeFingerprint();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RSA *rsa;
|
QByteArray getN() const {
|
||||||
uint64 fp = 0;
|
Expects(isValid());
|
||||||
|
return toBytes(_rsa->n);
|
||||||
|
}
|
||||||
|
QByteArray getE() const {
|
||||||
|
Expects(isValid());
|
||||||
|
return toBytes(_rsa->e);
|
||||||
|
}
|
||||||
|
uint64 getFingerPrint() const {
|
||||||
|
return _fingerprint;
|
||||||
|
}
|
||||||
|
bool isValid() const {
|
||||||
|
return _rsa != nullptr;
|
||||||
|
}
|
||||||
|
bool encrypt(const void *data, string &result) const {
|
||||||
|
Expects(isValid());
|
||||||
|
|
||||||
|
result.resize(256);
|
||||||
|
auto res = RSA_public_encrypt(256, reinterpret_cast<const unsigned char*>(data), reinterpret_cast<uchar*>(&result[0]), _rsa, RSA_NO_PADDING);
|
||||||
|
if (res != 256) {
|
||||||
|
ERR_load_crypto_strings();
|
||||||
|
LOG(("RSA Error: RSA_public_encrypt failed, key fp: %1, result: %2, error: %3").arg(getFingerPrint()).arg(res).arg(ERR_error_string(ERR_get_error(), 0)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
~Private() {
|
||||||
|
RSA_free(_rsa);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void computeFingerprint() {
|
||||||
|
Expects(isValid());
|
||||||
|
|
||||||
|
mtpBuffer string;
|
||||||
|
MTP_bytes(toBytes(_rsa->n)).write(string);
|
||||||
|
MTP_bytes(toBytes(_rsa->e)).write(string);
|
||||||
|
|
||||||
|
uchar sha1Buffer[20];
|
||||||
|
_fingerprint = *(uint64*)(hashSha1(&string[0], string.size() * sizeof(mtpPrime), sha1Buffer) + 3);
|
||||||
|
}
|
||||||
|
static QByteArray toBytes(BIGNUM *number) {
|
||||||
|
auto size = static_cast<int>(BN_num_bytes(number));
|
||||||
|
auto result = QByteArray(size, 0);
|
||||||
|
BN_bn2bin(number, reinterpret_cast<uchar*>(result.data()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RSA *_rsa = nullptr;
|
||||||
|
uint64 _fingerprint = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RSAPublicKey::RSAPublicKey(const char *key) : impl_(new Impl(key)) {
|
RSAPublicKey::RSAPublicKey(base::const_byte_span key) : _private(std::make_shared<Private>(key)) {
|
||||||
if (!impl_->rsa) return;
|
|
||||||
|
|
||||||
int nBytes = BN_num_bytes(impl_->rsa->n);
|
|
||||||
int eBytes = BN_num_bytes(impl_->rsa->e);
|
|
||||||
std::string nStr(nBytes, 0), eStr(eBytes, 0);
|
|
||||||
BN_bn2bin(impl_->rsa->n, (uchar*)&nStr[0]);
|
|
||||||
BN_bn2bin(impl_->rsa->e, (uchar*)&eStr[0]);
|
|
||||||
|
|
||||||
mtpBuffer tmp;
|
|
||||||
MTP_string(nStr).write(tmp);
|
|
||||||
MTP_string(eStr).write(tmp);
|
|
||||||
|
|
||||||
uchar sha1Buffer[20];
|
|
||||||
impl_->fp = *(uint64*)(hashSha1(&tmp[0], tmp.size() * sizeof(mtpPrime), sha1Buffer) + 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 RSAPublicKey::getFingerPrint() const {
|
RSAPublicKey::RSAPublicKey(const QByteArray &n, const QByteArray &e) : _private(std::make_shared<Private>(n, e)) {
|
||||||
return impl_->fp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RSAPublicKey::isValid() const {
|
bool RSAPublicKey::isValid() const {
|
||||||
return impl_->rsa != nullptr;
|
return _private && _private->isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RSAPublicKey::encrypt(const void *data, std::string &result) const {
|
uint64 RSAPublicKey::getFingerPrint() const {
|
||||||
Expects(isValid());
|
Expects(isValid());
|
||||||
|
return _private->getFingerPrint();
|
||||||
|
}
|
||||||
|
|
||||||
result.resize(256);
|
QByteArray RSAPublicKey::getN() const {
|
||||||
int res = RSA_public_encrypt(256, reinterpret_cast<const unsigned char*>(data), reinterpret_cast<uchar*>(&result[0]), impl_->rsa, RSA_NO_PADDING);
|
Expects(isValid());
|
||||||
if (res != 256) {
|
return _private->getN();
|
||||||
ERR_load_crypto_strings();
|
}
|
||||||
LOG(("RSA Error: RSA_public_encrypt failed, key fp: %1, result: %2, error: %3").arg(getFingerPrint()).arg(res).arg(ERR_error_string(ERR_get_error(), 0)));
|
|
||||||
return false;
|
QByteArray RSAPublicKey::getE() const {
|
||||||
}
|
Expects(isValid());
|
||||||
return true;
|
return _private->getE();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RSAPublicKey::encrypt(const void *data, string &result) const {
|
||||||
|
Expects(isValid());
|
||||||
|
return _private->encrypt(data, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
|
@ -26,21 +26,26 @@ namespace internal {
|
||||||
// this class holds an RSA public key and can encrypt fixed-size messages with it
|
// this class holds an RSA public key and can encrypt fixed-size messages with it
|
||||||
class RSAPublicKey final {
|
class RSAPublicKey final {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// key in RSAPublicKey "-----BEGIN RSA PUBLIC KEY----- ..." format
|
// key in RSAPublicKey "-----BEGIN RSA PUBLIC KEY----- ..." format
|
||||||
RSAPublicKey(const char *key);
|
RSAPublicKey() = default;
|
||||||
|
RSAPublicKey(base::const_byte_span key);
|
||||||
|
RSAPublicKey(const QByteArray &n, const QByteArray &e);
|
||||||
|
RSAPublicKey(RSAPublicKey &&other) = default;
|
||||||
|
RSAPublicKey(const RSAPublicKey &other) = default;
|
||||||
|
RSAPublicKey &operator=(RSAPublicKey &&other) = default;
|
||||||
|
RSAPublicKey &operator=(const RSAPublicKey &other) = default;
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
uint64 getFingerPrint() const;
|
uint64 getFingerPrint() const;
|
||||||
|
QByteArray getN() const;
|
||||||
|
QByteArray getE() const;
|
||||||
|
|
||||||
// data has exactly 256 chars to be encrypted
|
// data has exactly 256 chars to be encrypted
|
||||||
bool encrypt(const void *data, std::string &result) const;
|
bool encrypt(const void *data, std::string &result) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class Private;
|
||||||
struct Impl;
|
std::shared_ptr<Private> _private;
|
||||||
typedef QSharedPointer<Impl> ImplPtr;
|
|
||||||
ImplPtr impl_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,11 @@ public:
|
||||||
void requestSendDelayed() {
|
void requestSendDelayed() {
|
||||||
MTP::sendAnything();
|
MTP::sendAnything();
|
||||||
}
|
}
|
||||||
|
void requestCancellingDiscard() {
|
||||||
|
for (auto &request : _requests) {
|
||||||
|
request.handled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class RequestWrap {
|
class RequestWrap {
|
||||||
|
|
|
@ -42,45 +42,68 @@ void Downloader::clearPriorities() {
|
||||||
++_priority;
|
++_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Downloader::requestedAmountIncrement(MTP::DcId dcId, int index, int amount) {
|
||||||
|
Expects(index >= 0 && index < MTP::kDownloadSessionsCount);
|
||||||
|
auto it = _requestedBytesAmount.find(dcId);
|
||||||
|
if (it == _requestedBytesAmount.cend()) {
|
||||||
|
it = _requestedBytesAmount.emplace(dcId, RequestedInDc { 0 }).first;
|
||||||
|
}
|
||||||
|
it->second[index] += amount;
|
||||||
|
if (it->second[index]) {
|
||||||
|
Messenger::Instance().killDownloadSessionsStop(dcId);
|
||||||
|
} else {
|
||||||
|
Messenger::Instance().killDownloadSessionsStart(dcId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Downloader::chooseDcIndexForRequest(MTP::DcId dcId) const {
|
||||||
|
auto result = 0;
|
||||||
|
auto it = _requestedBytesAmount.find(dcId);
|
||||||
|
if (it != _requestedBytesAmount.cend()) {
|
||||||
|
for (auto i = 1; i != MTP::kDownloadSessionsCount; ++i) {
|
||||||
|
if (it->second[i] < it->second[result]) {
|
||||||
|
result = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct DataRequested {
|
|
||||||
DataRequested() {
|
|
||||||
memset(v, 0, sizeof(v));
|
|
||||||
}
|
|
||||||
int64 v[MTPDownloadSessionsCount];
|
|
||||||
};
|
|
||||||
QMap<int32, DataRequested> DataRequestedMap;
|
|
||||||
|
|
||||||
constexpr auto kDownloadPhotoPartSize = 64 * 1024; // 64kb for photo
|
constexpr auto kDownloadPhotoPartSize = 64 * 1024; // 64kb for photo
|
||||||
constexpr auto kDownloadDocumentPartSize = 128 * 1024; // 128kb for document
|
constexpr auto kDownloadDocumentPartSize = 128 * 1024; // 128kb for document
|
||||||
|
constexpr auto kMaxFileQueries = 16; // max 16 file parts downloaded at the same time
|
||||||
|
constexpr auto kMaxWebFileQueries = 8; // max 8 http[s] files downloaded at the same time
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct FileLoaderQueue {
|
struct FileLoaderQueue {
|
||||||
FileLoaderQueue(int32 limit) : limit(limit) {
|
FileLoaderQueue(int queriesLimit) : queriesLimit(queriesLimit) {
|
||||||
}
|
}
|
||||||
int queries = 0;
|
int queriesCount = 0;
|
||||||
int limit = 0;
|
int queriesLimit = 0;
|
||||||
FileLoader *start = nullptr;
|
FileLoader *start = nullptr;
|
||||||
FileLoader *end = nullptr;
|
FileLoader *end = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
typedef QMap<int32, FileLoaderQueue> LoaderQueues;
|
|
||||||
LoaderQueues queues;
|
|
||||||
|
|
||||||
FileLoaderQueue _webQueue(MaxWebFileQueries);
|
using LoaderQueues = QMap<int32, FileLoaderQueue>;
|
||||||
|
LoaderQueues queues;
|
||||||
|
|
||||||
QThread *_webLoadThread = 0;
|
FileLoaderQueue _webQueue(kMaxWebFileQueries);
|
||||||
WebLoadManager *_webLoadManager = 0;
|
|
||||||
WebLoadManager *webLoadManager() {
|
QThread *_webLoadThread = nullptr;
|
||||||
return (_webLoadManager && _webLoadManager != FinishedWebLoadManager) ? _webLoadManager : 0;
|
WebLoadManager *_webLoadManager = nullptr;
|
||||||
}
|
WebLoadManager *webLoadManager() {
|
||||||
WebLoadMainManager *_webLoadMainManager = 0;
|
return (_webLoadManager && _webLoadManager != FinishedWebLoadManager) ? _webLoadManager : nullptr;
|
||||||
}
|
}
|
||||||
|
WebLoadMainManager *_webLoadMainManager = nullptr;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
FileLoader::FileLoader(const QString &toFile, int32 size, LocationType locationType, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading)
|
FileLoader::FileLoader(const QString &toFile, int32 size, LocationType locationType, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading)
|
||||||
: _downloader(&AuthSession::Current().downloader())
|
: _downloader(&AuthSession::Current().downloader())
|
||||||
|
@ -144,10 +167,14 @@ void FileLoader::permitLoadFromCloud() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLoader::loadNext() {
|
void FileLoader::loadNext() {
|
||||||
if (_queue->queries >= _queue->limit) return;
|
if (_queue->queriesCount >= _queue->queriesLimit) {
|
||||||
for (FileLoader *i = _queue->start; i;) {
|
return;
|
||||||
|
}
|
||||||
|
for (auto i = _queue->start; i;) {
|
||||||
if (i->loadPart()) {
|
if (i->loadPart()) {
|
||||||
if (_queue->queries >= _queue->limit) return;
|
if (_queue->queriesCount >= _queue->queriesLimit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
i = i->_next;
|
i = i->_next;
|
||||||
}
|
}
|
||||||
|
@ -357,44 +384,46 @@ void FileLoader::cancel(bool fail) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLoader::startLoading(bool loadFirst, bool prior) {
|
void FileLoader::startLoading(bool loadFirst, bool prior) {
|
||||||
if ((_queue->queries >= _queue->limit && (!loadFirst || !prior)) || _finished) return;
|
if ((_queue->queriesCount >= _queue->queriesLimit && (!loadFirst || !prior)) || _finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
loadPart();
|
loadPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
mtpFileLoader::mtpFileLoader(const StorageImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading)
|
mtpFileLoader::mtpFileLoader(const StorageImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading)
|
||||||
: FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading)
|
: FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading)
|
||||||
, _dc(location->dc())
|
, _dcId(location->dc())
|
||||||
, _location(location) {
|
, _location(location) {
|
||||||
auto shiftedDcId = MTP::downloadDcId(_dc, 0);
|
auto shiftedDcId = MTP::downloadDcId(_dcId, 0);
|
||||||
auto i = queues.find(shiftedDcId);
|
auto i = queues.find(shiftedDcId);
|
||||||
if (i == queues.cend()) {
|
if (i == queues.cend()) {
|
||||||
i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries));
|
i = queues.insert(shiftedDcId, FileLoaderQueue(kMaxFileQueries));
|
||||||
}
|
}
|
||||||
_queue = &i.value();
|
_queue = &i.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
mtpFileLoader::mtpFileLoader(int32 dc, uint64 id, uint64 accessHash, int32 version, LocationType type, const QString &to, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading)
|
mtpFileLoader::mtpFileLoader(int32 dc, uint64 id, uint64 accessHash, int32 version, LocationType type, const QString &to, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading)
|
||||||
: FileLoader(to, size, type, toCache, fromCloud, autoLoading)
|
: FileLoader(to, size, type, toCache, fromCloud, autoLoading)
|
||||||
, _dc(dc)
|
, _dcId(dc)
|
||||||
, _id(id)
|
, _id(id)
|
||||||
, _accessHash(accessHash)
|
, _accessHash(accessHash)
|
||||||
, _version(version) {
|
, _version(version) {
|
||||||
auto shiftedDcId = MTP::downloadDcId(_dc, 0);
|
auto shiftedDcId = MTP::downloadDcId(_dcId, 0);
|
||||||
auto i = queues.find(shiftedDcId);
|
auto i = queues.find(shiftedDcId);
|
||||||
if (i == queues.cend()) {
|
if (i == queues.cend()) {
|
||||||
i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries));
|
i = queues.insert(shiftedDcId, FileLoaderQueue(kMaxFileQueries));
|
||||||
}
|
}
|
||||||
_queue = &i.value();
|
_queue = &i.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
mtpFileLoader::mtpFileLoader(const WebFileImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading)
|
mtpFileLoader::mtpFileLoader(const WebFileImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading)
|
||||||
: FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading)
|
: FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading)
|
||||||
, _dc(location->dc())
|
, _dcId(location->dc())
|
||||||
, _urlLocation(location) {
|
, _urlLocation(location) {
|
||||||
auto shiftedDcId = MTP::downloadDcId(_dc, 0);
|
auto shiftedDcId = MTP::downloadDcId(_dcId, 0);
|
||||||
auto i = queues.find(shiftedDcId);
|
auto i = queues.find(shiftedDcId);
|
||||||
if (i == queues.cend()) {
|
if (i == queues.cend()) {
|
||||||
i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries));
|
i = queues.insert(shiftedDcId, FileLoaderQueue(kMaxFileQueries));
|
||||||
}
|
}
|
||||||
_queue = &i.value();
|
_queue = &i.value();
|
||||||
}
|
}
|
||||||
|
@ -403,58 +432,15 @@ int32 mtpFileLoader::currentOffset(bool includeSkipped) const {
|
||||||
return (_fileIsOpen ? _file.size() : _data.size()) - (includeSkipped ? 0 : _skippedBytes);
|
return (_fileIsOpen ? _file.size() : _data.size()) - (includeSkipped ? 0 : _skippedBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
QString serializereqs(const QMap<mtpRequestId, int32> &reqs) { // serialize requests map in json-like format
|
|
||||||
QString result;
|
|
||||||
result.reserve(reqs.size() * 16 + 4);
|
|
||||||
result.append(qsl("{ "));
|
|
||||||
for (auto i = reqs.cbegin(), e = reqs.cend(); i != e;) {
|
|
||||||
result.append(QString::number(i.key())).append(qsl(" : ")).append(QString::number(i.value()));
|
|
||||||
if (++i == e) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
result.append(qsl(", "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.append(qsl(" }"));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mtpFileLoader::loadPart() {
|
bool mtpFileLoader::loadPart() {
|
||||||
if (_finished || _lastComplete || (!_dcIndexByRequest.isEmpty() && !_size)) {
|
if (_finished || _lastComplete || (!_sentRequests.empty() && !_size)) {
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
|
||||||
DEBUG_LOG(("FileLoader(%1): loadPart() returned, _finished=%2, _lastComplete=%3, _requests.size()=%4, _size=%5").arg(_id).arg(Logs::b(_finished)).arg(Logs::b(_lastComplete)).arg(_dcIndexByRequest.size()).arg(_size));
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
} else if (_size && _nextRequestOffset >= _size) {
|
||||||
if (_size && _nextRequestOffset >= _size) {
|
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
|
||||||
DEBUG_LOG(("FileLoader(%1): loadPart() returned, _size=%2, _nextRequestOffset=%3, _requests=%4").arg(_id).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest)));
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto offset = _nextRequestOffset;
|
makeRequest(_nextRequestOffset);
|
||||||
auto dcIndex = 0;
|
_nextRequestOffset += partSize();
|
||||||
auto &dr = DataRequestedMap[_dc];
|
|
||||||
if (_size) {
|
|
||||||
for (auto i = 1; i != MTPDownloadSessionsCount; ++i) {
|
|
||||||
if (dr.v[i] < dr.v[dcIndex]) {
|
|
||||||
dcIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
App::app()->killDownloadSessionsStop(_dc);
|
|
||||||
|
|
||||||
auto requestId = makeRequest(offset, dcIndex);
|
|
||||||
_dcIndexByRequest.insert(requestId, dcIndex);
|
|
||||||
|
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
|
||||||
DEBUG_LOG(("FileLoader(%1): requested part with offset=%2, _queue->queries=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(offset).arg(_queue->queries).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,42 +451,55 @@ int mtpFileLoader::partSize() const {
|
||||||
return kDownloadDocumentPartSize;
|
return kDownloadDocumentPartSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtpRequestId mtpFileLoader::makeRequest(int offset, int dcIndex) {
|
mtpFileLoader::RequestData mtpFileLoader::prepareRequest(int offset) const {
|
||||||
auto limit = partSize();
|
auto result = RequestData();
|
||||||
DataRequestedMap[_dc].v[dcIndex] += limit;
|
result.dcId = _cdnDcId ? _cdnDcId : _dcId;
|
||||||
++_queue->queries;
|
result.dcIndex = _size ? _downloader->chooseDcIndexForRequest(result.dcId) : 0;
|
||||||
_nextRequestOffset += limit;
|
result.offset = offset;
|
||||||
|
return result;
|
||||||
if (_urlLocation) {
|
|
||||||
return MTP::send(MTPupload_GetWebFile(MTP_inputWebFileLocation(MTP_bytes(_urlLocation->url()), MTP_long(_urlLocation->accessHash())), MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::webPartLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::downloadDcId(_dc, dcIndex), 50);
|
|
||||||
}
|
|
||||||
MTPInputFileLocation loc;
|
|
||||||
if (_location) {
|
|
||||||
loc = MTP_inputFileLocation(MTP_long(_location->volume()), MTP_int(_location->local()), MTP_long(_location->secret()));
|
|
||||||
} else {
|
|
||||||
loc = MTP_inputDocumentFileLocation(MTP_long(_id), MTP_long(_accessHash), MTP_int(_version));
|
|
||||||
}
|
|
||||||
return MTP::send(MTPupload_GetFile(loc, MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::normalPartLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::downloadDcId(_dc, dcIndex), 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtpFileLoader::normalPartLoaded(int offset, const MTPupload_File &result, mtpRequestId req) {
|
void mtpFileLoader::makeRequest(int offset) {
|
||||||
if (result.type() != mtpc_upload_file) {
|
auto requestData = prepareRequest(offset);
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
auto send = [this, &requestData] {
|
||||||
DEBUG_LOG(("FileLoader(%1): bad cons received! %2").arg(_id).arg(result.type()));
|
auto offset = requestData.offset;
|
||||||
|
auto limit = partSize();
|
||||||
|
auto shiftedDcId = MTP::downloadDcId(requestData.dcId, requestData.dcIndex);
|
||||||
|
if (_cdnDcId) {
|
||||||
|
t_assert(requestData.dcId == _cdnDcId);
|
||||||
|
return MTP::send(MTPupload_GetCdnFile(MTP_bytes(_cdnToken), MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::cdnPartLoaded), rpcFail(&mtpFileLoader::cdnPartFailed), shiftedDcId, 50);
|
||||||
|
} else if (_urlLocation) {
|
||||||
|
t_assert(requestData.dcId == _dcId);
|
||||||
|
return MTP::send(MTPupload_GetWebFile(MTP_inputWebFileLocation(MTP_bytes(_urlLocation->url()), MTP_long(_urlLocation->accessHash())), MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::webPartLoaded), rpcFail(&mtpFileLoader::partFailed), shiftedDcId, 50);
|
||||||
|
} else {
|
||||||
|
t_assert(requestData.dcId == _dcId);
|
||||||
|
auto location = [this] {
|
||||||
|
if (_location) {
|
||||||
|
return MTP_inputFileLocation(MTP_long(_location->volume()), MTP_int(_location->local()), MTP_long(_location->secret()));
|
||||||
|
}
|
||||||
|
return MTP_inputDocumentFileLocation(MTP_long(_id), MTP_long(_accessHash), MTP_int(_version));
|
||||||
|
};
|
||||||
|
return MTP::send(MTPupload_GetFile(location(), MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::normalPartLoaded), rpcFail(&mtpFileLoader::partFailed), shiftedDcId, 50);
|
||||||
}
|
}
|
||||||
return cancel(true);
|
};
|
||||||
|
placeSentRequest(send(), requestData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtpFileLoader::normalPartLoaded(const MTPupload_File &result, mtpRequestId requestId) {
|
||||||
|
Expects(result.type() == mtpc_upload_fileCdnRedirect || result.type() == mtpc_upload_file);
|
||||||
|
|
||||||
|
auto offset = finishSentRequestGetOffset(requestId);
|
||||||
|
if (result.type() == mtpc_upload_fileCdnRedirect) {
|
||||||
|
return switchToCDN(offset, result.c_upload_fileCdnRedirect());
|
||||||
}
|
}
|
||||||
auto bytes = gsl::as_bytes(gsl::make_span(result.c_upload_file().vbytes.v));
|
auto bytes = gsl::as_bytes(gsl::make_span(result.c_upload_file().vbytes.v));
|
||||||
return partLoaded(offset, bytes, req);
|
return partLoaded(offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtpFileLoader::webPartLoaded(int offset, const MTPupload_WebFile &result, mtpRequestId req) {
|
void mtpFileLoader::webPartLoaded(const MTPupload_WebFile &result, mtpRequestId requestId) {
|
||||||
if (result.type() != mtpc_upload_webFile) {
|
Expects(result.type() == mtpc_upload_webFile);
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
|
||||||
DEBUG_LOG(("FileLoader(%1): bad cons received! %2").arg(_id).arg(result.type()));
|
auto offset = finishSentRequestGetOffset(requestId);
|
||||||
}
|
|
||||||
return cancel(true);
|
|
||||||
}
|
|
||||||
auto &webFile = result.c_upload_webFile();
|
auto &webFile = result.c_upload_webFile();
|
||||||
if (!_size) {
|
if (!_size) {
|
||||||
_size = webFile.vsize.v;
|
_size = webFile.vsize.v;
|
||||||
|
@ -509,32 +508,72 @@ void mtpFileLoader::webPartLoaded(int offset, const MTPupload_WebFile &result, m
|
||||||
return cancel(true);
|
return cancel(true);
|
||||||
}
|
}
|
||||||
auto bytes = gsl::as_bytes(gsl::make_span(webFile.vbytes.v));
|
auto bytes = gsl::as_bytes(gsl::make_span(webFile.vbytes.v));
|
||||||
return partLoaded(offset, bytes, req);
|
return partLoaded(offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes, mtpRequestId req) {
|
void mtpFileLoader::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId requestId) {
|
||||||
auto i = _dcIndexByRequest.find(req);
|
auto offset = finishSentRequestGetOffset(requestId);
|
||||||
if (i == _dcIndexByRequest.cend()) {
|
if (result.type() == mtpc_upload_cdnFileReuploadNeeded) {
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
auto requestData = RequestData();
|
||||||
DEBUG_LOG(("FileLoader(%1): request req=%2 for offset=%3 not found in _requests=%4").arg(_id).arg(req).arg(offset).arg(serializereqs(_dcIndexByRequest)));
|
requestData.dcId = _dcId;
|
||||||
}
|
requestData.dcIndex = 0;
|
||||||
return loadNext();
|
requestData.offset = offset;
|
||||||
|
auto shiftedDcId = MTP::downloadDcId(requestData.dcId, requestData.dcIndex);
|
||||||
|
auto requestId = MTP::send(MTPupload_ReuploadCdnFile(MTP_bytes(_cdnToken), result.c_upload_cdnFileReuploadNeeded().vrequest_token), rpcDone(&mtpFileLoader::reuploadDone), rpcFail(&mtpFileLoader::cdnPartFailed), shiftedDcId);
|
||||||
|
placeSentRequest(requestId, requestData);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
Expects(result.type() == mtpc_upload_cdnFile);
|
||||||
|
|
||||||
auto limit = partSize();
|
auto key = gsl::as_bytes(gsl::make_span(_cdnEncryptionKey));
|
||||||
auto dcIndex = i.value();
|
auto iv = gsl::as_bytes(gsl::make_span(_cdnEncryptionIV));
|
||||||
DataRequestedMap[_dc].v[dcIndex] -= limit;
|
Expects(key.size() == MTP::CTRState::KeySize);
|
||||||
|
Expects(iv.size() == MTP::CTRState::IvecSize);
|
||||||
|
|
||||||
--_queue->queries;
|
auto state = MTP::CTRState();
|
||||||
_dcIndexByRequest.erase(i);
|
auto ivec = gsl::as_writeable_bytes(gsl::make_span(state.ivec));
|
||||||
|
std::copy(iv.begin(), iv.end(), ivec.begin());
|
||||||
|
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
auto counterOffset = static_cast<uint32>(offset) >> 4;
|
||||||
DEBUG_LOG(("FileLoader(%1): got part with offset=%2, bytes=%3, _queue->queries=%4, _nextRequestOffset=%5, _requests=%6").arg(_id).arg(offset).arg(bytes.size()).arg(_queue->queries).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest)));
|
state.ivec[15] = static_cast<uchar>(counterOffset & 0xFF);
|
||||||
}
|
state.ivec[14] = static_cast<uchar>((counterOffset >> 8) & 0xFF);
|
||||||
|
state.ivec[13] = static_cast<uchar>((counterOffset >> 16) & 0xFF);
|
||||||
|
state.ivec[12] = static_cast<uchar>((counterOffset >> 24) & 0xFF);
|
||||||
|
|
||||||
|
auto decryptInPlace = result.c_upload_cdnFile().vbytes.v;
|
||||||
|
MTP::aesCtrEncrypt(decryptInPlace.data(), decryptInPlace.size(), key.data(), &state);
|
||||||
|
auto bytes = gsl::as_bytes(gsl::make_span(decryptInPlace));
|
||||||
|
return partLoaded(offset, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtpFileLoader::reuploadDone(const MTPBool &result, mtpRequestId requestId) {
|
||||||
|
auto offset = finishSentRequestGetOffset(requestId);
|
||||||
|
makeRequest(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtpFileLoader::placeSentRequest(mtpRequestId requestId, const RequestData &requestData) {
|
||||||
|
_downloader->requestedAmountIncrement(requestData.dcId, requestData.dcIndex, partSize());
|
||||||
|
++_queue->queriesCount;
|
||||||
|
_sentRequests.emplace(requestId, requestData);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mtpFileLoader::finishSentRequestGetOffset(mtpRequestId requestId) {
|
||||||
|
auto it = _sentRequests.find(requestId);
|
||||||
|
Expects(it != _sentRequests.cend());
|
||||||
|
|
||||||
|
auto requestData = it->second;
|
||||||
|
_downloader->requestedAmountIncrement(requestData.dcId, requestData.dcIndex, -partSize());
|
||||||
|
|
||||||
|
--_queue->queriesCount;
|
||||||
|
_sentRequests.erase(it);
|
||||||
|
|
||||||
|
return requestData.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes) {
|
||||||
if (bytes.size()) {
|
if (bytes.size()) {
|
||||||
if (_fileIsOpen) {
|
if (_fileIsOpen) {
|
||||||
int64 fsize = _file.size();
|
auto fsize = _file.size();
|
||||||
if (offset < fsize) {
|
if (offset < fsize) {
|
||||||
_skippedBytes -= bytes.size();
|
_skippedBytes -= bytes.size();
|
||||||
} else if (offset > fsize) {
|
} else if (offset > fsize) {
|
||||||
|
@ -566,7 +605,7 @@ void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes, mtpReque
|
||||||
if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset
|
if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset
|
||||||
_lastComplete = true;
|
_lastComplete = true;
|
||||||
}
|
}
|
||||||
if (_dcIndexByRequest.isEmpty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) {
|
if (_sentRequests.empty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) {
|
||||||
if (!_fname.isEmpty() && (_toCache == LoadToCacheAsWell)) {
|
if (!_fname.isEmpty() && (_toCache == LoadToCacheAsWell)) {
|
||||||
if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly);
|
if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly);
|
||||||
if (!_fileIsOpen) {
|
if (!_fileIsOpen) {
|
||||||
|
@ -584,15 +623,11 @@ void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes, mtpReque
|
||||||
}
|
}
|
||||||
removeFromQueue();
|
removeFromQueue();
|
||||||
|
|
||||||
if (!_queue->queries) {
|
|
||||||
App::app()->killDownloadSessionsStart(_dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_localStatus == LocalNotFound || _localStatus == LocalFailed) {
|
if (_localStatus == LocalNotFound || _localStatus == LocalFailed) {
|
||||||
if (_urlLocation) {
|
if (_urlLocation) {
|
||||||
Local::writeImage(storageKey(*_urlLocation), StorageImageSaved(_data));
|
Local::writeImage(storageKey(*_urlLocation), StorageImageSaved(_data));
|
||||||
} else if (_locationType != UnknownFileLocation) { // audio, video, document
|
} else if (_locationType != UnknownFileLocation) { // audio, video, document
|
||||||
MediaKey mkey = mediaKey(_locationType, _dc, _id, _version);
|
auto mkey = mediaKey(_locationType, _dcId, _id, _version);
|
||||||
if (!_fname.isEmpty()) {
|
if (!_fname.isEmpty()) {
|
||||||
Local::writeFileLocation(mkey, FileLocation(_fname));
|
Local::writeFileLocation(mkey, FileLocation(_fname));
|
||||||
}
|
}
|
||||||
|
@ -607,10 +642,6 @@ void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes, mtpReque
|
||||||
Local::writeImage(storageKey(*_location), StorageImageSaved(_data));
|
Local::writeImage(storageKey(*_location), StorageImageSaved(_data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (DebugLogging::FileLoader() && _id) {
|
|
||||||
DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (_finished) {
|
if (_finished) {
|
||||||
_downloader->taskFinished().notify();
|
_downloader->taskFinished().notify();
|
||||||
|
@ -628,22 +659,59 @@ bool mtpFileLoader::partFailed(const RPCError &error) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mtpFileLoader::cdnPartFailed(const RPCError &error, mtpRequestId requestId) {
|
||||||
|
if (MTP::isDefaultHandledError(error)) return false;
|
||||||
|
|
||||||
|
if (error.type() == qstr("FILE_TOKEN_INVALID") || error.type() == qstr("REQUEST_TOKEN_INVALID")) {
|
||||||
|
auto offset = finishSentRequestGetOffset(requestId);
|
||||||
|
changeCDNParams(offset, 0, QByteArray(), QByteArray(), QByteArray());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return partFailed(error);
|
||||||
|
}
|
||||||
|
|
||||||
void mtpFileLoader::cancelRequests() {
|
void mtpFileLoader::cancelRequests() {
|
||||||
if (_dcIndexByRequest.isEmpty()) return;
|
while (!_sentRequests.empty()) {
|
||||||
|
auto requestId = _sentRequests.begin()->first;
|
||||||
auto limit = partSize();
|
MTP::cancel(requestId);
|
||||||
DataRequested &dr(DataRequestedMap[_dc]);
|
finishSentRequestGetOffset(requestId);
|
||||||
for (auto i = _dcIndexByRequest.cbegin(), e = _dcIndexByRequest.cend(); i != e; ++i) {
|
|
||||||
MTP::cancel(i.key());
|
|
||||||
int32 dcIndex = i.value();
|
|
||||||
dr.v[dcIndex] -= limit;
|
|
||||||
}
|
}
|
||||||
_queue->queries -= _dcIndexByRequest.size();
|
}
|
||||||
_dcIndexByRequest.clear();
|
|
||||||
|
|
||||||
if (!_queue->queries) {
|
void mtpFileLoader::switchToCDN(int offset, const MTPDupload_fileCdnRedirect &redirect) {
|
||||||
Messenger::Instance().killDownloadSessionsStart(_dc);
|
changeCDNParams(offset, redirect.vdc_id.v, redirect.vfile_token.v, redirect.vencryption_key.v, redirect.vencryption_iv.v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtpFileLoader::changeCDNParams(int offset, MTP::DcId dcId, const QByteArray &token, const QByteArray &encryptionKey, const QByteArray &encryptionIV) {
|
||||||
|
if (dcId != 0 && (encryptionKey.size() != MTP::CTRState::KeySize || encryptionIV.size() != MTP::CTRState::IvecSize)) {
|
||||||
|
LOG(("Message Error: Wrong key (%1) / iv (%2) size in CDN params").arg(encryptionKey.size()).arg(encryptionIV.size()));
|
||||||
|
cancel(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto resendAllRequests = (_cdnDcId != dcId
|
||||||
|
|| _cdnToken != token
|
||||||
|
|| _cdnEncryptionKey != encryptionKey
|
||||||
|
|| _cdnEncryptionIV != encryptionIV);
|
||||||
|
_cdnDcId = dcId;
|
||||||
|
_cdnToken = token;
|
||||||
|
_cdnEncryptionKey = encryptionKey;
|
||||||
|
_cdnEncryptionIV = encryptionIV;
|
||||||
|
|
||||||
|
if (resendAllRequests && !_sentRequests.empty()) {
|
||||||
|
auto resendOffsets = std::vector<int>();
|
||||||
|
resendOffsets.reserve(_sentRequests.size());
|
||||||
|
while (!_sentRequests.empty()) {
|
||||||
|
auto requestId = _sentRequests.begin()->first;
|
||||||
|
MTP::cancel(requestId);
|
||||||
|
auto resendOffset = finishSentRequestGetOffset(requestId);
|
||||||
|
resendOffsets.push_back(resendOffset);
|
||||||
|
}
|
||||||
|
for (auto resendOffset : resendOffsets) {
|
||||||
|
makeRequest(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
makeRequest(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mtpFileLoader::tryLoadLocal() {
|
bool mtpFileLoader::tryLoadLocal() {
|
||||||
|
@ -660,7 +728,7 @@ bool mtpFileLoader::tryLoadLocal() {
|
||||||
_localTaskId = Local::startImageLoad(storageKey(*_location), this);
|
_localTaskId = Local::startImageLoad(storageKey(*_location), this);
|
||||||
} else {
|
} else {
|
||||||
if (_toCache == LoadToCacheAsWell) {
|
if (_toCache == LoadToCacheAsWell) {
|
||||||
MediaKey mkey = mediaKey(_locationType, _dc, _id, _version);
|
MediaKey mkey = mediaKey(_locationType, _dcId, _id, _version);
|
||||||
if (_locationType == DocumentFileLocation) {
|
if (_locationType == DocumentFileLocation) {
|
||||||
_localTaskId = Local::startStickerImageLoad(mkey, this);
|
_localTaskId = Local::startStickerImageLoad(mkey, this);
|
||||||
} else if (_locationType == AudioFileLocation) {
|
} else if (_locationType == AudioFileLocation) {
|
||||||
|
|
|
@ -40,6 +40,9 @@ public:
|
||||||
return _taskFinishedObservable;
|
return _taskFinishedObservable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void requestedAmountIncrement(MTP::DcId dcId, int index, int amount);
|
||||||
|
int chooseDcIndexForRequest(MTP::DcId dcId) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::Observable<void> _taskFinishedObservable;
|
base::Observable<void> _taskFinishedObservable;
|
||||||
int _priority = 1;
|
int _priority = 1;
|
||||||
|
@ -47,6 +50,9 @@ private:
|
||||||
SingleQueuedInvokation _delayedLoadersDestroyer;
|
SingleQueuedInvokation _delayedLoadersDestroyer;
|
||||||
std::vector<std::unique_ptr<FileLoader>> _delayedDestroyedLoaders;
|
std::vector<std::unique_ptr<FileLoader>> _delayedDestroyedLoaders;
|
||||||
|
|
||||||
|
using RequestedInDc = std::array<int64, MTP::kDownloadSessionsCount>;
|
||||||
|
std::map<MTP::DcId, RequestedInDc> _requestedBytesAmount;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
@ -134,11 +140,11 @@ signals:
|
||||||
protected:
|
protected:
|
||||||
void readImage(const QSize &shrinkBox) const;
|
void readImage(const QSize &shrinkBox) const;
|
||||||
|
|
||||||
Storage::Downloader *_downloader = nullptr;
|
gsl::not_null<Storage::Downloader*> _downloader;
|
||||||
FileLoader *_prev = nullptr;
|
FileLoader *_prev = nullptr;
|
||||||
FileLoader *_next = nullptr;
|
FileLoader *_next = nullptr;
|
||||||
int _priority = 0;
|
int _priority = 0;
|
||||||
FileLoaderQueue *_queue;
|
FileLoaderQueue *_queue = nullptr;
|
||||||
|
|
||||||
bool _paused = false;
|
bool _paused = false;
|
||||||
bool _autoLoading = false;
|
bool _autoLoading = false;
|
||||||
|
@ -198,26 +204,41 @@ public:
|
||||||
~mtpFileLoader();
|
~mtpFileLoader();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct RequestData {
|
||||||
|
MTP::DcId dcId = 0;
|
||||||
|
int dcIndex = 0;
|
||||||
|
int offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
bool tryLoadLocal() override;
|
bool tryLoadLocal() override;
|
||||||
void cancelRequests() override;
|
void cancelRequests() override;
|
||||||
|
|
||||||
int partSize() const;
|
int partSize() const;
|
||||||
mtpRequestId makeRequest(int offset, int dcIndex);
|
RequestData prepareRequest(int offset) const;
|
||||||
|
void makeRequest(int offset);
|
||||||
QMap<mtpRequestId, int> _dcIndexByRequest;
|
|
||||||
|
|
||||||
bool loadPart() override;
|
bool loadPart() override;
|
||||||
void normalPartLoaded(int offset, const MTPupload_File &result, mtpRequestId req);
|
void normalPartLoaded(const MTPupload_File &result, mtpRequestId requestId);
|
||||||
void webPartLoaded(int offset, const MTPupload_WebFile &result, mtpRequestId req);
|
void webPartLoaded(const MTPupload_WebFile &result, mtpRequestId requestId);
|
||||||
|
void cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId requestId);
|
||||||
|
void reuploadDone(const MTPBool &result, mtpRequestId requestId);
|
||||||
|
|
||||||
void partLoaded(int offset, base::const_byte_span bytes, mtpRequestId req);
|
void partLoaded(int offset, base::const_byte_span bytes);
|
||||||
bool partFailed(const RPCError &error);
|
bool partFailed(const RPCError &error);
|
||||||
|
bool cdnPartFailed(const RPCError &error, mtpRequestId requestId);
|
||||||
|
|
||||||
|
void placeSentRequest(mtpRequestId requestId, const RequestData &requestData);
|
||||||
|
int finishSentRequestGetOffset(mtpRequestId requestId);
|
||||||
|
void switchToCDN(int offset, const MTPDupload_fileCdnRedirect &redirect);
|
||||||
|
void changeCDNParams(int offset, MTP::DcId dcId, const QByteArray &token, const QByteArray &encryptionKey, const QByteArray &encryptionIV);
|
||||||
|
|
||||||
|
std::map<mtpRequestId, RequestData> _sentRequests;
|
||||||
|
|
||||||
bool _lastComplete = false;
|
bool _lastComplete = false;
|
||||||
int32 _skippedBytes = 0;
|
int32 _skippedBytes = 0;
|
||||||
int32 _nextRequestOffset = 0;
|
int32 _nextRequestOffset = 0;
|
||||||
|
|
||||||
int32 _dc; // for photo locations
|
MTP::DcId _dcId = 0; // for photo locations
|
||||||
const StorageImageLocation *_location = nullptr;
|
const StorageImageLocation *_location = nullptr;
|
||||||
|
|
||||||
uint64 _id = 0; // for document locations
|
uint64 _id = 0; // for document locations
|
||||||
|
@ -226,6 +247,11 @@ private:
|
||||||
|
|
||||||
const WebFileImageLocation *_urlLocation = nullptr; // for webdocument locations
|
const WebFileImageLocation *_urlLocation = nullptr; // for webdocument locations
|
||||||
|
|
||||||
|
MTP::DcId _cdnDcId = 0;
|
||||||
|
QByteArray _cdnToken;
|
||||||
|
QByteArray _cdnEncryptionKey;
|
||||||
|
QByteArray _cdnEncryptionIV;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class webFileLoaderPrivate;
|
class webFileLoaderPrivate;
|
||||||
|
|
|
@ -20,6 +20,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#include "storage/file_upload.h"
|
#include "storage/file_upload.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kMaxUploadFileParallelSize = MTP::kUploadSessionsCount * 512 * 1024; // max 512kb uploaded at the same time in each session
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
FileUploader::FileUploader() : sentSize(0) {
|
FileUploader::FileUploader() : sentSize(0) {
|
||||||
memset(sentSizes, 0, sizeof(sentSizes));
|
memset(sentSizes, 0, sizeof(sentSizes));
|
||||||
nextTimer.setSingleShot(true);
|
nextTimer.setSingleShot(true);
|
||||||
|
@ -88,7 +94,7 @@ void FileUploader::currentFailed() {
|
||||||
dcMap.clear();
|
dcMap.clear();
|
||||||
uploading = FullMsgId();
|
uploading = FullMsgId();
|
||||||
sentSize = 0;
|
sentSize = 0;
|
||||||
for (int i = 0; i < MTPUploadSessionsCount; ++i) {
|
for (int i = 0; i < MTP::kUploadSessionsCount; ++i) {
|
||||||
sentSizes[i] = 0;
|
sentSizes[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,13 +102,13 @@ void FileUploader::currentFailed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileUploader::killSessions() {
|
void FileUploader::killSessions() {
|
||||||
for (int i = 0; i < MTPUploadSessionsCount; ++i) {
|
for (int i = 0; i < MTP::kUploadSessionsCount; ++i) {
|
||||||
MTP::stopSession(MTP::uploadDcId(i));
|
MTP::stopSession(MTP::uploadDcId(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileUploader::sendNext() {
|
void FileUploader::sendNext() {
|
||||||
if (sentSize >= MaxUploadFileParallelSize || _paused.msg) return;
|
if (sentSize >= kMaxUploadFileParallelSize || _paused.msg) return;
|
||||||
|
|
||||||
bool killing = killSessionsTimer.isActive();
|
bool killing = killSessionsTimer.isActive();
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
|
@ -123,7 +129,7 @@ void FileUploader::sendNext() {
|
||||||
uploading = i.key();
|
uploading = i.key();
|
||||||
}
|
}
|
||||||
int todc = 0;
|
int todc = 0;
|
||||||
for (int dc = 1; dc < MTPUploadSessionsCount; ++dc) {
|
for (int dc = 1; dc < MTP::kUploadSessionsCount; ++dc) {
|
||||||
if (sentSizes[dc] < sentSizes[todc]) {
|
if (sentSizes[dc] < sentSizes[todc]) {
|
||||||
todc = dc;
|
todc = dc;
|
||||||
}
|
}
|
||||||
|
@ -246,7 +252,7 @@ void FileUploader::clear() {
|
||||||
docRequestsSent.clear();
|
docRequestsSent.clear();
|
||||||
dcMap.clear();
|
dcMap.clear();
|
||||||
sentSize = 0;
|
sentSize = 0;
|
||||||
for (int32 i = 0; i < MTPUploadSessionsCount; ++i) {
|
for (int i = 0; i < MTP::kUploadSessionsCount; ++i) {
|
||||||
MTP::stopSession(MTP::uploadDcId(i));
|
MTP::stopSession(MTP::uploadDcId(i));
|
||||||
sentSizes[i] = 0;
|
sentSizes[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ private:
|
||||||
QMap<mtpRequestId, int32> docRequestsSent;
|
QMap<mtpRequestId, int32> docRequestsSent;
|
||||||
QMap<mtpRequestId, int32> dcMap;
|
QMap<mtpRequestId, int32> dcMap;
|
||||||
uint32 sentSize;
|
uint32 sentSize;
|
||||||
uint32 sentSizes[MTPUploadSessionsCount];
|
uint32 sentSizes[MTP::kUploadSessionsCount];
|
||||||
|
|
||||||
FullMsgId uploading, _paused;
|
FullMsgId uploading, _paused;
|
||||||
Queue queue;
|
Queue queue;
|
||||||
|
|
|
@ -458,7 +458,8 @@ void FileLoadTask::process() {
|
||||||
if (video->isGifv) {
|
if (video->isGifv) {
|
||||||
attributes.push_back(MTP_documentAttributeAnimated());
|
attributes.push_back(MTP_documentAttributeAnimated());
|
||||||
}
|
}
|
||||||
attributes.push_back(MTP_documentAttributeVideo(MTP_int(video->duration), MTP_int(coverWidth), MTP_int(coverHeight)));
|
auto flags = MTPDdocumentAttributeVideo::Flags(0);
|
||||||
|
attributes.push_back(MTP_documentAttributeVideo(MTP_flags(flags), MTP_int(video->duration), MTP_int(coverWidth), MTP_int(coverHeight)));
|
||||||
|
|
||||||
auto cover = (coverWidth > 90 || coverHeight > 90)
|
auto cover = (coverWidth > 90 || coverHeight > 90)
|
||||||
? video->thumbnail.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation)
|
? video->thumbnail.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation)
|
||||||
|
|
|
@ -853,8 +853,8 @@ struct ReadSettingsContext {
|
||||||
MTP::DcOptions dcOptions;
|
MTP::DcOptions dcOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
void applyReadContext(const ReadSettingsContext &context) {
|
void applyReadContext(ReadSettingsContext &&context) {
|
||||||
Messenger::Instance().dcOptions()->addFromOther(context.dcOptions);
|
Messenger::Instance().dcOptions()->addFromOther(std::move(context.dcOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSettingsContext &context) {
|
bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSettingsContext &context) {
|
||||||
|
@ -1799,7 +1799,7 @@ void _readUserSettings() {
|
||||||
LOG(("App Info: could not read encrypted user settings..."));
|
LOG(("App Info: could not read encrypted user settings..."));
|
||||||
|
|
||||||
_readOldUserSettings(true, context);
|
_readOldUserSettings(true, context);
|
||||||
applyReadContext(context);
|
applyReadContext(std::move(context));
|
||||||
|
|
||||||
return _writeUserSettings();
|
return _writeUserSettings();
|
||||||
}
|
}
|
||||||
|
@ -1822,7 +1822,7 @@ void _readUserSettings() {
|
||||||
_readingUserSettings = false;
|
_readingUserSettings = false;
|
||||||
LOG(("App Info: encrypted user settings read."));
|
LOG(("App Info: encrypted user settings read."));
|
||||||
|
|
||||||
applyReadContext(context);
|
applyReadContext(std::move(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _writeMtpData() {
|
void _writeMtpData() {
|
||||||
|
@ -1847,7 +1847,7 @@ void _readMtpData() {
|
||||||
if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), FileOption::Safe)) {
|
if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), FileOption::Safe)) {
|
||||||
if (LocalKey) {
|
if (LocalKey) {
|
||||||
_readOldMtpData(true, context);
|
_readOldMtpData(true, context);
|
||||||
applyReadContext(context);
|
applyReadContext(std::move(context));
|
||||||
|
|
||||||
_writeMtpData();
|
_writeMtpData();
|
||||||
}
|
}
|
||||||
|
@ -1866,7 +1866,7 @@ void _readMtpData() {
|
||||||
return _writeMtpData();
|
return _writeMtpData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
applyReadContext(context);
|
applyReadContext(std::move(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadMapState _readMap(const QByteArray &pass) {
|
ReadMapState _readMap(const QByteArray &pass) {
|
||||||
|
@ -2229,7 +2229,7 @@ void start() {
|
||||||
_readOldSettings(true, context);
|
_readOldSettings(true, context);
|
||||||
_readOldUserSettings(false, context); // needed further in _readUserSettings
|
_readOldUserSettings(false, context); // needed further in _readUserSettings
|
||||||
_readOldMtpData(false, context); // needed further in _readMtpData
|
_readOldMtpData(false, context); // needed further in _readMtpData
|
||||||
applyReadContext(context);
|
applyReadContext(std::move(context));
|
||||||
|
|
||||||
return writeSettings();
|
return writeSettings();
|
||||||
}
|
}
|
||||||
|
@ -2271,7 +2271,7 @@ void start() {
|
||||||
|
|
||||||
readTheme();
|
readTheme();
|
||||||
|
|
||||||
applyReadContext(context);
|
applyReadContext(std::move(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeSettings() {
|
void writeSettings() {
|
||||||
|
|
|
@ -118,7 +118,8 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
|
||||||
}
|
}
|
||||||
if (width > 0 && height > 0) {
|
if (width > 0 && height > 0) {
|
||||||
if (duration >= 0) {
|
if (duration >= 0) {
|
||||||
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(width), MTP_int(height)));
|
auto flags = MTPDdocumentAttributeVideo::Flags(0);
|
||||||
|
attributes.push_back(MTP_documentAttributeVideo(MTP_flags(flags), MTP_int(duration), MTP_int(width), MTP_int(height)));
|
||||||
} else {
|
} else {
|
||||||
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
|
attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height)));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue