mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
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;
|
||||
|
||||
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<Photo> users:Vector<User> =
|
||||
photos.photo#20212ca8 photo:Photo users:Vector<User> = 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<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;
|
||||
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
|
||||
sendMessageGamePlayAction#dd6a8f48 = SendMessageAction;
|
||||
sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
|
||||
sendMessageUploadRoundAction#bb718624 = SendMessageAction;
|
||||
|
||||
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;
|
||||
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<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---
|
||||
|
||||
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<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.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
|
||||
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
|
||||
|
@ -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 <typename Enum>
|
||||
inline QFlags<Enum> qFlags(Enum v) {
|
||||
inline constexpr QFlags<Enum> 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) {
|
||||
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())));
|
||||
}
|
||||
|
@ -335,14 +335,16 @@ void Result::createDocument() {
|
||||
QString mime = _content_type;
|
||||
|
||||
QVector<MTPDocumentAttribute> 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")) {
|
||||
|
@ -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<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));
|
||||
}
|
||||
|
@ -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<TimeMs>(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);
|
||||
|
@ -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<FullMsgId, PeerId> photoUpdates;
|
||||
|
||||
QMap<int32, TimeMs> killDownloadSessionTimes;
|
||||
QMap<MTP::DcId, TimeMs> killDownloadSessionTimes;
|
||||
SingleTimer killDownloadSessionsTimer;
|
||||
|
||||
// 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
|
||||
|
||||
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<Thread>();
|
||||
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() {
|
||||
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<mtpRequest> initWrapperImpl, *initWrapper = &initWrapperImpl;
|
||||
MTPInitConnection<mtpRequest> initWrapper;
|
||||
int32 initSize = 0, initSizeInInts = 0;
|
||||
if (needsLayer) {
|
||||
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());
|
||||
initSizeInInts = (initWrapper->innerLength() >> 2) + 2;
|
||||
auto deviceModel = (_dcType == DcType::Cdn) ? "n/a" : cApiDeviceModel();
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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<uint64>(_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<int>(_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<uint64>(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<MTPint256>();
|
||||
@ -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<const char*>(&encBuffer[0]) + 3, dhEncString)) {
|
||||
if (!key.encrypt(reinterpret_cast<const char*>(&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() {
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<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() {
|
||||
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<MTPDcOption> &options, bool overwr
|
||||
auto shiftedIdsProcessed = std::vector<ShiftedDcId>();
|
||||
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<MTPDcOption> &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<DcId>();
|
||||
{
|
||||
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<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();
|
||||
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<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 {
|
||||
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<MTPDdcOption::Flags> {
|
||||
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<int>(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<MTPDcOption> 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;
|
||||
|
@ -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 <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
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<MTPDcOption> &options);
|
||||
void addFromList(const MTPVector<MTPDcOption> &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<MTPlong> &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<MTPDcOption> &options, bool overwrite);
|
||||
void computeCdnDcIds();
|
||||
|
||||
std::map<int, Option> _data;
|
||||
mutable QReadWriteLock _mutex;
|
||||
void readBuiltInPublicKeys();
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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<internal::Connection> _quittingConnections;
|
||||
|
||||
std::unique_ptr<internal::ConfigLoader> _configLoader;
|
||||
mtpRequestId _cdnConfigLoadRequestId = 0;
|
||||
|
||||
std::map<DcId, AuthKeyPtr> _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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -25,55 +25,110 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
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<char*>(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<gsl::byte*>(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<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)) {
|
||||
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<Private>(key)) {
|
||||
}
|
||||
|
||||
uint64 RSAPublicKey::getFingerPrint() const {
|
||||
return impl_->fp;
|
||||
RSAPublicKey::RSAPublicKey(const QByteArray &n, const QByteArray &e) : _private(std::make_shared<Private>(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<const unsigned char*>(data), reinterpret_cast<uchar*>(&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
|
||||
|
@ -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<Impl> ImplPtr;
|
||||
ImplPtr impl_;
|
||||
class Private;
|
||||
std::shared_ptr<Private> _private;
|
||||
|
||||
};
|
||||
|
||||
|
@ -290,6 +290,11 @@ public:
|
||||
void requestSendDelayed() {
|
||||
MTP::sendAnything();
|
||||
}
|
||||
void requestCancellingDiscard() {
|
||||
for (auto &request : _requests) {
|
||||
request.handled();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class RequestWrap {
|
||||
|
@ -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<int32, DataRequested> 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<int32, FileLoaderQueue> LoaderQueues;
|
||||
LoaderQueues queues;
|
||||
|
||||
FileLoaderQueue _webQueue(MaxWebFileQueries);
|
||||
using LoaderQueues = QMap<int32, FileLoaderQueue>;
|
||||
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<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() {
|
||||
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<uint32>(offset) >> 4;
|
||||
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 (_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<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() {
|
||||
@ -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) {
|
||||
|
@ -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<void> _taskFinishedObservable;
|
||||
int _priority = 1;
|
||||
@ -47,6 +50,9 @@ private:
|
||||
SingleQueuedInvokation _delayedLoadersDestroyer;
|
||||
std::vector<std::unique_ptr<FileLoader>> _delayedDestroyedLoaders;
|
||||
|
||||
using RequestedInDc = std::array<int64, MTP::kDownloadSessionsCount>;
|
||||
std::map<MTP::DcId, RequestedInDc> _requestedBytesAmount;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Storage
|
||||
@ -134,11 +140,11 @@ signals:
|
||||
protected:
|
||||
void readImage(const QSize &shrinkBox) const;
|
||||
|
||||
Storage::Downloader *_downloader = nullptr;
|
||||
gsl::not_null<Storage::Downloader*> _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<mtpRequestId, int> _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<mtpRequestId, RequestData> _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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ private:
|
||||
QMap<mtpRequestId, int32> docRequestsSent;
|
||||
QMap<mtpRequestId, int32> dcMap;
|
||||
uint32 sentSize;
|
||||
uint32 sentSizes[MTPUploadSessionsCount];
|
||||
uint32 sentSizes[MTP::kUploadSessionsCount];
|
||||
|
||||
FullMsgId uploading, _paused;
|
||||
Queue queue;
|
||||
|
@ -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)
|
||||
|
@ -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() {
|
||||
|
@ -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)));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user