API scheme updated to layer 66.

Support CDN file download.
This commit is contained in:
John Preston 2017-03-23 19:11:35 +03:00
parent 7dd24a30b5
commit 8d28d0691f
26 changed files with 873 additions and 418 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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())));
}

View File

@ -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")) {

View File

@ -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));
}

View File

@ -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);

View File

@ -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.

View File

@ -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() {
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

View File

@ -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();

View File

@ -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

View File

@ -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;
};

View File

@ -290,6 +290,11 @@ public:
void requestSendDelayed() {
MTP::sendAnything();
}
void requestCancellingDiscard() {
for (auto &request : _requests) {
request.handled();
}
}
private:
class RequestWrap {

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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() {

View File

@ -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)));
}