diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index 270b5babd..bc7230a1a 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -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; 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; userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto; @@ -357,6 +357,8 @@ inputMessagesFilterVoice#50f5c392 = MessagesFilter; inputMessagesFilterMusic#3751b49e = MessagesFilter; inputMessagesFilterChatPhotos#3a20ecb8 = 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; updateMessageID#4e90bfd6 id:int random_id:long = Update; @@ -440,8 +442,9 @@ photos.photosSlice#15051f54 count:int photos:Vector users:Vector = photos.photo#20212ca8 photo:Photo users:Vector = photos.Photo; 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 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 = Config; @@ -501,6 +504,8 @@ sendMessageUploadDocumentAction#aa0cd9e4 progress:int = SendMessageAction; sendMessageGeoLocationAction#176f8ba1 = SendMessageAction; sendMessageChooseContactAction#628cbc6f = SendMessageAction; sendMessageGamePlayAction#dd6a8f48 = SendMessageAction; +sendMessageRecordRoundAction#88f27fbc = SendMessageAction; +sendMessageUploadRoundAction#bb718624 = SendMessageAction; contacts.found#1aa1f784 results:Vector chats:Vector users:Vector = contacts.Found; @@ -533,7 +538,7 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL; documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute; documentAttributeAnimated#11b58939 = 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; documentAttributeFilename#15590068 file_name:string = 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 = 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 = CdnConfig; + ---functions--- 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.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.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.getNearestDc#1fb33026 = NearestDc; @@ -1034,6 +1048,7 @@ help.getSupport#9cdf08cd = help.Support; help.getAppChangelog#9010ef6f prev_app_version:string = Updates; help.getTermsOfService#350170f3 = help.TermsOfService; help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool; +help.getCdnConfig#52029342 = CdnConfig; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector = 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.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; -// LAYER 65 +// LAYER 66 diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 2dc9d2a64..11b1dced3 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -46,8 +46,6 @@ enum { MTPTcpConnectionWaitTimeout = 2000, // 2 seconds waiting for tcp, until we accept http 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 MTPDebugBufferSize = 1024 * 1024, // 1 mb start size @@ -320,8 +318,6 @@ enum { FileLoaderQueueStopTimeout = 5000, 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 DocumentMaxPartsCount = 3000, // no more than 3000 parts @@ -330,7 +326,6 @@ enum { DocumentUploadPartSize2 = 128 * 1024, // 128kb for small document ( <= 375mb ) DocumentUploadPartSize3 = 256 * 1024, // 256kb for medium document ( <= 750mb ) 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 MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 2bc1e051c..8dd907f28 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -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)) template -inline QFlags qFlags(Enum v) { +inline constexpr QFlags qFlags(Enum v) { return QFlags(v); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 81259a981..31f447607 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -89,7 +89,7 @@ MTPVector composeDocumentAttributes(DocumentData *document if (document->dimensions.width() > 0 && document->dimensions.height() > 0) { int32 duration = document->duration(); 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 { attributes.push_back(MTP_documentAttributeImageSize(MTP_int(document->dimensions.width()), MTP_int(document->dimensions.height()))); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 91c320be2..0448f8442 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -335,14 +335,16 @@ void Result::createDocument() { QString mime = _content_type; QVector attributes; - QSize dimensions(_width, _height); + auto dimensions = QSize(_width, _height); 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_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) { - 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) { auto flags = MTPDdocumentAttributeAudio::Flags(0); if (mime == qstr("audio/ogg")) { diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 5195ddaa5..c5a5ee956 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -348,7 +348,7 @@ void MainWindow::sendServiceHistoryRequest() { UserData *user = App::userLoaded(ServiceUserId); if (!user) { auto userFlags = MTPDuser::Flag::f_first_name | MTPDuser::Flag::f_phone | MTPDuser::Flag::f_status | MTPDuser::Flag::f_verified; - user = App::feedUsers(MTP_vector(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(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)); } diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 4b50f83a4..a3abcf277 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -481,17 +481,17 @@ void Messenger::peerClearPhoto(PeerId id) { } } -void Messenger::killDownloadSessionsStart(int32 dc) { - if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) { - killDownloadSessionTimes.insert(dc, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout); +void Messenger::killDownloadSessionsStart(MTP::DcId dcId) { + if (killDownloadSessionTimes.constFind(dcId) == killDownloadSessionTimes.cend()) { + killDownloadSessionTimes.insert(dcId, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout); } if (!killDownloadSessionsTimer.isActive()) { killDownloadSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout + 5); } } -void Messenger::killDownloadSessionsStop(int32 dc) { - killDownloadSessionTimes.remove(dc); +void Messenger::killDownloadSessionsStop(MTP::DcId dcId) { + killDownloadSessionTimes.remove(dcId); if (killDownloadSessionTimes.isEmpty() && killDownloadSessionsTimer.isActive()) { killDownloadSessionsTimer.stop(); } @@ -543,7 +543,7 @@ void Messenger::killDownloadSessions() { auto ms = getms(), left = static_cast(MTPAckSendWaiting) + MTPKillFileSessionTimeout; for (auto i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) { 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)); } i = killDownloadSessionTimes.erase(i); diff --git a/Telegram/SourceFiles/messenger.h b/Telegram/SourceFiles/messenger.h index 80f39a2d3..171344f8a 100644 --- a/Telegram/SourceFiles/messenger.h +++ b/Telegram/SourceFiles/messenger.h @@ -108,8 +108,8 @@ public: void writeUserConfigIn(TimeMs ms); - void killDownloadSessionsStart(int32 dc); - void killDownloadSessionsStop(int32 dc); + void killDownloadSessionsStart(MTP::DcId dcId); + void killDownloadSessionsStop(MTP::DcId dcId); void checkLocalTime(); void checkMapVersion(); @@ -145,7 +145,7 @@ private: QMap photoUpdates; - QMap killDownloadSessionTimes; + QMap killDownloadSessionTimes; SingleTimer killDownloadSessionsTimer; // Some fields are just moved from the declaration. diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index 0acde2f2b..6dd933189 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -340,34 +340,13 @@ private: }; -typedef QMap 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 Connection::Connection(Instance *instance) : _instance(instance) { } void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) { - t_assert(thread == nullptr && data == nullptr); + Expects(thread == nullptr && data == nullptr); thread = std::make_unique(); auto newData = std::make_unique(_instance, thread.get(), this, sessionData, shiftedDcId); @@ -378,14 +357,14 @@ void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) { } void Connection::kill() { - t_assert(data != nullptr && thread != nullptr); + Expects(data != nullptr && thread != nullptr); data->stop(); data = nullptr; thread->quit(); } void Connection::waitTillFinish() { - t_assert(data == nullptr && thread != nullptr); + Expects(data == nullptr && thread != nullptr); DEBUG_LOG(("Waiting for connectionThread to finish")); thread->wait(); @@ -393,19 +372,19 @@ void Connection::waitTillFinish() { } int32 Connection::state() const { - t_assert(data != nullptr && thread != nullptr); + Expects(data != nullptr && thread != nullptr); return data->getState(); } QString Connection::transport() const { - t_assert(data != nullptr && thread != nullptr); + Expects(data != nullptr && thread != nullptr); return data->transport(); } Connection::~Connection() { - t_assert(data == nullptr); + Expects(data == nullptr); if (thread) { waitTillFinish(); } @@ -477,10 +456,10 @@ ConnectionPrivate::ConnectionPrivate(Instance *instance, QThread *thread, Connec retryTimer.moveToThread(thread); moveToThread(thread); - t_assert(_shiftedDcId != 0); + Expects(_shiftedDcId != 0); - connect(thread, SIGNAL(started()), this, SLOT(socketStart())); - connect(thread, SIGNAL(finished()), this, SLOT(doFinish())); + connect(thread, &QThread::started, this, [this] { connectToServer(); }); + connect(thread, &QThread::finished, this, [this] { finishAndDestroy(); }); connect(this, SIGNAL(finished(internal::Connection*)), _instance, SLOT(connectionFinished(internal::Connection*)), Qt::QueuedConnection); connect(&retryTimer, SIGNAL(timeout()), this, SLOT(retryByTimer())); @@ -515,7 +494,11 @@ ConnectionPrivate::ConnectionPrivate(Instance *instance, QThread *thread, Connec } void ConnectionPrivate::onConfigLoaded() { - socketStart(true); + connectToServer(true); +} + +void ConnectionPrivate::onCDNConfigLoaded() { + restart(); } int32 ConnectionPrivate::getShiftedDcId() const { @@ -881,12 +864,14 @@ void ConnectionPrivate::tryToSend() { } } - MTPInitConnection initWrapperImpl, *initWrapper = &initWrapperImpl; + MTPInitConnection initWrapper; int32 initSize = 0, initSizeInInts = 0; if (needsLayer) { auto langCode = (cLang() == languageTest || cLang() == languageDefault) ? Sandbox::LangSystemISO() : str_const_toString(LanguageCodes[cLang()]); - initWrapperImpl = MTPInitConnection(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(langCode), mtpRequest()); - initSizeInInts = (initWrapper->innerLength() >> 2) + 2; + auto deviceModel = (_dcType == DcType::Cdn) ? "n/a" : cApiDeviceModel(); + auto systemVersion = (_dcType == DcType::Cdn) ? "n/a" : cApiSystemVersion(); + initWrapper = MTPInitConnection(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); } @@ -946,7 +931,7 @@ void ConnectionPrivate::tryToSend() { memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length wrappedRequest->push_back(mtpc_invokeWithLayer); wrappedRequest->push_back(MTP::internal::CurrentLayer); - initWrapper->write(*wrappedRequest); + initWrapper.write(*wrappedRequest); wrappedRequest->resize(wrappedRequest->size() + noWrapSize); memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime)); toSendRequest = wrappedRequest; @@ -978,7 +963,7 @@ void ConnectionPrivate::tryToSend() { initSerialized.reserve(initSizeInInts); initSerialized.push_back(mtpc_invokeWithLayer); 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->push_back(mtpc_msg_container); @@ -1082,7 +1067,7 @@ void ConnectionPrivate::retryByTimer() { } keyId = 0; } - socketStart(); + connectToServer(); } void ConnectionPrivate::restartNow() { @@ -1091,27 +1076,31 @@ void ConnectionPrivate::restartNow() { restart(); } -void ConnectionPrivate::socketStart(bool afterConfig) { +void ConnectionPrivate::connectToServer(bool afterConfig) { if (_finished) { - DEBUG_LOG(("MTP Error: socketStart() called for finished connection!")); + DEBUG_LOG(("MTP Error: connectToServer() called for finished connection!")); return; } - auto dcType = DcOptions::DcType::Regular; - auto isDownloadDc = isDownloadDcId(_shiftedDcId); - if (isDownloadDc) { // using media_only addresses only if key for this dc is already created + auto bareDc = bareDcId(_shiftedDcId); + _dcType = Messenger::Instance().dcOptions()->dcType(_shiftedDcId); + if (_dcType == DcType::MediaDownload) { // using media_only addresses only if key for this dc is already created QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData || sessionData->getKey()) { - dcType = DcOptions::DcType::MediaDownload; + if (!sessionData || !sessionData->getKey()) { + _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; auto kIPv4 = Variants::IPv4; auto kIPv6 = Variants::IPv6; auto kTcp = Variants::Tcp; 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 noIPv6 = (!Global::TryIPv6() || (variants.data[kIPv6][kHttp].port == 0)); 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)); 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); - QMetaObject::invokeMethod(_instance, "configLoadRequest", Qt::QueuedConnection); + InvokeQueued(_instance, [instance = _instance] { instance->configLoadRequest(); }); return; } @@ -1201,18 +1190,18 @@ void ConnectionPrivate::restart() { void ConnectionPrivate::onSentSome(uint64 size) { if (!_waitForReceivedTimer.isActive()) { - uint64 remain = _waitForReceived; + auto remain = static_cast(_waitForReceived); 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)); if (remain != _waitForReceived) { DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain)); } } if (isUploadDcId(_shiftedDcId)) { - remain *= MTPUploadSessionsCount; + remain *= kUploadSessionsCount; } else if (isDownloadDcId(_shiftedDcId)) { - remain *= MTPDownloadSessionsCount; + remain *= kDownloadSessionsCount; } _waitForReceivedTimer.start(remain); } @@ -1276,7 +1265,7 @@ void ConnectionPrivate::onWaitReceivedFailed() { if (retryTimer.isActive()) return; DEBUG_LOG(("MTP Info: immediate restart!")); - QTimer::singleShot(0, this, SLOT(socketStart())); + InvokeQueued(this, [this] { connectToServer(); }); } void ConnectionPrivate::onWaitConnectedFailed() { @@ -1287,7 +1276,7 @@ void ConnectionPrivate::onWaitConnectedFailed() { restarted = true; DEBUG_LOG(("MTP Info: immediate restart!")); - QTimer::singleShot(0, this, SLOT(socketStart())); + InvokeQueued(this, [this] { connectToServer(); }); } void ConnectionPrivate::onWaitIPv4Failed() { @@ -1319,13 +1308,18 @@ void ConnectionPrivate::doDisconnect() { restarted = false; } -void ConnectionPrivate::doFinish() { +void ConnectionPrivate::finishAndDestroy() { doDisconnect(); _finished = true; emit finished(_owner); deleteLater(); } +void ConnectionPrivate::requestCDNConfig() { + connect(_instance, SIGNAL(cdnConfigLoaded()), this, SLOT(onCDNConfigLoaded()), Qt::UniqueConnection); + InvokeQueued(_instance, [instance = _instance] { instance->cdnConfigLoadRequest(); }); +} + void ConnectionPrivate::handleReceived() { QReadLocker lockFinished(&sessionDataMutex); if (!sessionData) return; @@ -2046,16 +2040,20 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr return HandleResult::ResetSession; } - mtpBuffer update(end - from); - if (end > from) memcpy(update.data(), from, (end - from) * sizeof(mtpPrime)); + if (_dcType == DcType::Regular) { + mtpBuffer update(end - from); + if (end > from) memcpy(update.data(), from, (end - from) * sizeof(mtpPrime)); - QWriteLocker locker(sessionData->haveReceivedMutex()); - mtpResponseMap &haveReceived(sessionData->haveReceivedMap()); - mtpRequestId fakeRequestId = sessionData->nextFakeRequestId(); - haveReceived.insert(fakeRequestId, mtpResponse(update)); // notify main process about new updates + QWriteLocker locker(sessionData->haveReceivedMutex()); + mtpResponseMap &haveReceived(sessionData->haveReceivedMap()); + mtpRequestId fakeRequestId = sessionData->nextFakeRequestId(); + 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) { - LOG(("Message Error: unknown constructor %1").arg(cons)); // maybe new api?.. + 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?.. + } + } else { + LOG(("Message Error: unexpected updates in dcType: %1").arg(static_cast(_dcType))); } return HandleResult::Success; @@ -2430,27 +2428,17 @@ void ConnectionPrivate::pqAnswered() { return restart(); } - static MTP::internal::RSAPublicKeys RSAKeys = MTP::internal::InitRSAPublicKeys(); - const MTP::internal::RSAPublicKey *rsaKey = nullptr; - auto &fingerPrints = res_pq.c_resPQ().vserver_public_key_fingerprints.v; - for (auto &fingerPrint : fingerPrints) { - auto it = RSAKeys.constFind(static_cast(fingerPrint.v)); - if (it != RSAKeys.cend()) { - rsaKey = &it.value(); - break; + auto rsaKey = internal::RSAPublicKey(); + if (!Messenger::Instance().dcOptions()->getDcRSAKey(bareDcId(_shiftedDcId), res_pq.c_resPQ().vserver_public_key_fingerprints.v, &rsaKey)) { + if (_dcType == DcType::Cdn) { + LOG(("Warning: CDN public RSA key not found")); + requestCDNConfig(); + return; } - } - 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(", "))); + LOG(("AuthKey Error: could not choose public RSA key")); return restart(); } + t_assert(rsaKey.isValid()); _authKeyData->server_nonce = res_pq_data.vserver_nonce; _authKeyData->new_nonce = rand_value(); @@ -2477,14 +2465,14 @@ void ConnectionPrivate::pqAnswered() { MTPReq_DH_params req_DH_params; req_DH_params.vnonce = _authKeyData->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.vq = p_q_inner.c_p_q_inner_data().vq; req_DH_params.vencrypted_data = MTP_string(std::move(dhEncString)); 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 encSize = (p_q_inner_size >> 2) + 6; if (encSize >= 65) { @@ -2509,7 +2497,7 @@ std::string ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data, } auto dhEncString = std::string(); - if (!key->encrypt(reinterpret_cast(&encBuffer[0]) + 3, dhEncString)) { + if (!key.encrypt(reinterpret_cast(&encBuffer[0]) + 3, dhEncString)) { return std::string(); } return dhEncString; @@ -2872,17 +2860,7 @@ void ConnectionPrivate::onError4(qint32 errorCode) { LOG(("Protocol Error: -429 flood code returned!")); } if (_conn || !_conn6) { - destroyConn(); - _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(); - } + handleError(errorCode); } else { destroyConn(&_conn4); } @@ -2895,22 +2873,32 @@ void ConnectionPrivate::onError6(qint32 errorCode) { LOG(("Protocol Error: -429 flood code returned!")); } if (_conn || !_conn4) { - destroyConn(); - _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(); - } + handleError(errorCode); } else { 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() { } diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index a89552416..a1354fd92 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mtproto/core_types.h" #include "mtproto/auth_key.h" +#include "mtproto/dc_options.h" #include "core/single_timer.h" namespace MTP { @@ -128,7 +129,6 @@ public slots: void onReceivedSome(); void onReadyData(); - void socketStart(bool afterConfig = false); void onConnected4(); void onConnected6(); @@ -137,8 +137,6 @@ public slots: void onError4(qint32 errorCode); void onError6(qint32 errorCode); - void doFinish(); - // Auth key creation packet receive slots void pqAnswered(); void dhParamsAnswered(); @@ -153,10 +151,15 @@ public slots: void updateAuthKey(); void onConfigLoaded(); + void onCDNConfigLoaded(); private: + void connectToServer(bool afterConfig = false); void doDisconnect(); void restart(); + void finishAndDestroy(); + void requestCDNConfig(); + void handleError(int errorCode); void createConn(bool createIPv4, bool createIPv6); void destroyConn(AbstractConnection **conn = 0); // 0 - destory all @@ -182,10 +185,11 @@ private: 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); Instance *_instance = nullptr; + DcType _dcType = DcType::Regular; mutable QReadWriteLock stateConnMutex; int32 _state = DisconnectedState; diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp index 8fbda1645..dd6b6fb36 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.cpp +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -20,12 +20,55 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "mtproto/dc_options.h" +#include "storage/serialize_common.h" + namespace MTP { +class DcOptions::WriteLocker { +public: + WriteLocker(DcOptions *that) : _that(that), _lock(&_that->_useThroughLockers) { + } + ~WriteLocker() { + _that->computeCdnDcIds(); + } + +private: + gsl::not_null _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() { - QWriteLocker lock(&_mutex); + WriteLocker lock(this); _data.clear(); + readBuiltInPublicKeys(); + auto bdcs = builtInDcs(); for (auto i = 0, l = builtInDcsCount(); i != l; ++i) { auto flags = MTPDdcOption::Flags(0); @@ -53,7 +96,7 @@ void DcOptions::processFromList(const QVector &options, bool overwr auto shiftedIdsProcessed = std::vector(); shiftedIdsProcessed.reserve(options.size()); { - QWriteLocker lock(&_mutex); + WriteLocker lock(this); if (overwrite) { idsChanged.reserve(_data.size()); } @@ -107,22 +150,22 @@ void DcOptions::addFromList(const MTPVector &options) { processFromList(options.v, false); } -void DcOptions::addFromOther(const DcOptions &options) { +void DcOptions::addFromOther(DcOptions &&options) { if (this == &options || _immutable) { return; } auto idsChanged = std::vector(); { - QReadLocker lock(&options._mutex); + ReadLocker lock(&options); if (options._data.empty()) { return; } idsChanged.reserve(options._data.size()); { - QWriteLocker lock(&_mutex); - for (auto &item : options._data) { + WriteLocker lock(this); + for (auto &item : base::take(options._data)) { auto dcId = item.second.id; auto flags = item.second.flags; 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) { - QWriteLocker lock(&_mutex); + WriteLocker lock(this); applyOneGuarded(bareDcId(id), flags, ip, port); } @@ -167,7 +215,7 @@ QByteArray DcOptions::serialize() const { return DcOptions().serialize(); } - QReadLocker lock(&_mutex); + ReadLocker lock(this); auto size = sizeof(qint32); for (auto &item : _data) { @@ -175,6 +223,24 @@ QByteArray DcOptions::serialize() const { 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 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(); result.reserve(size); { @@ -192,6 +258,10 @@ QByteArray DcOptions::serialize() const { stream << qint32(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; } @@ -205,14 +275,14 @@ void DcOptions::constructFromSerialized(const QByteArray &serialized) { } QDataStream stream(&buffer); stream.setVersion(QDataStream::Qt_5_1); - qint32 count = 0; + auto count = qint32(0); stream >> count; if (stream.status() != QDataStream::Ok) { LOG(("MTP Error: Bad data for DcOptions::constructFromSerialized()")); return; } - QWriteLocker lock(&_mutex); + WriteLocker lock(this); _data.clear(); for (auto i = 0; i != count; ++i) { 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); } + + // 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(); { - QReadLocker lock(&_mutex); + ReadLocker lock(this); result.reserve(_data.size()); 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); } } @@ -244,50 +341,171 @@ DcOptions::Ids DcOptions::sortedDcIds() const { return result; } -DcId DcOptions::getDefaultDcId() const { - auto result = sortedDcIds(); - t_assert(!result.empty()); +DcType DcOptions::dcType(ShiftedDcId shiftedDcId) const { + ReadLocker lock(this); + 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 &fingerprints, internal::RSAPublicKey *result) const { + auto findKey = [&fingerprints, &result](const std::map &keys) { + for_const (auto &fingerprint, fingerprints) { + auto it = keys.find(static_cast(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 { - auto isMediaDownload = (type == DcType::MediaDownload); - int shifts[2][2][4] = { - { // IPv4 - { // TCP IPv4 - isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only) : -1, - qFlags(MTPDdcOption::Flag::f_tcpo_only), - isMediaDownload ? qFlags(MTPDdcOption::Flag::f_media_only) : -1, - 0 - }, { // HTTP IPv4 - -1, - -1, - isMediaDownload ? qFlags(MTPDdcOption::Flag::f_media_only) : -1, - 0 - }, - }, { // IPv6 - { // TCP IPv6 - isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6) : -1, - MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6, - isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1, - qFlags(MTPDdcOption::Flag::f_ipv6) - }, { // HTTP IPv6 - -1, - -1, - isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1, - qFlags(MTPDdcOption::Flag::f_ipv6) - }, - }, + auto lookupDesiredFlags = [type](int address, int protocol) -> std::vector { + switch (type) { + case DcType::Regular: { + switch (address) { + case Variants::IPv4: { + switch (protocol) { + case Variants::Tcp: return { + // Regular TCP IPv4 + qFlags(MTPDdcOption::Flag::f_tcpo_only), + MTPDdcOption::Flags(0) + }; + case Variants::Http: return { + // Regular HTTP IPv4 + MTPDdcOption::Flags(0), + }; + } + } break; + case Variants::IPv6: { + switch (protocol) { + case Variants::Tcp: return { + // Regular TCP IPv6 + (MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6), + 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(); { - QReadLocker lock(&_mutex); + ReadLocker lock(this); for (auto address = 0; address != Variants::AddressTypeCount; ++address) { for (auto protocol = 0; protocol != Variants::ProtocolCount; ++protocol) { - for (auto variant = 0; variant != base::array_size(shifts[address][protocol]); ++variant) { - auto shift = shifts[address][protocol][variant]; + auto desiredFlags = lookupDesiredFlags(address, protocol); + for (auto flags : desiredFlags) { + auto shift = static_cast(flags); if (shift < 0) continue; auto it = _data.find(shiftDcId(dcId, shift)); @@ -304,6 +522,15 @@ DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const { 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) { QVector options; @@ -372,7 +599,7 @@ bool DcOptions::writeToFile(const QString &path) const { QTextStream stream(&f); stream.setCodec("UTF-8"); - QReadLocker lock(&_mutex); + ReadLocker lock(this); for (auto &item : _data) { auto &endpoint = item.second; stream << endpoint.id << ' ' << QString::fromStdString(endpoint.ip) << ' ' << endpoint.port; diff --git a/Telegram/SourceFiles/mtproto/dc_options.h b/Telegram/SourceFiles/mtproto/dc_options.h index 64daab026..c693f3763 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.h +++ b/Telegram/SourceFiles/mtproto/dc_options.h @@ -21,12 +21,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "base/observer.h" +#include "mtproto/rsa_public_key.h" #include #include #include namespace MTP { +enum class DcType { + Regular, + MediaDownload, + Cdn, +}; class DcOptions { public: // construct methods don't notify "changed" subscribers. @@ -41,10 +47,9 @@ public: } void setFromList(const MTPVector &options); void addFromList(const MTPVector &options); - void addFromOther(const DcOptions &options); + void addFromOther(DcOptions &&options); - Ids sortedDcIds() const; - DcId getDefaultDcId() const; + Ids configEnumDcIds() const; struct Endpoint { std::string ip; @@ -64,11 +69,12 @@ public: }; Endpoint data[AddressTypeCount][ProtocolCount]; }; - enum class DcType { - Regular, - MediaDownload, - }; 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 &fingerprints, internal::RSAPublicKey *result) const; // Debug feature for now. bool loadFromFile(const QString &path); @@ -87,9 +93,21 @@ private: bool applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std::string &ip, int port); void processFromList(const QVector &options, bool overwrite); + void computeCdnDcIds(); - std::map _data; - mutable QReadWriteLock _mutex; + void readBuiltInPublicKeys(); + + class WriteLocker; + friend class WriteLocker; + + class ReadLocker; + friend class ReadLocker; + + std::map _data; + std::set _cdnDcIds; + std::map _publicKeys; + std::map> _cdnPublicKeys; + mutable QReadWriteLock _useThroughLockers; mutable base::Observable _changed; diff --git a/Telegram/SourceFiles/mtproto/dcenter.cpp b/Telegram/SourceFiles/mtproto/dcenter.cpp index dfa91ddf5..6fcb17c67 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.cpp +++ b/Telegram/SourceFiles/mtproto/dcenter.cpp @@ -78,7 +78,7 @@ void ConfigLoader::load() { sendRequest(_instance->mainDcId()); _enumDCTimer.start(kEnumerateDcTimeout); } else { - auto ids = _instance->dcOptions()->sortedDcIds(); + auto ids = _instance->dcOptions()->configEnumDcIds(); t_assert(!ids.empty()); _enumCurrent = ids.front(); enumDC(); @@ -108,7 +108,7 @@ void ConfigLoader::enumDC() { } else { _instance->killSession(MTP::configDcId(_enumCurrent)); } - auto ids = _instance->dcOptions()->sortedDcIds(); + auto ids = _instance->dcOptions()->configEnumDcIds(); t_assert(!ids.empty()); auto i = std::find(ids.cbegin(), ids.cend(), _enumCurrent); diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index 9ee5d273b..3331b0d74 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -86,10 +86,13 @@ constexpr ShiftedDcId logoutDcId(DcId dcId) { return shiftDcId(dcId, internal::kLogoutDcShift); } +constexpr auto kDownloadSessionsCount = 2; +constexpr auto kUploadSessionsCount = 2; + namespace internal { 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); }; @@ -97,18 +100,22 @@ constexpr ShiftedDcId downloadDcId(DcId dcId, int index) { // send(req, callbacks, MTP::downloadDcId(dc, index)) - for download shifted dc id inline ShiftedDcId downloadDcId(DcId dcId, int index) { - t_assert(index >= 0 && index < MTPDownloadSessionsCount); + Expects(index >= 0 && index < kDownloadSessionsCount); return internal::downloadDcId(dcId, index); } -constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) { - return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, MTPDownloadSessionsCount - 1) + internal::kDcShift); +inline constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) { + 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 { 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); }; @@ -117,12 +124,12 @@ constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { // send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id // uploading always to the main dc so bareDcId == 0 inline ShiftedDcId uploadDcId(int index) { - t_assert(index >= 0 && index < MTPUploadSessionsCount); + Expects(index >= 0 && index < kUploadSessionsCount); return internal::uploadDcId(0, index); }; 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) { diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index ada343106..59533b365 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -25,10 +25,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "messenger.h" #include "mtproto/connection.h" +#include "mtproto/sender.h" +#include "mtproto/rsa_public_key.h" namespace MTP { -class Instance::Private { +class Instance::Private : public Sender { public: Private(Instance *instance, DcOptions *options, Instance::Mode mode); @@ -45,6 +47,7 @@ public: DcOptions *dcOptions(); void configLoadRequest(); + void cdnConfigLoadRequest(); void restart(); void restart(ShiftedDcId shiftedDcId); @@ -116,6 +119,9 @@ private: void configLoadDone(const MTPConfig &result); bool configLoadFail(const RPCError &error); + void cdnConfigLoadDone(const MTPCdnConfig &result); + bool cdnConfigLoadFail(const RPCError &error); + void checkDelayedRequests(); Instance *_instance = nullptr; @@ -133,6 +139,7 @@ private: base::set_of_unique_ptr _quittingConnections; std::unique_ptr _configLoader; + mtpRequestId _cdnConfigLoadRequestId = 0; std::map _keysForWrite; 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) , _mode(mode) { } @@ -188,7 +195,6 @@ void Instance::Private::start(Config &&config) { for (auto &key : config.keys) { auto dcId = key->dcId(); - auto shiftedDcId = dcId; if (isKeysDestroyer()) { shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId); @@ -273,6 +279,22 @@ void Instance::Private::configLoadRequest() { _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() { for (auto &session : _sessions) { session.second->restart(); @@ -423,7 +445,7 @@ void Instance::Private::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFai } } for (auto dcId : dcIds) { - if (dcId != mainDcId()) { + if (dcId != mainDcId() && dcOptions()->dcType(dcId) != DcType::Cdn) { auto shiftedDcId = MTP::logoutDcId(dcId); auto requestId = _instance->send(MTPauth_LogOut(), rpcDone([this](mtpRequestId requestId) { logoutGuestDone(requestId); @@ -1204,6 +1226,8 @@ void Instance::Private::prepareToDestroy() { // It accesses Instance in destructor, so it should be destroyed first. _configLoader.reset(); + requestCancellingDiscard(); + for (auto &session : base::take(_sessions)) { session.second->kill(); } @@ -1233,6 +1257,10 @@ void Instance::configLoadRequest() { _private->configLoadRequest(); } +void Instance::cdnConfigLoadRequest() { + _private->cdnConfigLoadRequest(); +} + void Instance::connectionFinished(internal::Connection *connection) { _private->connectionFinished(connection); } diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.h b/Telegram/SourceFiles/mtproto/mtp_instance.h index eb70bf3a5..4202a76a5 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.h +++ b/Telegram/SourceFiles/mtproto/mtp_instance.h @@ -119,14 +119,18 @@ public: bool isKeysDestroyer() const; void scheduleKeyDestroy(ShiftedDcId shiftedDcId); + void configLoadRequest(); + + void cdnConfigLoadRequest(); + ~Instance(); public slots: - void configLoadRequest(); void connectionFinished(internal::Connection *connection); signals: void configLoaded(); + void cdnConfigLoaded(); void keyDestroyed(qint32 shiftedDcId); void allKeysDestroyed(); diff --git a/Telegram/SourceFiles/mtproto/rsa_public_key.cpp b/Telegram/SourceFiles/mtproto/rsa_public_key.cpp index 96e155902..c0b667cd8 100644 --- a/Telegram/SourceFiles/mtproto/rsa_public_key.cpp +++ b/Telegram/SourceFiles/mtproto/rsa_public_key.cpp @@ -25,55 +25,110 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include +using std::string; + namespace MTP { namespace internal { -struct RSAPublicKey::Impl { - Impl(const char *key) : rsa(PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast(key), -1), 0, 0, 0)) { +class RSAPublicKey::Private { +public: + Private(base::const_byte_span key) : _rsa(PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast(key.data()), key.size()), 0, 0, 0)) { + if (_rsa) { + computeFingerprint(); + } } - ~Impl() { - RSA_free(rsa); + Private(const QByteArray &n, const QByteArray &e) : _rsa(RSA_new()) { + 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; - uint64 fp = 0; + QByteArray getN() const { + 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(data), reinterpret_cast(&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(BN_num_bytes(number)); + auto result = QByteArray(size, 0); + BN_bn2bin(number, reinterpret_cast(result.data())); + return result; + } + + RSA *_rsa = nullptr; + uint64 _fingerprint = 0; + }; -RSAPublicKey::RSAPublicKey(const char *key) : impl_(new Impl(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); +RSAPublicKey::RSAPublicKey(base::const_byte_span key) : _private(std::make_shared(key)) { } -uint64 RSAPublicKey::getFingerPrint() const { - return impl_->fp; +RSAPublicKey::RSAPublicKey(const QByteArray &n, const QByteArray &e) : _private(std::make_shared(n, e)) { } 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()); + return _private->getFingerPrint(); +} - result.resize(256); - int res = RSA_public_encrypt(256, reinterpret_cast(data), reinterpret_cast(&result[0]), impl_->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; +QByteArray RSAPublicKey::getN() const { + Expects(isValid()); + return _private->getN(); +} + +QByteArray RSAPublicKey::getE() const { + Expects(isValid()); + return _private->getE(); +} + +bool RSAPublicKey::encrypt(const void *data, string &result) const { + Expects(isValid()); + return _private->encrypt(data, result); } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/rsa_public_key.h b/Telegram/SourceFiles/mtproto/rsa_public_key.h index bf29436ce..e5adc183a 100644 --- a/Telegram/SourceFiles/mtproto/rsa_public_key.h +++ b/Telegram/SourceFiles/mtproto/rsa_public_key.h @@ -26,21 +26,26 @@ namespace internal { // this class holds an RSA public key and can encrypt fixed-size messages with it class RSAPublicKey final { public: - // 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; uint64 getFingerPrint() const; + QByteArray getN() const; + QByteArray getE() const; // data has exactly 256 chars to be encrypted bool encrypt(const void *data, std::string &result) const; private: - - struct Impl; - typedef QSharedPointer ImplPtr; - ImplPtr impl_; + class Private; + std::shared_ptr _private; }; diff --git a/Telegram/SourceFiles/mtproto/sender.h b/Telegram/SourceFiles/mtproto/sender.h index 05bccacfe..9eb13297a 100644 --- a/Telegram/SourceFiles/mtproto/sender.h +++ b/Telegram/SourceFiles/mtproto/sender.h @@ -290,6 +290,11 @@ public: void requestSendDelayed() { MTP::sendAnything(); } + void requestCancellingDiscard() { + for (auto &request : _requests) { + request.handled(); + } + } private: class RequestWrap { diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index 48aaa2a67..366d992a9 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -42,45 +42,68 @@ void Downloader::clearPriorities() { ++_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 { -struct DataRequested { - DataRequested() { - memset(v, 0, sizeof(v)); - } - int64 v[MTPDownloadSessionsCount]; -}; -QMap DataRequestedMap; - constexpr auto kDownloadPhotoPartSize = 64 * 1024; // 64kb for photo 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 struct FileLoaderQueue { - FileLoaderQueue(int32 limit) : limit(limit) { + FileLoaderQueue(int queriesLimit) : queriesLimit(queriesLimit) { } - int queries = 0; - int limit = 0; + int queriesCount = 0; + int queriesLimit = 0; FileLoader *start = nullptr; FileLoader *end = nullptr; }; namespace { - typedef QMap LoaderQueues; - LoaderQueues queues; - FileLoaderQueue _webQueue(MaxWebFileQueries); +using LoaderQueues = QMap; +LoaderQueues queues; - QThread *_webLoadThread = 0; - WebLoadManager *_webLoadManager = 0; - WebLoadManager *webLoadManager() { - return (_webLoadManager && _webLoadManager != FinishedWebLoadManager) ? _webLoadManager : 0; - } - WebLoadMainManager *_webLoadMainManager = 0; +FileLoaderQueue _webQueue(kMaxWebFileQueries); + +QThread *_webLoadThread = nullptr; +WebLoadManager *_webLoadManager = nullptr; +WebLoadManager *webLoadManager() { + 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) : _downloader(&AuthSession::Current().downloader()) @@ -144,10 +167,14 @@ void FileLoader::permitLoadFromCloud() { } void FileLoader::loadNext() { - if (_queue->queries >= _queue->limit) return; - for (FileLoader *i = _queue->start; i;) { + if (_queue->queriesCount >= _queue->queriesLimit) { + return; + } + for (auto i = _queue->start; i;) { if (i->loadPart()) { - if (_queue->queries >= _queue->limit) return; + if (_queue->queriesCount >= _queue->queriesLimit) { + return; + } } else { i = i->_next; } @@ -357,44 +384,46 @@ void FileLoader::cancel(bool fail) { } 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(); } mtpFileLoader::mtpFileLoader(const StorageImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading) : FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading) -, _dc(location->dc()) +, _dcId(location->dc()) , _location(location) { - auto shiftedDcId = MTP::downloadDcId(_dc, 0); + auto shiftedDcId = MTP::downloadDcId(_dcId, 0); auto i = queues.find(shiftedDcId); if (i == queues.cend()) { - i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries)); + i = queues.insert(shiftedDcId, FileLoaderQueue(kMaxFileQueries)); } _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) : FileLoader(to, size, type, toCache, fromCloud, autoLoading) -, _dc(dc) +, _dcId(dc) , _id(id) , _accessHash(accessHash) , _version(version) { - auto shiftedDcId = MTP::downloadDcId(_dc, 0); + auto shiftedDcId = MTP::downloadDcId(_dcId, 0); auto i = queues.find(shiftedDcId); if (i == queues.cend()) { - i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries)); + i = queues.insert(shiftedDcId, FileLoaderQueue(kMaxFileQueries)); } _queue = &i.value(); } mtpFileLoader::mtpFileLoader(const WebFileImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading) : FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading) -, _dc(location->dc()) +, _dcId(location->dc()) , _urlLocation(location) { - auto shiftedDcId = MTP::downloadDcId(_dc, 0); + auto shiftedDcId = MTP::downloadDcId(_dcId, 0); auto i = queues.find(shiftedDcId); if (i == queues.cend()) { - i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries)); + i = queues.insert(shiftedDcId, FileLoaderQueue(kMaxFileQueries)); } _queue = &i.value(); } @@ -403,58 +432,15 @@ int32 mtpFileLoader::currentOffset(bool includeSkipped) const { return (_fileIsOpen ? _file.size() : _data.size()) - (includeSkipped ? 0 : _skippedBytes); } -namespace { - QString serializereqs(const QMap &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() { - if (_finished || _lastComplete || (!_dcIndexByRequest.isEmpty() && !_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)); - } + if (_finished || _lastComplete || (!_sentRequests.empty() && !_size)) { return false; - } - 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))); - } + } else if (_size && _nextRequestOffset >= _size) { return false; } - auto offset = _nextRequestOffset; - auto dcIndex = 0; - 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))); - } - + makeRequest(_nextRequestOffset); + _nextRequestOffset += partSize(); return true; } @@ -465,42 +451,55 @@ int mtpFileLoader::partSize() const { return kDownloadDocumentPartSize; } -mtpRequestId mtpFileLoader::makeRequest(int offset, int dcIndex) { - auto limit = partSize(); - DataRequestedMap[_dc].v[dcIndex] += limit; - ++_queue->queries; - _nextRequestOffset += limit; - - 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); +mtpFileLoader::RequestData mtpFileLoader::prepareRequest(int offset) const { + auto result = RequestData(); + result.dcId = _cdnDcId ? _cdnDcId : _dcId; + result.dcIndex = _size ? _downloader->chooseDcIndexForRequest(result.dcId) : 0; + result.offset = offset; + return result; } -void mtpFileLoader::normalPartLoaded(int offset, const MTPupload_File &result, mtpRequestId req) { - if (result.type() != mtpc_upload_file) { - if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): bad cons received! %2").arg(_id).arg(result.type())); +void mtpFileLoader::makeRequest(int offset) { + auto requestData = prepareRequest(offset); + auto send = [this, &requestData] { + 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)); - return partLoaded(offset, bytes, req); + return partLoaded(offset, bytes); } -void mtpFileLoader::webPartLoaded(int offset, const MTPupload_WebFile &result, mtpRequestId req) { - if (result.type() != mtpc_upload_webFile) { - if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): bad cons received! %2").arg(_id).arg(result.type())); - } - return cancel(true); - } +void mtpFileLoader::webPartLoaded(const MTPupload_WebFile &result, mtpRequestId requestId) { + Expects(result.type() == mtpc_upload_webFile); + + auto offset = finishSentRequestGetOffset(requestId); auto &webFile = result.c_upload_webFile(); if (!_size) { _size = webFile.vsize.v; @@ -509,32 +508,72 @@ void mtpFileLoader::webPartLoaded(int offset, const MTPupload_WebFile &result, m return cancel(true); } 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) { - auto i = _dcIndexByRequest.find(req); - if (i == _dcIndexByRequest.cend()) { - if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): request req=%2 for offset=%3 not found in _requests=%4").arg(_id).arg(req).arg(offset).arg(serializereqs(_dcIndexByRequest))); - } - return loadNext(); +void mtpFileLoader::cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId requestId) { + auto offset = finishSentRequestGetOffset(requestId); + if (result.type() == mtpc_upload_cdnFileReuploadNeeded) { + auto requestData = RequestData(); + requestData.dcId = _dcId; + requestData.dcIndex = 0; + 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 dcIndex = i.value(); - DataRequestedMap[_dc].v[dcIndex] -= limit; + auto key = gsl::as_bytes(gsl::make_span(_cdnEncryptionKey)); + auto iv = gsl::as_bytes(gsl::make_span(_cdnEncryptionIV)); + Expects(key.size() == MTP::CTRState::KeySize); + Expects(iv.size() == MTP::CTRState::IvecSize); - --_queue->queries; - _dcIndexByRequest.erase(i); + auto state = MTP::CTRState(); + auto ivec = gsl::as_writeable_bytes(gsl::make_span(state.ivec)); + std::copy(iv.begin(), iv.end(), ivec.begin()); - if (DebugLogging::FileLoader() && _id) { - 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))); - } + auto counterOffset = static_cast(offset) >> 4; + state.ivec[15] = static_cast(counterOffset & 0xFF); + state.ivec[14] = static_cast((counterOffset >> 8) & 0xFF); + state.ivec[13] = static_cast((counterOffset >> 16) & 0xFF); + state.ivec[12] = static_cast((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 (_fileIsOpen) { - int64 fsize = _file.size(); + auto fsize = _file.size(); if (offset < fsize) { _skippedBytes -= bytes.size(); } 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 _lastComplete = true; } - if (_dcIndexByRequest.isEmpty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) { + if (_sentRequests.empty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) { if (!_fname.isEmpty() && (_toCache == LoadToCacheAsWell)) { if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly); if (!_fileIsOpen) { @@ -584,15 +623,11 @@ void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes, mtpReque } removeFromQueue(); - if (!_queue->queries) { - App::app()->killDownloadSessionsStart(_dc); - } - if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { if (_urlLocation) { Local::writeImage(storageKey(*_urlLocation), StorageImageSaved(_data)); } else if (_locationType != UnknownFileLocation) { // audio, video, document - MediaKey mkey = mediaKey(_locationType, _dc, _id, _version); + auto mkey = mediaKey(_locationType, _dcId, _id, _version); if (!_fname.isEmpty()) { 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)); } } - } 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) { _downloader->taskFinished().notify(); @@ -628,22 +659,59 @@ bool mtpFileLoader::partFailed(const RPCError &error) { 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() { - if (_dcIndexByRequest.isEmpty()) return; - - auto limit = partSize(); - DataRequested &dr(DataRequestedMap[_dc]); - for (auto i = _dcIndexByRequest.cbegin(), e = _dcIndexByRequest.cend(); i != e; ++i) { - MTP::cancel(i.key()); - int32 dcIndex = i.value(); - dr.v[dcIndex] -= limit; + while (!_sentRequests.empty()) { + auto requestId = _sentRequests.begin()->first; + MTP::cancel(requestId); + finishSentRequestGetOffset(requestId); } - _queue->queries -= _dcIndexByRequest.size(); - _dcIndexByRequest.clear(); +} - if (!_queue->queries) { - Messenger::Instance().killDownloadSessionsStart(_dc); +void mtpFileLoader::switchToCDN(int offset, const MTPDupload_fileCdnRedirect &redirect) { + 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(); + 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() { @@ -660,7 +728,7 @@ bool mtpFileLoader::tryLoadLocal() { _localTaskId = Local::startImageLoad(storageKey(*_location), this); } else { if (_toCache == LoadToCacheAsWell) { - MediaKey mkey = mediaKey(_locationType, _dc, _id, _version); + MediaKey mkey = mediaKey(_locationType, _dcId, _id, _version); if (_locationType == DocumentFileLocation) { _localTaskId = Local::startStickerImageLoad(mkey, this); } else if (_locationType == AudioFileLocation) { diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index c0320aa06..5bc1a314f 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -40,6 +40,9 @@ public: return _taskFinishedObservable; } + void requestedAmountIncrement(MTP::DcId dcId, int index, int amount); + int chooseDcIndexForRequest(MTP::DcId dcId) const; + private: base::Observable _taskFinishedObservable; int _priority = 1; @@ -47,6 +50,9 @@ private: SingleQueuedInvokation _delayedLoadersDestroyer; std::vector> _delayedDestroyedLoaders; + using RequestedInDc = std::array; + std::map _requestedBytesAmount; + }; } // namespace Storage @@ -134,11 +140,11 @@ signals: protected: void readImage(const QSize &shrinkBox) const; - Storage::Downloader *_downloader = nullptr; + gsl::not_null _downloader; FileLoader *_prev = nullptr; FileLoader *_next = nullptr; int _priority = 0; - FileLoaderQueue *_queue; + FileLoaderQueue *_queue = nullptr; bool _paused = false; bool _autoLoading = false; @@ -198,26 +204,41 @@ public: ~mtpFileLoader(); private: + struct RequestData { + MTP::DcId dcId = 0; + int dcIndex = 0; + int offset = 0; + }; + bool tryLoadLocal() override; void cancelRequests() override; int partSize() const; - mtpRequestId makeRequest(int offset, int dcIndex); - - QMap _dcIndexByRequest; + RequestData prepareRequest(int offset) const; + void makeRequest(int offset); bool loadPart() override; - void normalPartLoaded(int offset, const MTPupload_File &result, mtpRequestId req); - void webPartLoaded(int offset, const MTPupload_WebFile &result, mtpRequestId req); + void normalPartLoaded(const MTPupload_File &result, mtpRequestId requestId); + 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 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 _sentRequests; bool _lastComplete = false; int32 _skippedBytes = 0; int32 _nextRequestOffset = 0; - int32 _dc; // for photo locations + MTP::DcId _dcId = 0; // for photo locations const StorageImageLocation *_location = nullptr; uint64 _id = 0; // for document locations @@ -226,6 +247,11 @@ private: const WebFileImageLocation *_urlLocation = nullptr; // for webdocument locations + MTP::DcId _cdnDcId = 0; + QByteArray _cdnToken; + QByteArray _cdnEncryptionKey; + QByteArray _cdnEncryptionIV; + }; class webFileLoaderPrivate; diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index 963035466..b7bd87a15 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -20,6 +20,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #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) { memset(sentSizes, 0, sizeof(sentSizes)); nextTimer.setSingleShot(true); @@ -88,7 +94,7 @@ void FileUploader::currentFailed() { dcMap.clear(); uploading = FullMsgId(); sentSize = 0; - for (int i = 0; i < MTPUploadSessionsCount; ++i) { + for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { sentSizes[i] = 0; } @@ -96,13 +102,13 @@ void FileUploader::currentFailed() { } void FileUploader::killSessions() { - for (int i = 0; i < MTPUploadSessionsCount; ++i) { + for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { MTP::stopSession(MTP::uploadDcId(i)); } } void FileUploader::sendNext() { - if (sentSize >= MaxUploadFileParallelSize || _paused.msg) return; + if (sentSize >= kMaxUploadFileParallelSize || _paused.msg) return; bool killing = killSessionsTimer.isActive(); if (queue.isEmpty()) { @@ -123,7 +129,7 @@ void FileUploader::sendNext() { uploading = i.key(); } int todc = 0; - for (int dc = 1; dc < MTPUploadSessionsCount; ++dc) { + for (int dc = 1; dc < MTP::kUploadSessionsCount; ++dc) { if (sentSizes[dc] < sentSizes[todc]) { todc = dc; } @@ -246,7 +252,7 @@ void FileUploader::clear() { docRequestsSent.clear(); dcMap.clear(); sentSize = 0; - for (int32 i = 0; i < MTPUploadSessionsCount; ++i) { + for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { MTP::stopSession(MTP::uploadDcId(i)); sentSizes[i] = 0; } diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 7ff4a3057..b5db7e841 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -130,7 +130,7 @@ private: QMap docRequestsSent; QMap dcMap; uint32 sentSize; - uint32 sentSizes[MTPUploadSessionsCount]; + uint32 sentSizes[MTP::kUploadSessionsCount]; FullMsgId uploading, _paused; Queue queue; diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 25cf43864..4465ef3b2 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -458,7 +458,8 @@ void FileLoadTask::process() { if (video->isGifv) { 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) ? video->thumbnail.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation) diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index de5cceb12..7a98bf509 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -853,8 +853,8 @@ struct ReadSettingsContext { MTP::DcOptions dcOptions; }; -void applyReadContext(const ReadSettingsContext &context) { - Messenger::Instance().dcOptions()->addFromOther(context.dcOptions); +void applyReadContext(ReadSettingsContext &&context) { + Messenger::Instance().dcOptions()->addFromOther(std::move(context.dcOptions)); } 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...")); _readOldUserSettings(true, context); - applyReadContext(context); + applyReadContext(std::move(context)); return _writeUserSettings(); } @@ -1822,7 +1822,7 @@ void _readUserSettings() { _readingUserSettings = false; LOG(("App Info: encrypted user settings read.")); - applyReadContext(context); + applyReadContext(std::move(context)); } void _writeMtpData() { @@ -1847,7 +1847,7 @@ void _readMtpData() { if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), FileOption::Safe)) { if (LocalKey) { _readOldMtpData(true, context); - applyReadContext(context); + applyReadContext(std::move(context)); _writeMtpData(); } @@ -1866,7 +1866,7 @@ void _readMtpData() { return _writeMtpData(); } } - applyReadContext(context); + applyReadContext(std::move(context)); } ReadMapState _readMap(const QByteArray &pass) { @@ -2229,7 +2229,7 @@ void start() { _readOldSettings(true, context); _readOldUserSettings(false, context); // needed further in _readUserSettings _readOldMtpData(false, context); // needed further in _readMtpData - applyReadContext(context); + applyReadContext(std::move(context)); return writeSettings(); } @@ -2271,7 +2271,7 @@ void start() { readTheme(); - applyReadContext(context); + applyReadContext(std::move(context)); } void writeSettings() { diff --git a/Telegram/SourceFiles/storage/serialize_document.cpp b/Telegram/SourceFiles/storage/serialize_document.cpp index 2ee5489c2..786448905 100644 --- a/Telegram/SourceFiles/storage/serialize_document.cpp +++ b/Telegram/SourceFiles/storage/serialize_document.cpp @@ -118,7 +118,8 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream & } if (width > 0 && height > 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 { attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); }