diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 82ee6ba777..47981d4e08 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -922,7 +922,7 @@ float64 StickersBox::Inner::aboveShadowOpacity() const { } void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { - auto pressed = base::take(_pressed, -1); + auto pressed = std::exchange(_pressed, -1); if (_section != Section::Installed && _selected < 0 && pressed >= 0) { setCursor(style::cur_default); diff --git a/Telegram/SourceFiles/codegen/style/generator.cpp b/Telegram/SourceFiles/codegen/style/generator.cpp index 92bf1b6db2..9837c37b8f 100644 --- a/Telegram/SourceFiles/codegen/style/generator.cpp +++ b/Telegram/SourceFiles/codegen/style/generator.cpp @@ -772,7 +772,7 @@ void palette::finalize() {\n\ auto result = module_.enumVariables([this, &indexInPalette, &checksumString, &dataRows, &names](const Variable &variable) -> bool { auto name = variable.name.back(); auto index = indexInPalette++; - paletteIndices_.insert(std::make_pair(name, index)); + paletteIndices_.emplace(name, index); if (variable.value.type().tag != structure::TypeTag::Color) { return false; } diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index afef280e08..b893bef39b 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -33,9 +33,8 @@ inline constexpr size_t array_size(const T(&)[N]) { } template -inline T take(T &source, T &&new_value = T()) { - std::swap(new_value, source); - return std::move(new_value); +inline T take(T &source) { + return std::exchange(source, T()); } namespace internal { diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp index c2e981faf9..5335079a5d 100644 --- a/Telegram/SourceFiles/fileuploader.cpp +++ b/Telegram/SourceFiles/fileuploader.cpp @@ -103,7 +103,7 @@ void FileUploader::currentFailed() { void FileUploader::killSessions() { for (int i = 0; i < MTPUploadSessionsCount; ++i) { - MTP::stopSession(MTP::uplDcId(i)); + MTP::stopSession(MTP::uploadDcId(i)); } } @@ -187,9 +187,9 @@ void FileUploader::sendNext() { } mtpRequestId requestId; if (i->docSize > UseBigFilesFrom) { - requestId = MTP::send(MTPupload_SaveBigFilePart(MTP_long(i->id()), MTP_int(i->docSentParts), MTP_int(i->docPartsCount), MTP_bytes(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::uplDcId(todc)); + requestId = MTP::send(MTPupload_SaveBigFilePart(MTP_long(i->id()), MTP_int(i->docSentParts), MTP_int(i->docPartsCount), MTP_bytes(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::uploadDcId(todc)); } else { - requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->id()), MTP_int(i->docSentParts), MTP_bytes(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::uplDcId(todc)); + requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(i->id()), MTP_int(i->docSentParts), MTP_bytes(toSend)), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::uploadDcId(todc)); } docRequestsSent.insert(requestId, i->docSentParts); dcMap.insert(requestId, todc); @@ -200,7 +200,7 @@ void FileUploader::sendNext() { } else { UploadFileParts::iterator part = parts.begin(); - mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(partsOfId), MTP_int(part.key()), MTP_bytes(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::uplDcId(todc)); + mtpRequestId requestId = MTP::send(MTPupload_SaveFilePart(MTP_long(partsOfId), MTP_int(part.key()), MTP_bytes(part.value())), rpcDone(&FileUploader::partLoaded), rpcFail(&FileUploader::partFailed), MTP::uploadDcId(todc)); requestsSent.insert(requestId, part.value()); dcMap.insert(requestId, todc); sentSize += part.value().size(); @@ -246,7 +246,7 @@ void FileUploader::clear() { dcMap.clear(); sentSize = 0; for (int32 i = 0; i < MTPUploadSessionsCount; ++i) { - MTP::stopSession(MTP::uplDcId(i)); + MTP::stopSession(MTP::uploadDcId(i)); sentSizes[i] = 0; } killSessionsTimer.stop(); diff --git a/Telegram/SourceFiles/intro/introphone.cpp b/Telegram/SourceFiles/intro/introphone.cpp index eaa86e924e..caefcd3ac7 100644 --- a/Telegram/SourceFiles/intro/introphone.cpp +++ b/Telegram/SourceFiles/intro/introphone.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/effects/widget_fade_wrap.h" #include "core/click_handler_types.h" #include "boxes/confirmbox.h" +#include "messenger.h" namespace Intro { @@ -57,6 +58,8 @@ PhoneWidget::PhoneWidget(QWidget *parent, Widget::Data *data) : Step(parent, dat _country->onChooseCountry(qsl("US")); } _changed = false; + + Messenger::Instance().destroyStaleAuthorizationKeys(); } void PhoneWidget::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 5f68a7fd96..4553851ca9 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -265,7 +265,7 @@ bool Widget::resetFail(const RPCError &error) { void Widget::gotNearestDC(const MTPNearestDc &result) { auto &nearest = result.c_nearestDc(); DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3").arg(nearest.vcountry.c_string().v.c_str()).arg(nearest.vnearest_dc.v).arg(nearest.vthis_dc.v)); - Messenger::Instance().mtp()->suggestMainDcId(nearest.vnearest_dc.v); + Messenger::Instance().suggestMainDcId(nearest.vnearest_dc.v); auto nearestCountry = qs(nearest.vcountry); if (getData()->country != nearestCountry) { getData()->country = nearestCountry; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index da9edeb8c3..274550a888 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -139,11 +139,16 @@ bool _checkStreamStatus(QDataStream &stream) { QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted; constexpr auto kLocalKeySize = MTP::AuthKey::kSize; -MTP::AuthKey _oldKey, _settingsKey, _passKey, _localKey; -void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKey *result) { - MTP::AuthKey::Data key = { 0 }; - int32 iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password - QByteArray newSalt; + +auto OldKey = MTP::AuthKeyPtr(); +auto SettingsKey = MTP::AuthKeyPtr(); +auto PassKey = MTP::AuthKeyPtr(); +auto LocalKey = MTP::AuthKeyPtr(); + +void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKeyPtr *result) { + auto key = MTP::AuthKey::Data { 0 }; + auto iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password + auto newSalt = QByteArray(); if (!salt) { newSalt.resize(LocalEncryptSaltSize); memset_rand(newSalt.data(), newSalt.size()); @@ -154,7 +159,7 @@ void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKey *resu PKCS5_PBKDF2_HMAC_SHA1(pass.constData(), pass.size(), (uchar*)salt->data(), salt->size(), iterCount, key.size(), (uchar*)key.data()); - result->setKey(key); + *result = std::make_shared(key); } struct FileReadDescriptor { @@ -261,7 +266,7 @@ struct FileWriteDescriptor { return true; } - static QByteArray prepareEncrypted(EncryptedDescriptor &data, const MTP::AuthKey &key = _localKey) { + static QByteArray prepareEncrypted(EncryptedDescriptor &data, const MTP::AuthKeyPtr &key = LocalKey) { data.finish(); QByteArray &toEncrypt(data.data); @@ -275,11 +280,11 @@ struct FileWriteDescriptor { *(uint32*)toEncrypt.data() = size; QByteArray encrypted(0x10 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data()); - MTP::aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 0x10, fullSize, &key, encrypted.constData()); + MTP::aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 0x10, fullSize, key, encrypted.constData()); return encrypted; } - bool writeEncrypted(EncryptedDescriptor &data, const MTP::AuthKey &key = _localKey) { + bool writeEncrypted(EncryptedDescriptor &data, const MTP::AuthKeyPtr &key = LocalKey) { return writeData(prepareEncrypted(data, key)); } void finish() { @@ -408,7 +413,7 @@ bool readFile(FileReadDescriptor &result, const QString &name, FileOptions optio return false; } -bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, const MTP::AuthKey &key = _localKey) { +bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, const MTP::AuthKeyPtr &key = LocalKey) { if (encrypted.size() <= 16 || (encrypted.size() & 0x0F)) { LOG(("App Error: bad encrypted part size: %1").arg(encrypted.size())); return false; @@ -418,7 +423,7 @@ bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, cons QByteArray decrypted; decrypted.resize(fullLen); const char *encryptedKey = encrypted.constData(), *encryptedData = encrypted.constData() + 16; - aesDecryptLocal(encryptedData, decrypted.data(), fullLen, &key, encryptedKey); + aesDecryptLocal(encryptedData, decrypted.data(), fullLen, key, encryptedKey); uchar sha1Buffer[20]; if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), encryptedKey, 16)) { LOG(("App Info: bad decrypt key, data not decrypted - incorrect password?")); @@ -444,7 +449,7 @@ bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, cons return true; } -bool readEncryptedFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKey &key = _localKey) { +bool readEncryptedFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKeyPtr &key = LocalKey) { if (!readFile(result, name, options)) { return false; } @@ -474,7 +479,7 @@ bool readEncryptedFile(FileReadDescriptor &result, const QString &name, FileOpti return true; } -bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKey &key = _localKey) { +bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKeyPtr &key = LocalKey) { return readEncryptedFile(result, toFilePart(fkey), options, key); } @@ -1536,7 +1541,7 @@ void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettings continue; } - createLocalKey(QByteArray(), &salt, &_oldKey); + createLocalKey(QByteArray(), &salt, &OldKey); if (data.size() <= 16 || (data.size() & 0x0F)) { LOG(("App Error: bad encrypted part size in old user config: %1").arg(data.size())); @@ -1545,7 +1550,7 @@ void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettings uint32 fullDataLen = data.size() - 16; decrypted.resize(fullDataLen); const char *dataKey = data.constData(), *encrypted = data.constData() + 16; - aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &_oldKey, dataKey); + aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, OldKey, dataKey); uchar sha1Buffer[20]; if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) { LOG(("App Error: bad decrypt key, data from old user config not decrypted")); @@ -1608,7 +1613,7 @@ void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsConte break; } - if (!_oldKey.created()) { + if (!OldKey) { LOG(("MTP Error: reading old encrypted keys without old key!")); continue; } @@ -1620,7 +1625,7 @@ void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsConte uint32 fullDataLen = data.size() - 16; decrypted.resize(fullDataLen); const char *dataKey = data.constData(), *encrypted = data.constData() + 16; - aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &_oldKey, dataKey); + aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, OldKey, dataKey); uchar sha1Buffer[20]; if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) { LOG(("MTP Error: bad decrypt key, data from old keys not decrypted")); @@ -1780,7 +1785,7 @@ void _readUserSettings() { void _writeMtpData() { FileWriteDescriptor mtp(toFilePart(_dataNameKey), FileOption::Safe); - if (!_localKey.created()) { + if (!LocalKey) { LOG(("App Error: localkey not created in _writeMtpData()")); return; } @@ -1798,7 +1803,7 @@ void _readMtpData() { ReadSettingsContext context; FileReadDescriptor mtp; if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), FileOption::Safe)) { - if (_localKey.created()) { + if (LocalKey) { _readOldMtpData(true, context); applyReadContext(context); @@ -1846,10 +1851,10 @@ ReadMapState _readMap(const QByteArray &pass) { LOG(("App Error: bad salt in map file, size: %1").arg(salt.size())); return ReadMapFailed; } - createLocalKey(pass, &salt, &_passKey); + createLocalKey(pass, &salt, &PassKey); EncryptedDescriptor keyData, map; - if (!decryptLocal(keyData, keyEncrypted, _passKey)) { + if (!decryptLocal(keyData, keyEncrypted, PassKey)) { LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password...")); return ReadMapPassNeeded; } @@ -1858,7 +1863,7 @@ ReadMapState _readMap(const QByteArray &pass) { LOG(("App Error: could not read pass-protected key from map file")); return ReadMapFailed; } - _localKey.setKey(key); + LocalKey = std::make_shared(key); _passKeyEncrypted = keyEncrypted; _passKeySalt = salt; @@ -2053,15 +2058,15 @@ void _writeMap(WriteMapWhen when) { QByteArray pass(kLocalKeySize, Qt::Uninitialized), salt(LocalEncryptSaltSize, Qt::Uninitialized); memset_rand(pass.data(), pass.size()); memset_rand(salt.data(), salt.size()); - createLocalKey(pass, &salt, &_localKey); + createLocalKey(pass, &salt, &LocalKey); _passKeySalt.resize(LocalEncryptSaltSize); memset_rand(_passKeySalt.data(), _passKeySalt.size()); - createLocalKey(QByteArray(), &_passKeySalt, &_passKey); + createLocalKey(QByteArray(), &_passKeySalt, &PassKey); EncryptedDescriptor passKeyData(kLocalKeySize); - _localKey.write(passKeyData.stream); - _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, _passKey); + LocalKey->write(passKeyData.stream); + _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, PassKey); } map.writeData(_passKeySalt); map.writeData(_passKeyEncrypted); @@ -2196,10 +2201,10 @@ void start() { LOG(("App Error: bad salt in settings file, size: %1").arg(salt.size())); return writeSettings(); } - createLocalKey(QByteArray(), &salt, &_settingsKey); + createLocalKey(QByteArray(), &salt, &SettingsKey); EncryptedDescriptor settings; - if (!decryptLocal(settings, settingsEncrypted, _settingsKey)) { + if (!decryptLocal(settings, settingsEncrypted, SettingsKey)) { LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode...")); return writeSettings(); } @@ -2234,10 +2239,10 @@ void writeSettings() { if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); FileWriteDescriptor settings(cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe); - if (_settingsSalt.isEmpty() || !_settingsKey.created()) { + if (_settingsSalt.isEmpty() || !SettingsKey) { _settingsSalt.resize(LocalEncryptSaltSize); memset_rand(_settingsSalt.data(), _settingsSalt.size()); - createLocalKey(QByteArray(), &_settingsSalt, &_settingsKey); + createLocalKey(QByteArray(), &_settingsSalt, &SettingsKey); } settings.writeData(_settingsSalt); @@ -2287,7 +2292,7 @@ void writeSettings() { TWindowPos pos(cWindowPos()); data.stream << quint32(dbiWindowPosition) << qint32(pos.x) << qint32(pos.y) << qint32(pos.w) << qint32(pos.h) << qint32(pos.moncrc) << qint32(pos.maximized); - settings.writeEncrypted(data, _settingsKey); + settings.writeEncrypted(data, SettingsKey); } void writeUserSettings() { @@ -2329,17 +2334,17 @@ void reset() { } bool checkPasscode(const QByteArray &passcode) { - auto checkKey = MTP::AuthKey(); + auto checkKey = MTP::AuthKeyPtr(); createLocalKey(passcode, &_passKeySalt, &checkKey); - return (checkKey == _passKey); + return checkKey->equals(PassKey); } void setPasscode(const QByteArray &passcode) { - createLocalKey(passcode, &_passKeySalt, &_passKey); + createLocalKey(passcode, &_passKeySalt, &PassKey); EncryptedDescriptor passKeyData(kLocalKeySize); - _localKey.write(passKeyData.stream); - _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, _passKey); + LocalKey->write(passKeyData.stream); + _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, PassKey); _mapChanged = true; _writeMap(WriteMapNow); @@ -3639,7 +3644,7 @@ void readSavedGifs() { void writeBackground(int32 id, const QImage &img) { if (!_working() || !_backgroundCanWrite) return; - if (!_localKey.created()) { + if (!LocalKey) { LOG(("App Error: localkey not created in writeBackground()")); return; } @@ -3716,7 +3721,7 @@ bool readBackground() { bool readThemeUsingKey(FileKey key) { FileReadDescriptor theme; - if (!readEncryptedFile(theme, key, FileOption::Safe, _settingsKey)) { + if (!readEncryptedFile(theme, key, FileOption::Safe, SettingsKey)) { return false; } @@ -3787,7 +3792,7 @@ void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const data.stream << cache.paletteChecksum << cache.contentChecksum << cache.colors << cache.background << backgroundTiled; FileWriteDescriptor file(_themeKey, FileOption::Safe); - file.writeEncrypted(data, _settingsKey); + file.writeEncrypted(data, SettingsKey); } void clearTheme() { @@ -3814,7 +3819,7 @@ bool copyThemeColorsToPalette(const QString &path) { } FileReadDescriptor theme; - if (!readEncryptedFile(theme, _themeKey, FileOption::Safe, _settingsKey)) { + if (!readEncryptedFile(theme, _themeKey, FileOption::Safe, SettingsKey)) { return false; } @@ -4269,18 +4274,18 @@ bool isBotTrusted(UserData *bot) { } bool encrypt(const void *src, void *dst, uint32 len, const void *key128) { - if (!_localKey.created()) { + if (!LocalKey) { return false; } - MTP::aesEncryptLocal(src, dst, len, &_localKey, key128); + MTP::aesEncryptLocal(src, dst, len, LocalKey, key128); return true; } bool decrypt(const void *src, void *dst, uint32 len, const void *key128) { - if (!_localKey.created()) { + if (!LocalKey) { return false; } - MTP::aesDecryptLocal(src, dst, len, &_localKey, key128); + MTP::aesDecryptLocal(src, dst, len, LocalKey, key128); return true; } diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index aefde0a405..1f5b3d5a54 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -39,6 +39,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "history/history_location_manager.h" #include "ui/widgets/tooltip.h" #include "ui/filedialog.h" +#include "serialize/serialize_common.h" namespace { @@ -52,10 +53,12 @@ Messenger *Messenger::InstancePointer() { struct Messenger::Private { MTP::Instance::Config mtpConfig; + MTP::AuthKeysList mtpKeysToDestroy; }; Messenger::Messenger() : QObject() -, _private(std::make_unique()) { +, _private(std::make_unique()) +, _delayedLoadersDestroyer(this, "onDelayedDestroyLoaders") { t_assert(SingleInstance == nullptr); SingleInstance = this; @@ -166,14 +169,25 @@ void Messenger::setMtpMainDcId(MTP::DcId mainDcId) { void Messenger::setMtpKey(MTP::DcId dcId, const MTP::AuthKey::Data &keyData) { t_assert(!_mtproto); - _private->mtpConfig.keys.insert(std::make_pair(dcId, keyData)); + _private->mtpConfig.keys.push_back(std::make_shared(MTP::AuthKey::Type::ReadFromFile, dcId, keyData)); } QByteArray Messenger::serializeMtpAuthorization() const { - auto serialize = [this](auto keysCount, auto mainDcId, auto writeKeys) { + auto serialize = [this](auto mainDcId, auto &keys, auto &keysToDestroy) { + auto keysSize = [](auto &list) { + return sizeof(qint32) + list.size() * (sizeof(qint32) + MTP::AuthKey::Data().size()); + }; + auto writeKeys = [](QDataStream &stream, auto &keys) { + stream << qint32(keys.size()); + for (auto &key : keys) { + stream << qint32(key->dcId()); + key->write(stream); + } + }; + auto result = QByteArray(); - auto size = sizeof(qint32) + sizeof(qint32) + sizeof(qint32); // userId + mainDcId + keys count - size += keysCount * (sizeof(qint32) + MTP::AuthKey::Data().size()); + auto size = sizeof(qint32) + sizeof(qint32); // userId + mainDcId + size += keysSize(keys) + keysSize(keysToDestroy); result.reserve(size); { QBuffer buffer(&result); @@ -184,27 +198,20 @@ QByteArray Messenger::serializeMtpAuthorization() const { QDataStream stream(&buffer); stream.setVersion(QDataStream::Qt_5_1); - stream << qint32(AuthSession::CurrentUserId()) << qint32(mainDcId) << qint32(keysCount); - writeKeys(stream); + stream << qint32(AuthSession::CurrentUserId()) << qint32(mainDcId); + writeKeys(stream, keys); + writeKeys(stream, keysToDestroy); } return result; }; if (_mtproto) { auto keys = _mtproto->getKeysForWrite(); - return serialize(keys.size(), _mtproto->mainDcId(), [&keys](QDataStream &stream) { - for (auto &key : keys) { - stream << qint32(key->getDC()); - key->write(stream); - } - }); + auto keysToDestroy = _mtprotoForKeysDestroy ? _mtprotoForKeysDestroy->getKeysForWrite() : MTP::AuthKeysList(); + return serialize(_mtproto->mainDcId(), keys, keysToDestroy); } auto &keys = _private->mtpConfig.keys; - return serialize(keys.size(), _private->mtpConfig.mainDcId, [&keys](QDataStream &stream) { - for (auto &key : keys) { - stream << qint32(key.first); - stream.writeRawData(key.second.data(), key.second.size()); - } - }); + auto &keysToDestroy = _private->mtpKeysToDestroy; + return serialize(_private->mtpConfig.mainDcId, keys, keysToDestroy); } void Messenger::setMtpAuthorization(const QByteArray &serialized) { @@ -220,8 +227,8 @@ void Messenger::setMtpAuthorization(const QByteArray &serialized) { QDataStream stream(&buffer); stream.setVersion(QDataStream::Qt_5_1); - qint32 userId = 0, mainDcId = 0, count = 0; - stream >> userId >> mainDcId >> count; + auto userId = Serialize::read(stream); + auto mainDcId = Serialize::read(stream); if (stream.status() != QDataStream::Ok) { LOG(("MTP Error: could not read main fields from serialized mtp authorization.")); return; @@ -231,22 +238,33 @@ void Messenger::setMtpAuthorization(const QByteArray &serialized) { authSessionCreate(userId); } _private->mtpConfig.mainDcId = mainDcId; - for (auto i = 0; i != count; ++i) { - qint32 dcId = 0; - MTP::AuthKey::Data keyData; - stream >> dcId; - stream.readRawData(keyData.data(), keyData.size()); + + auto readKeys = [&stream](auto &keys) { + auto count = Serialize::read(stream); if (stream.status() != QDataStream::Ok) { - LOG(("MTP Error: could not read key from serialized mtp authorization.")); + LOG(("MTP Error: could not read keys count from serialized mtp authorization.")); return; } - _private->mtpConfig.keys.insert(std::make_pair(dcId, keyData)); - } + keys.reserve(count); + for (auto i = 0; i != count; ++i) { + auto dcId = Serialize::read(stream); + auto keyData = Serialize::read(stream); + if (stream.status() != QDataStream::Ok) { + LOG(("MTP Error: could not read key from serialized mtp authorization.")); + return; + } + keys.push_back(std::make_shared(MTP::AuthKey::Type::ReadFromFile, dcId, keyData)); + } + }; + readKeys(_private->mtpConfig.keys); + readKeys(_private->mtpKeysToDestroy); + LOG(("MTP Info: read keys, current: %1, to destroy: %2").arg(_private->mtpConfig.keys.size()).arg(_private->mtpKeysToDestroy.size())); } void Messenger::startMtp() { t_assert(!_mtproto); - _mtproto = std::make_unique(_dcOptions.get(), std::move(_private->mtpConfig)); + _mtproto = std::make_unique(_dcOptions.get(), MTP::Instance::Mode::Normal, base::take(_private->mtpConfig)); + _private->mtpConfig.mainDcId = _mtproto->mainDcId(); _mtproto->setStateChangedHandler([](MTP::ShiftedDcId shiftedDcId, int32 state) { if (App::wnd()) { @@ -258,6 +276,57 @@ void Messenger::startMtp() { App::main()->getDifference(); } }); + + if (!_private->mtpKeysToDestroy.empty()) { + destroyMtpKeys(base::take(_private->mtpKeysToDestroy)); + } +} + +void Messenger::destroyMtpKeys(MTP::AuthKeysList &&keys) { + if (keys.empty()) { + return; + } + if (_mtprotoForKeysDestroy) { + _mtprotoForKeysDestroy->addKeysForDestroy(std::move(keys)); + Local::writeMtpData(); + return; + } + auto destroyConfig = MTP::Instance::Config(); + destroyConfig.mainDcId = MTP::Instance::Config::kNoneMainDc; + destroyConfig.keys = std::move(keys); + _mtprotoForKeysDestroy = std::make_unique(_dcOptions.get(), MTP::Instance::Mode::KeysDestroyer, std::move(destroyConfig)); + connect(_mtprotoForKeysDestroy.get(), SIGNAL(allKeysDestroyed()), this, SLOT(onAllKeysDestroyed())); +} + +void Messenger::onAllKeysDestroyed() { + LOG(("MTP Info: all keys scheduled for destroy are destroyed.")); + _mtprotoForKeysDestroy.reset(); + Local::writeMtpData(); +} + +void Messenger::suggestMainDcId(MTP::DcId mainDcId) { + t_assert(_mtproto != nullptr); + + _mtproto->suggestMainDcId(mainDcId); + if (_private->mtpConfig.mainDcId != MTP::Instance::Config::kNotSetMainDc) { + _private->mtpConfig.mainDcId = mainDcId; + } +} + +void Messenger::destroyStaleAuthorizationKeys() { + t_assert(_mtproto != nullptr); + + auto keys = _mtproto->getKeysForWrite(); + for (auto &key : keys) { + if (key->type() == MTP::AuthKey::Type::ReadFromFile) { + _private->mtpKeysToDestroy = _mtproto->getKeysForWrite(); + _mtproto.reset(); + LOG(("MTP Info: destroying stale keys, count: %1").arg(_private->mtpKeysToDestroy.size())); + startMtp(); + Local::writeMtpData(); + return; + } + } } void Messenger::loadLanguage() { @@ -449,7 +518,7 @@ void Messenger::killDownloadSessions() { for (auto i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) { if (i.value() <= ms) { for (int j = 0; j < MTPDownloadSessionsCount; ++j) { - MTP::stopSession(MTP::dldDcId(i.key(), j)); + MTP::stopSession(MTP::downloadDcId(i.key(), j)); } i = killDownloadSessionTimes.erase(i); } else { @@ -593,7 +662,13 @@ void Messenger::checkMapVersion() { void Messenger::prepareToDestroy() { _window.reset(); + + // Some MTP requests can be cancelled from data clearing. + App::clearHistories(); + _delayedDestroyedLoaders.clear(); + _mtproto.reset(); + _mtprotoForKeysDestroy.reset(); } Messenger::~Messenger() { @@ -602,8 +677,6 @@ Messenger::~Messenger() { Shortcuts::finish(); - App::clearHistories(); - Window::Notifications::finish(); anim::stopManager(); @@ -628,3 +701,12 @@ Messenger::~Messenger() { MainWindow *Messenger::mainWindow() { return _window.get(); } + +void Messenger::delayedDestroyLoader(std::unique_ptr loader) { + _delayedDestroyedLoaders.push_back(std::move(loader)); + _delayedLoadersDestroyer.call(); +} + +void Messenger::onDelayedDestroyLoaders() { + _delayedDestroyedLoaders.clear(); +} diff --git a/Telegram/SourceFiles/messenger.h b/Telegram/SourceFiles/messenger.h index 3b9c2be7e3..c51f80a6b8 100644 --- a/Telegram/SourceFiles/messenger.h +++ b/Telegram/SourceFiles/messenger.h @@ -65,6 +65,8 @@ public: MTP::Instance *mtp() { return _mtproto.get(); } + void suggestMainDcId(MTP::DcId mainDcId); + void destroyStaleAuthorizationKeys(); AuthSession *authSession() { return _authSession.get(); @@ -96,11 +98,17 @@ public: void handleAppActivated(); void handleAppDeactivated(); + // Temporary here, when all Images and Documents are owned by AuthSession it'll have this. + void delayedDestroyLoader(std::unique_ptr loader); + signals: void peerPhotoDone(PeerId peer); void peerPhotoFail(PeerId peer); public slots: + void onAllKeysDestroyed(); + void onDelayedDestroyLoaders(); + void photoUpdated(const FullMsgId &msgId, bool silent, const MTPInputFile &file); void onSwitchDebugMode(); @@ -117,6 +125,7 @@ public slots: void call_handleObservables(); private: + void destroyMtpKeys(MTP::AuthKeysList &&keys); void startLocalStorage(); void loadLanguage(); @@ -135,6 +144,10 @@ private: std::unique_ptr _dcOptions; std::unique_ptr _mtproto; + std::unique_ptr _mtprotoForKeysDestroy; std::unique_ptr _authSession; + SingleDelayedCall _delayedLoadersDestroyer; + std::vector> _delayedDestroyedLoaders; + }; diff --git a/Telegram/SourceFiles/mtproto/auth_key.h b/Telegram/SourceFiles/mtproto/auth_key.h index 22860c14d3..cfd8584abc 100644 --- a/Telegram/SourceFiles/mtproto/auth_key.h +++ b/Telegram/SourceFiles/mtproto/auth_key.h @@ -29,37 +29,36 @@ class AuthKey { public: static constexpr auto kSize = 256; // 2048 bits. using Data = std::array; + using KeyId = uint64; - bool created() const { - return _isset; + enum class Type { + Generated, + ReadFromFile, + Local, + }; + AuthKey(Type type, DcId dcId, const Data &data) : _type(type), _dcId(dcId), _key(data) { + countKeyId(); + } + AuthKey(const Data &data) : _type(Type::Local), _key(data) { + countKeyId(); } - void setKey(const Data &from) { - _key = from; - auto sha1 = hashSha1(_key.data(), _key.size()); + AuthKey(const AuthKey &other) = delete; + AuthKey &operator=(const AuthKey &other) = delete; - // Lower 64 bits = 8 bytes of 20 byte SHA1 hash. - _keyId = *reinterpret_cast(sha1.data() + 12); - _isset = true; + Type type() const { + return _type; } - void setDC(int dc) { - _dc = dc; + int dcId() const { + return _dcId; } - int getDC() const { - t_assert(_isset); - return _dc; - } - - uint64 keyId() const { - t_assert(_isset); + KeyId keyId() const { return _keyId; } void prepareAES(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send = true) const { - t_assert(_isset); - uint32 x = send ? 0 : 8; uchar data_a[16 + 32], sha1_a[20]; @@ -95,28 +94,30 @@ public: } void write(QDataStream &to) const { - t_assert(_isset); to.writeRawData(_key.data(), _key.size()); } - static const uint64 RecreateKeyId = 0xFFFFFFFFFFFFFFFFL; - - friend bool operator==(const AuthKey &a, const AuthKey &b); + bool equals(const std::shared_ptr &other) const { + return other ? (_key == other->_key) : false; + } private: + void countKeyId() { + auto sha1 = hashSha1(_key.data(), _key.size()); + + // Lower 64 bits = 8 bytes of 20 byte SHA1 hash. + _keyId = *reinterpret_cast(sha1.data() + 12); + } + + Type _type = Type::Generated; + DcId _dcId = 0; Data _key = { 0 }; - uint64 _keyId = 0; - bool _isset = false; - int _dc = 0; + KeyId _keyId = 0; }; -inline bool operator==(const AuthKey &a, const AuthKey &b) { - return (a._key == b._key); -} - using AuthKeyPtr = std::shared_ptr; -using AuthKeysMap = QVector; +using AuthKeysList = std::vector; void aesIgeEncrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv); void aesIgeDecrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv); @@ -128,7 +129,7 @@ inline void aesIgeEncrypt(const void *src, void *dst, uint32 len, const AuthKeyP return aesIgeEncrypt(src, dst, len, static_cast(&aesKey), static_cast(&aesIV)); } -inline void aesEncryptLocal(const void *src, void *dst, uint32 len, const AuthKey *authKey, const void *key128) { +inline void aesEncryptLocal(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const void *key128) { MTPint256 aesKey, aesIV; authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false); @@ -142,7 +143,7 @@ inline void aesIgeDecrypt(const void *src, void *dst, uint32 len, const AuthKeyP return aesIgeDecrypt(src, dst, len, static_cast(&aesKey), static_cast(&aesIV)); } -inline void aesDecryptLocal(const void *src, void *dst, uint32 len, const AuthKey *authKey, const void *key128) { +inline void aesDecryptLocal(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const void *key128) { MTPint256 aesKey, aesIV; authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false); diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index bfddf2e1ce..8c29f03f3f 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -42,6 +42,8 @@ namespace MTP { namespace internal { namespace { +constexpr auto kRecreateKeyId = AuthKey::KeyId(0xFFFFFFFFFFFFFFFFULL); + void wrapInvokeAfter(mtpRequest &to, const mtpRequest &from, const mtpRequestMap &haveSent, int32 skipBeforeRequest = 0) { mtpMsgId afterId(*(mtpMsgId*)(from->after->data() + 4)); mtpRequestMap::const_iterator i = afterId ? haveSent.constFind(afterId) : haveSent.cend(); @@ -346,22 +348,14 @@ RSAPublicKeys InitRSAPublicKeys() { Connection::Connection(Instance *instance) : _instance(instance) { } -int32 Connection::prepare(SessionData *sessionData, int32 dc) { +void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) { t_assert(thread == nullptr && data == nullptr); thread = std::make_unique(); - auto newData = std::make_unique(_instance, thread.get(), this, sessionData, dc); - dc = newData->getDC(); - if (dc) { - // will be deleted in the thread::finished signal - data = newData.release(); - } else { - thread.reset(); - } - return dc; -} + auto newData = std::make_unique(_instance, thread.get(), this, sessionData, shiftedDcId); -void Connection::start() { + // will be deleted in the thread::finished signal + data = newData.release(); thread->start(); } @@ -404,13 +398,13 @@ void ConnectionPrivate::createConn(bool createIPv4, bool createIPv6) { if (createIPv4) { QWriteLocker lock(&stateConnMutex); _conn4 = AbstractConnection::create(thread()); - connect(_conn4, SIGNAL(error(bool)), this, SLOT(onError4(bool))); + connect(_conn4, SIGNAL(error(qint32)), this, SLOT(onError4(qint32))); connect(_conn4, SIGNAL(receivedSome()), this, SLOT(onReceivedSome())); } if (createIPv6) { QWriteLocker lock(&stateConnMutex); _conn6 = AbstractConnection::create(thread()); - connect(_conn6, SIGNAL(error(bool)), this, SLOT(onError6(bool))); + connect(_conn6, SIGNAL(error(qint32)), this, SLOT(onError6(qint32))); connect(_conn6, SIGNAL(receivedSome()), this, SLOT(onReceivedSome())); } firstSentAt = 0; @@ -431,7 +425,7 @@ void ConnectionPrivate::destroyConn(AbstractConnection **conn) { toDisconnect = *conn; disconnect(*conn, SIGNAL(connected()), nullptr, nullptr); disconnect(*conn, SIGNAL(disconnected()), nullptr, nullptr); - disconnect(*conn, SIGNAL(error(bool)), nullptr, nullptr); + disconnect(*conn, SIGNAL(error(qint32)), nullptr, nullptr); disconnect(*conn, SIGNAL(receivedData()), nullptr, nullptr); disconnect(*conn, SIGNAL(receivedSome()), nullptr, nullptr); *conn = nullptr; @@ -448,10 +442,10 @@ void ConnectionPrivate::destroyConn(AbstractConnection **conn) { } } -ConnectionPrivate::ConnectionPrivate(Instance *instance, QThread *thread, Connection *owner, SessionData *data, uint32 _dc) : QObject() +ConnectionPrivate::ConnectionPrivate(Instance *instance, QThread *thread, Connection *owner, SessionData *data, ShiftedDcId shiftedDcId) : QObject() , _instance(instance) , _state(DisconnectedState) -, dc(_dc) +, _shiftedDcId(shiftedDcId) , _owner(owner) , _waitForReceived(MTPMinReceiveDelay) , _waitForConnected(MTPMinConnectDelay) @@ -465,10 +459,7 @@ ConnectionPrivate::ConnectionPrivate(Instance *instance, QThread *thread, Connec retryTimer.moveToThread(thread); moveToThread(thread); - if (!dc) { - dc = Messenger::Instance().dcOptions()->getDefaultDcId(); - DEBUG_LOG(("MTP Info: searching for any DC, %1 selected...").arg(dc)); - } + t_assert(_shiftedDcId != 0); connect(thread, SIGNAL(started()), this, SLOT(socketStart())); connect(thread, SIGNAL(finished()), this, SLOT(doFinish())); @@ -509,8 +500,8 @@ void ConnectionPrivate::onConfigLoaded() { socketStart(true); } -int32 ConnectionPrivate::getDC() const { - return dc; +int32 ConnectionPrivate::getShiftedDcId() const { + return _shiftedDcId; } int32 ConnectionPrivate::getState() const { @@ -586,7 +577,7 @@ void ConnectionPrivate::resetSession() { // recreate all msg_id and msg_seqno newId = m; } - MTP_LOG(dc, ("Replacing msgId %1 to %2!").arg(id).arg(newId)); + MTP_LOG(_shiftedDcId, ("Replacing msgId %1 to %2!").arg(id).arg(newId)); replaces.insert(id, newId); id = newId; *(mtpMsgId*)(i.value()->data() + 4) = id; @@ -613,7 +604,7 @@ void ConnectionPrivate::resetSession() { // recreate all msg_id and msg_seqno newId = m; } - MTP_LOG(dc, ("Replacing msgId %1 to %2!").arg(id).arg(newId)); + MTP_LOG(_shiftedDcId, ("Replacing msgId %1 to %2!").arg(id).arg(newId)); replaces.insert(id, newId); id = newId; *(mtpMsgId*)(j.value()->data() + 4) = id; @@ -777,13 +768,13 @@ void ConnectionPrivate::tryToSend() { int32 state = getState(); bool prependOnly = (state != ConnectedState); mtpRequest pingRequest; - if (dc == bareDcId(dc)) { // main session + if (_shiftedDcId == bareDcId(_shiftedDcId)) { // main session if (!prependOnly && !_pingIdToSend && !_pingId && _pingSendAt <= getms(true)) { _pingIdToSend = rand_value(); } } if (_pingIdToSend) { - if (prependOnly || dc != bareDcId(dc)) { + if (prependOnly || _shiftedDcId != bareDcId(_shiftedDcId)) { MTPPing ping(MTPping(MTP_long(_pingIdToSend))); uint32 pingSize = ping.innerLength() >> 2; // copy from Session::send pingRequest = mtpRequestData::prepare(pingSize); @@ -801,7 +792,7 @@ void ConnectionPrivate::tryToSend() { _pingSendAt = pingRequest->msDate + (MTPPingSendAfterAuto * 1000LL); pingRequest->requestId = 0; // dont add to haveSent / wereAcked maps - if (dc == bareDcId(dc) && !prependOnly) { // main session + if (_shiftedDcId == bareDcId(_shiftedDcId) && !prependOnly) { // main session _pingSender.start(MTPPingSendAfter * 1000); } @@ -809,10 +800,10 @@ void ConnectionPrivate::tryToSend() { _pingIdToSend = 0; } else { if (prependOnly) { - DEBUG_LOG(("MTP Info: dc %1 not sending, waiting for Connected state, state: %2").arg(dc).arg(state)); + DEBUG_LOG(("MTP Info: dc %1 not sending, waiting for Connected state, state: %2").arg(_shiftedDcId).arg(state)); return; // just do nothing, if is not connected yet } else { - DEBUG_LOG(("MTP Info: dc %1 trying to send after ping, state: %2").arg(dc).arg(state)); + DEBUG_LOG(("MTP Info: dc %1 trying to send after ping, state: %2").arg(_shiftedDcId).arg(state)); } } @@ -1064,7 +1055,7 @@ void ConnectionPrivate::retryByTimer() { } else if (retryTimeout < 64000) { retryTimeout *= 2; } - if (keyId == AuthKey::RecreateKeyId) { + if (keyId == kRecreateKeyId) { if (sessionData->getKey()) { unlockKey(); @@ -1088,31 +1079,36 @@ void ConnectionPrivate::socketStart(bool afterConfig) { return; } auto dcType = DcOptions::DcType::Regular; - bool isDldDc = isDldDcId(dc); - if (isDldDcId(dc)) { // using media_only addresses only if key for this dc is already created + auto isDownloadDc = isDownloadDcId(_shiftedDcId); + if (isDownloadDc) { // using media_only addresses only if key for this dc is already created QReadLocker lockFinished(&sessionDataMutex); if (!sessionData || sessionData->getKey()) { dcType = DcOptions::DcType::MediaDownload; } } - auto bareDc = bareDcId(dc); + 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(bareDcId(dc), 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) { - if (afterConfig) { - if (noIPv4) LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found right after config load!").arg(dc)); - if (Global::TryIPv6() && noIPv6) LOG(("MTP Error: DC %1 options for IPv6 over HTTP not found right after config load!").arg(dc)); + if (_instance->isKeysDestroyer()) { + LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found for auth key destruction!").arg(_shiftedDcId)); + if (Global::TryIPv6() && noIPv6) LOG(("MTP Error: DC %1 options for IPv6 over HTTP not found for auth key destruction!").arg(_shiftedDcId)); + emit _instance->keyDestroyed(_shiftedDcId); + return; + } else if (afterConfig) { + LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found right after config load!").arg(_shiftedDcId)); + if (Global::TryIPv6() && noIPv6) LOG(("MTP Error: DC %1 options for IPv6 over HTTP not found right after config load!").arg(_shiftedDcId)); return restart(); } - if (noIPv4) DEBUG_LOG(("MTP Info: DC %1 options for IPv4 over HTTP not found, waiting for config").arg(dc)); - if (Global::TryIPv6() && noIPv6) DEBUG_LOG(("MTP Info: DC %1 options for IPv6 over HTTP not found, waiting for config").arg(dc)); + 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); return; @@ -1146,11 +1142,11 @@ void ConnectionPrivate::socketStart(bool afterConfig) { } } -void ConnectionPrivate::restart(bool mayBeBadKey) { +void ConnectionPrivate::restart() { QReadLocker lockFinished(&sessionDataMutex); if (!sessionData) return; - DEBUG_LOG(("MTP Info: restarting Connection, maybe bad key = %1").arg(Logs::b(mayBeBadKey))); + DEBUG_LOG(("MTP Info: restarting Connection")); _waitForReceivedTimer.stop(); _waitForConnectedTimer.stop(); @@ -1158,12 +1154,14 @@ void ConnectionPrivate::restart(bool mayBeBadKey) { auto key = sessionData->getKey(); if (key) { if (!sessionData->isCheckedKey()) { - if (mayBeBadKey) { - clearMessages(); - keyId = AuthKey::RecreateKeyId; + // No destroying in case of an error. + // + //if (mayBeBadKey) { + // clearMessages(); + // keyId = kRecreateKeyId; // retryTimeout = 1; // no ddos please - LOG(("MTP Info: key may be bad and was not checked - but won't be destroyed, no log outs because of bad server right now...")); - } + // LOG(("MTP Info: key may be bad and was not checked - but won't be destroyed, no log outs because of bad server right now...")); + //} } else { sessionData->setCheckedKey(false); } @@ -1193,9 +1191,9 @@ void ConnectionPrivate::onSentSome(uint64 size) { DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain)); } } - if (isUplDcId(dc)) { + if (isUploadDcId(_shiftedDcId)) { remain *= MTPUploadSessionsCount; - } else if (isDldDcId(dc)) { + } else if (isDownloadDcId(_shiftedDcId)) { remain *= MTPDownloadSessionsCount; } _waitForReceivedTimer.start(remain); @@ -1318,7 +1316,7 @@ void ConnectionPrivate::handleReceived() { ReadLockerAttempt lock(sessionData->keyMutex()); if (!lock) { - DEBUG_LOG(("MTP Error: auth_key for dc %1 busy, cant lock").arg(dc)); + DEBUG_LOG(("MTP Error: auth_key for dc %1 busy, cant lock").arg(_shiftedDcId)); clearMessages(); keyId = 0; @@ -1328,7 +1326,7 @@ void ConnectionPrivate::handleReceived() { auto key = sessionData->getKey(); if (!key || key->keyId() != keyId) { - DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(dc)); + DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(_shiftedDcId)); lockFinished.unlock(); return restart(); @@ -1434,7 +1432,7 @@ void ConnectionPrivate::handleReceived() { auto res = HandleResult::Success; // if no need to handle, then succeed end = data + 8 + (msgLen >> 2); const mtpPrime *sfrom(data + 4); - MTP_LOG(dc, ("Recv: ") + mtpTextSerialize(sfrom, end)); + MTP_LOG(_shiftedDcId, ("Recv: ") + mtpTextSerialize(sfrom, end)); bool needToHandle = false; { @@ -2308,7 +2306,7 @@ void ConnectionPrivate::updateAuthKey() { QReadLocker lockFinished(&sessionDataMutex); if (!sessionData || !_conn) return; - DEBUG_LOG(("AuthKey Info: Connection updating key from Session, dc %1").arg(dc)); + DEBUG_LOG(("AuthKey Info: Connection updating key from Session, dc %1").arg(_shiftedDcId)); uint64 newKeyId = 0; { ReadLockerAttempt lock(sessionData->keyMutex()); @@ -2325,7 +2323,7 @@ void ConnectionPrivate::updateAuthKey() { clearMessages(); keyId = newKeyId; } - DEBUG_LOG(("AuthKey Info: Connection update key from Session, dc %1 result: %2").arg(dc).arg(Logs::mb(&keyId, sizeof(keyId)).str())); + DEBUG_LOG(("AuthKey Info: Connection update key from Session, dc %1 result: %2").arg(_shiftedDcId).arg(Logs::mb(&keyId, sizeof(keyId)).str())); if (keyId) { return authKeyCreated(); } @@ -2339,6 +2337,11 @@ void ConnectionPrivate::updateAuthKey() { keyId = key->keyId(); unlockKey(); return authKeyCreated(); + } else if (_instance->isKeysDestroyer()) { + // We are here to destroy an old key, so we're done. + LOG(("MTP Error: No key %1 in updateAuthKey() for destroying.").arg(_shiftedDcId)); + emit _instance->keyDestroyed(_shiftedDcId); + return; } _authKeyData = std::make_unique(); @@ -2357,7 +2360,7 @@ void ConnectionPrivate::updateAuthKey() { } void ConnectionPrivate::clearMessages() { - if (keyId && keyId != AuthKey::RecreateKeyId && _conn) { + if (keyId && keyId != kRecreateKeyId && _conn) { _conn->received().clear(); } } @@ -2684,9 +2687,7 @@ void ConnectionPrivate::dhClientParamsAnswered() { uint64 salt1 = _authKeyData->new_nonce.l.l, salt2 = _authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2; sessionData->setSalt(serverSalt); - auto authKey = std::make_shared(); - authKey->setKey(_authKeyStrings->auth_key); - authKey->setDC(bareDcId(dc)); + auto authKey = std::make_shared(AuthKey::Type::Generated, bareDcId(_shiftedDcId), _authKeyStrings->auth_key); DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(serverSalt)); @@ -2806,29 +2807,47 @@ void ConnectionPrivate::clearAuthKeyData() { } } -void ConnectionPrivate::onError4(bool mayBeBadKey) { +void ConnectionPrivate::onError4(qint32 errorCode) { if (_conn && _conn == _conn6) return; // error in the unused + if (errorCode == -429) { + LOG(("Protocol Error: -429 flood code returned!")); + } if (_conn || !_conn6) { destroyConn(); _waitForConnectedTimer.stop(); - MTP_LOG(dc, ("Restarting after error in IPv4 connection, maybe bad key: %1...").arg(Logs::b(mayBeBadKey))); - return restart(mayBeBadKey); + if (errorCode == -404 && _instance->isKeysDestroyer()) { + LOG(("MTP Info: -404 error received on destroying key %1, assuming it is destroyed.").arg(_shiftedDcId)); + emit _instance->keyDestroyed(_shiftedDcId); + return; + } else { + MTP_LOG(_shiftedDcId, ("Restarting after error in IPv4 connection, error code: %1...").arg(errorCode)); + return restart(); + } } else { destroyConn(&_conn4); } } -void ConnectionPrivate::onError6(bool mayBeBadKey) { +void ConnectionPrivate::onError6(qint32 errorCode) { if (_conn && _conn == _conn4) return; // error in the unused + if (errorCode == -429) { + LOG(("Protocol Error: -429 flood code returned!")); + } if (_conn || !_conn4) { destroyConn(); _waitForConnectedTimer.stop(); - MTP_LOG(dc, ("Restarting after error in IPv6 connection, maybe bad key: %1...").arg(Logs::b(mayBeBadKey))); - return restart(mayBeBadKey); + if (errorCode == -404 && _instance->isKeysDestroyer()) { + LOG(("MTP Info: -404 error received on destroying key %1, assuming it is destroyed.").arg(_shiftedDcId)); + emit _instance->keyDestroyed(_shiftedDcId); + return; + } else { + MTP_LOG(_shiftedDcId, ("Restarting after error in IPv6 connection, error code: %1...").arg(errorCode)); + return restart(); + } } else { destroyConn(&_conn6); } @@ -2914,7 +2933,7 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q ReadLockerAttempt lock(sessionData->keyMutex()); if (!lock) { - DEBUG_LOG(("MTP Info: could not lock key for read in sendBuffer(), dc %1, restarting...").arg(dc)); + DEBUG_LOG(("MTP Info: could not lock key for read in sendBuffer(), dc %1, restarting...").arg(_shiftedDcId)); lockFinished.unlock(); restart(); @@ -2923,7 +2942,7 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q AuthKeyPtr key(sessionData->getKey()); if (!key || key->keyId() != keyId) { - DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(dc)); + DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(_shiftedDcId)); lockFinished.unlock(); restart(); @@ -2937,7 +2956,7 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q memcpy(request->data() + 2, &session, 2 * sizeof(mtpPrime)); const mtpPrime *from = request->constData() + 4; - MTP_LOG(dc, ("Send: ") + mtpTextSerialize(from, from + messageSize)); + MTP_LOG(_shiftedDcId, ("Send: ") + mtpTextSerialize(from, from + messageSize)); uchar encryptedSHA[20]; MTPint128 &msgKey(*(MTPint128*)(encryptedSHA + 4)); diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index 57cbe4a9d6..0148a3fb48 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -60,8 +60,7 @@ public: Connection(Instance *instance); - int32 prepare(SessionData *data, int32 dc = 0); // return dc - void start(); + void start(SessionData *data, ShiftedDcId shiftedDcId); void kill(); void waitTillFinish(); @@ -83,12 +82,12 @@ class ConnectionPrivate : public QObject { Q_OBJECT public: - ConnectionPrivate(Instance *instance, QThread *thread, Connection *owner, SessionData *data, uint32 dc); + ConnectionPrivate(Instance *instance, QThread *thread, Connection *owner, SessionData *data, ShiftedDcId shiftedDcId); ~ConnectionPrivate(); void stop(); - int32 getDC() const; + int32 getShiftedDcId() const; int32 getState() const; QString transport() const; @@ -113,7 +112,6 @@ signals: public slots: void retryByTimer(); void restartNow(); - void restart(bool mayBeBadKey = false); void onPingSender(); void onPingSendForce(); @@ -133,8 +131,8 @@ public slots: void onConnected6(); void onDisconnected4(); void onDisconnected6(); - void onError4(bool mayBeBadKey = false); - void onError6(bool mayBeBadKey = false); + void onError4(qint32 errorCode); + void onError6(qint32 errorCode); void doFinish(); @@ -155,6 +153,7 @@ public slots: private: void doDisconnect(); + void restart(); void createConn(bool createIPv4, bool createIPv6); void destroyConn(AbstractConnection **conn = 0); // 0 - destory all @@ -188,7 +187,7 @@ private: bool _needSessionReset = false; void resetSession(); - ShiftedDcId dc = 0; + ShiftedDcId _shiftedDcId = 0; Connection *_owner = nullptr; AbstractConnection *_conn = nullptr; AbstractConnection *_conn4 = nullptr; diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h index b9d784a2d4..d662cdb79f 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.h +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -65,18 +65,19 @@ public: return receivedQueue; } -signals: + // Used to emit error(...) with no real code from the server. + static constexpr auto kErrorCodeOther = -499; +signals: void receivedData(); void receivedSome(); // to stop restart timer - void error(bool mayBeBadKey = false); + void error(qint32 errorCodebool); void connected(); void disconnected(); protected: - BuffersQueue receivedQueue; // list of received packets, not processed yet bool _sentEncrypted; diff --git a/Telegram/SourceFiles/mtproto/connection_auto.cpp b/Telegram/SourceFiles/mtproto/connection_auto.cpp index 6b4535c4d6..6f6c32b604 100644 --- a/Telegram/SourceFiles/mtproto/connection_auto.cpp +++ b/Telegram/SourceFiles/mtproto/connection_auto.cpp @@ -119,7 +119,7 @@ void AutoConnection::sendData(mtpBuffer &buffer) { if (buffer.size() < 3) { LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime))); TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str())); - emit error(); + emit error(kErrorCodeOther); return; } @@ -204,7 +204,7 @@ void AutoConnection::requestFinished(QNetworkReply *reply) { if (status == WaitingBoth) { status = WaitingTcp; } else { - emit error(); + emit error(data[0]); } } else if (!data.isEmpty()) { if (status == UsingHttp) { @@ -230,7 +230,7 @@ void AutoConnection::requestFinished(QNetworkReply *reply) { if (status == WaitingBoth) { status = WaitingTcp; } else { - emit error(); + emit error(kErrorCodeOther); } } } else if (status == UsingTcp) { @@ -242,11 +242,10 @@ void AutoConnection::requestFinished(QNetworkReply *reply) { return; } - bool mayBeBadKey = HTTPConnection::handleError(reply) && _sentEncrypted; if (status == WaitingBoth) { status = WaitingTcp; } else if (status == WaitingHttp || status == UsingHttp) { - emit error(mayBeBadKey); + emit error(HTTPConnection::handleError(reply)); } else { LOG(("Strange Http Error: status %1").arg(status)); } @@ -267,8 +266,7 @@ void AutoConnection::socketPacket(const char *packet, uint32 length) { sock.disconnectFromHost(); emit connected(); } else if (status == WaitingTcp || status == UsingTcp) { - bool mayBeBadKey = (data[0] == -410) && _sentEncrypted; - emit error(mayBeBadKey); + emit error(data[0]); } else { LOG(("Strange Tcp Error; status %1").arg(status)); } @@ -296,7 +294,7 @@ void AutoConnection::socketPacket(const char *packet, uint32 length) { sock.disconnectFromHost(); emit connected(); } else { - emit error(); + emit error(kErrorCodeOther); } } } @@ -335,7 +333,7 @@ void AutoConnection::socketError(QAbstractSocket::SocketError e) { status = UsingHttp; emit connected(); } else if (status == WaitingTcp || status == UsingTcp) { - emit error(); + emit error(kErrorCodeOther); } else { LOG(("Strange Tcp Error: status %1").arg(status)); } diff --git a/Telegram/SourceFiles/mtproto/connection_http.cpp b/Telegram/SourceFiles/mtproto/connection_http.cpp index 5fa8afe878..c73d23a5dd 100644 --- a/Telegram/SourceFiles/mtproto/connection_http.cpp +++ b/Telegram/SourceFiles/mtproto/connection_http.cpp @@ -42,16 +42,13 @@ mtpBuffer HTTPConnection::handleResponse(QNetworkReply *reply) { return data; } -bool HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key" - bool mayBeBadKey = false; +qint32 HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key" + auto result = qint32(kErrorCodeOther); QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (statusCode.isValid()) { int status = statusCode.toInt(); - mayBeBadKey = (status == 410); - if (status == 429) { - LOG(("Protocol Error: 429 flood code returned!")); - } + result = -status; } switch (reply->error()) { @@ -66,7 +63,7 @@ bool HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad case QNetworkReply::BackgroundRequestNotAllowedError: case QNetworkReply::UnknownNetworkError: LOG(("HTTP Error: network error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; - // proxy errors (101-199): + // proxy errors (101-199): case QNetworkReply::ProxyConnectionRefusedError: case QNetworkReply::ProxyConnectionClosedError: case QNetworkReply::ProxyNotFoundError: @@ -74,7 +71,7 @@ bool HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad case QNetworkReply::ProxyAuthenticationRequiredError: case QNetworkReply::UnknownProxyError:LOG(("HTTP Error: proxy error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; - // content errors (201-299): + // content errors (201-299): case QNetworkReply::ContentAccessDenied: case QNetworkReply::ContentOperationNotPermittedError: case QNetworkReply::ContentNotFoundError: @@ -82,14 +79,14 @@ bool HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad case QNetworkReply::ContentReSendError: case QNetworkReply::UnknownContentError: LOG(("HTTP Error: content error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; - // protocol errors + // protocol errors case QNetworkReply::ProtocolUnknownError: case QNetworkReply::ProtocolInvalidOperationError: case QNetworkReply::ProtocolFailure: LOG(("HTTP Error: protocol error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; }; TCP_LOG(("HTTP Error %1, restarting! - %2").arg(reply->error()).arg(reply->errorString())); - return mayBeBadKey; + return result; } HTTPConnection::HTTPConnection(QThread *thread) : AbstractConnection(thread) @@ -106,7 +103,7 @@ void HTTPConnection::sendData(mtpBuffer &buffer) { if (buffer.size() < 3) { LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime))); TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str())); - emit error(); + emit error(kErrorCodeOther); return; } @@ -165,7 +162,7 @@ void HTTPConnection::requestFinished(QNetworkReply *reply) { mtpBuffer data = handleResponse(reply); if (data.size() == 1) { - emit error(); + emit error(data[0]); } else if (!data.isEmpty()) { if (status == UsingHttp) { receivedQueue.push_back(data); @@ -181,7 +178,7 @@ void HTTPConnection::requestFinished(QNetworkReply *reply) { } } catch (Exception &e) { DEBUG_LOG(("Connection Error: exception in parsing HTTP fake pq-responce, %1").arg(e.what())); - emit error(); + emit error(kErrorCodeOther); } } } @@ -190,9 +187,7 @@ void HTTPConnection::requestFinished(QNetworkReply *reply) { return; } - bool mayBeBadKey = handleError(reply) && _sentEncrypted; - - emit error(mayBeBadKey); + emit error(handleError(reply)); } } diff --git a/Telegram/SourceFiles/mtproto/connection_http.h b/Telegram/SourceFiles/mtproto/connection_http.h index 3395c31617..6c06da25cf 100644 --- a/Telegram/SourceFiles/mtproto/connection_http.h +++ b/Telegram/SourceFiles/mtproto/connection_http.h @@ -45,12 +45,12 @@ public: QString transport() const override; + static mtpBuffer handleResponse(QNetworkReply *reply); + static qint32 handleError(QNetworkReply *reply); // returnes error code + public slots: void requestFinished(QNetworkReply *reply); - static mtpBuffer handleResponse(QNetworkReply *reply); - static bool handleError(QNetworkReply *reply); // returnes "maybe bad key" - private: enum Status { WaitingHttp = 0, diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp index ccbb65cefe..1eb903939c 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -55,7 +55,7 @@ AbstractTCPConnection::~AbstractTCPConnection() { void AbstractTCPConnection::socketRead() { if (sock.state() != QAbstractSocket::ConnectedState) { LOG(("MTP error: socket not connected in socketRead(), state: %1").arg(sock.state())); - emit error(); + emit error(kErrorCodeOther); return; } @@ -99,7 +99,7 @@ void AbstractTCPConnection::socketRead() { uint32 packetSize = tcpPacketSize(currentPos - packetRead); if (packetSize < 5 || packetSize > MTPPacketSizeMax) { LOG(("TCP Error: packet size = %1").arg(packetSize)); - emit error(); + emit error(kErrorCodeOther); return; } if (packetRead >= packetSize) { @@ -129,7 +129,7 @@ void AbstractTCPConnection::socketRead() { } } else if (bytes < 0) { LOG(("TCP Error: socket read return -1")); - emit error(); + emit error(kErrorCodeOther); return; } else { TCP_LOG(("TCP Info: no bytes read, but bytes available was true...")); @@ -157,11 +157,7 @@ mtpBuffer AbstractTCPConnection::handleResponse(const char *packet, uint32 lengt const mtpPrime *packetdata = reinterpret_cast(packet + (length - len)); TCP_LOG(("TCP Info: packet received, size = %1").arg(size * sizeof(mtpPrime))); if (size == 1) { - if (*packetdata == -429) { - LOG(("Protocol Error: -429 flood code returned!")); - } else { - LOG(("TCP Error: error packet received, code = %1").arg(*packetdata)); - } + LOG(("TCP Error: error packet received, code = %1").arg(*packetdata)); return mtpBuffer(1, *packetdata); } @@ -272,7 +268,7 @@ void TCPConnection::sendData(mtpBuffer &buffer) { if (buffer.size() < 3) { LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime))); TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str())); - emit error(); + emit error(kErrorCodeOther); return; } @@ -353,8 +349,7 @@ void TCPConnection::socketPacket(const char *packet, uint32 length) { mtpBuffer data = handleResponse(packet, length); if (data.size() == 1) { - bool mayBeBadKey = (data[0] == -410) && _sentEncrypted; - emit error(mayBeBadKey); + emit error(data[0]); } else if (status == UsingTcp) { receivedQueue.push_back(data); emit receivedData(); @@ -370,7 +365,7 @@ void TCPConnection::socketPacket(const char *packet, uint32 length) { } } catch (Exception &e) { DEBUG_LOG(("Connection Error: exception in parsing TCP fake pq-responce, %1").arg(e.what())); - emit error(); + emit error(kErrorCodeOther); } } } @@ -391,7 +386,7 @@ void TCPConnection::socketError(QAbstractSocket::SocketError e) { if (status == FinishedWork) return; handleError(e, sock); - emit error(); + emit error(kErrorCodeOther); } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index 0443794725..3effdb5e99 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -26,23 +26,22 @@ namespace MTP { // type DcId represents actual data center id, while in most cases // we use some shifted ids, like DcId() + X * DCShift -typedef int32 DcId; -typedef int32 ShiftedDcId; +using DcId = int32; +using ShiftedDcId = int32 ; } -typedef int32 mtpPrime; -typedef int32 mtpRequestId; -typedef uint64 mtpMsgId; -typedef uint64 mtpPingId; +using mtpPrime = int32; +using mtpRequestId = int32; +using mtpMsgId = uint64; +using mtpPingId = uint64; -typedef QVector mtpBuffer; -typedef uint32 mtpTypeId; +using mtpBuffer = QVector; +using mtpTypeId = uint32; class mtpRequestData; class mtpRequest : public QSharedPointer { public: - mtpRequest() { } explicit mtpRequest(mtpRequestData *ptr) : QSharedPointer(ptr) { @@ -51,7 +50,7 @@ public: uint32 innerLength() const; void write(mtpBuffer &to) const; - typedef void ResponseType; // don't know real response type =( + using ResponseType = void; // don't know real response type =( }; @@ -138,13 +137,13 @@ public: } }; -typedef QMap mtpPreRequestMap; -typedef QMap mtpRequestMap; -typedef QMap mtpMsgIdsSet; +using mtpPreRequestMap = QMap; +using mtpRequestMap = QMap; +using mtpMsgIdsSet = QMap; class mtpRequestIdsMap : public QMap { public: - typedef QMap ParentType; + using ParentType = QMap; mtpMsgId min() const { return size() ? cbegin().key() : 0; @@ -156,7 +155,7 @@ public: } }; -typedef QMap mtpResponseMap; +using mtpResponseMap = QMap; class mtpErrorUnexpected : public Exception { public: @@ -200,6 +199,7 @@ public: private: uint32 cnt; + }; template @@ -377,7 +377,7 @@ private: inline MTPint MTP_int(int32 v) { return MTPint(v); } -typedef MTPBoxed MTPInt; +using MTPInt = MTPBoxed; template class MTPflags { @@ -465,7 +465,7 @@ private: inline MTPlong MTP_long(uint64 v) { return MTPlong(v); } -typedef MTPBoxed MTPLong; +using MTPLong = MTPBoxed; inline bool operator==(const MTPlong &a, const MTPlong &b) { return a.v == b.v; @@ -514,7 +514,7 @@ private: inline MTPint128 MTP_int128(uint64 l, uint64 h) { return MTPint128(l, h); } -typedef MTPBoxed MTPInt128; +using MTPInt128 = MTPBoxed; inline bool operator==(const MTPint128 &a, const MTPint128 &b) { return a.l == b.l && a.h == b.h; @@ -559,7 +559,7 @@ private: inline MTPint256 MTP_int256(const MTPint128 &l, const MTPint128 &h) { return MTPint256(l, h); } -typedef MTPBoxed MTPInt256; +using MTPInt256 = MTPBoxed; inline bool operator==(const MTPint256 &a, const MTPint256 &b) { return a.l == b.l && a.h == b.h; @@ -605,7 +605,7 @@ private: inline MTPdouble MTP_double(float64 v) { return MTPdouble(v); } -typedef MTPBoxed MTPDouble; +using MTPDouble = MTPBoxed; inline bool operator==(const MTPdouble &a, const MTPdouble &b) { return a.v == b.v; @@ -724,7 +724,7 @@ inline MTPstring MTP_string(const char *v) { return MTPstring(new MTPDstring(v)); } MTPstring MTP_string(const QByteArray &v) = delete; -typedef MTPBoxed MTPString; +using MTPString = MTPBoxed; using MTPbytes = MTPstring; using MTPBytes = MTPBoxed; @@ -762,7 +762,7 @@ public: MTPDvector(const QVector &vec) : v(vec) { } - typedef QVector VType; + using VType = QVector; VType v; }; @@ -825,7 +825,7 @@ private: friend MTPvector MTP_vector(uint32 count, const U &value); template friend MTPvector MTP_vector(const QVector &v); - typedef typename MTPDvector::VType VType; + using VType = typename MTPDvector::VType; }; template inline MTPvector MTP_vector(uint32 count) { diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp index b991a9508f..9b5e08307b 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.cpp +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -31,7 +31,7 @@ void DcOptions::constructFromBuiltIn() { for (auto i = 0, l = builtInDcsCount(); i != l; ++i) { auto flags = MTPDdcOption::Flags(0); auto idWithShift = MTP::shiftDcId(bdcs[i].id, flags); - _data.insert(std::make_pair(idWithShift, Option(bdcs[i].id, flags, bdcs[i].ip, bdcs[i].port))); + _data.emplace(idWithShift, Option(bdcs[i].id, flags, bdcs[i].ip, bdcs[i].port)); DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port)); } @@ -39,7 +39,7 @@ void DcOptions::constructFromBuiltIn() { for (auto i = 0, l = builtInDcsCountIPv6(); i != l; ++i) { auto flags = MTPDdcOption::Flags(MTPDdcOption::Flag::f_ipv6); auto idWithShift = MTP::shiftDcId(bdcsipv6[i].id, flags); - _data.insert(std::make_pair(idWithShift, Option(bdcsipv6[i].id, flags, bdcsipv6[i].ip, bdcsipv6[i].port))); + _data.emplace(idWithShift, Option(bdcsipv6[i].id, flags, bdcsipv6[i].ip, bdcsipv6[i].port)); DEBUG_LOG(("MTP Info: adding built in DC %1 IPv6 connect option: %2:%3").arg(bdcsipv6[i].id).arg(bdcsipv6[i].ip).arg(bdcsipv6[i].port)); } } @@ -157,7 +157,7 @@ bool DcOptions::applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std: i->second.ip = ip; i->second.port = port; } else { - _data.insert(std::make_pair(dcIdWithShift, Option(dcId, flags, ip, port))); + _data.emplace(dcIdWithShift, Option(dcId, flags, ip, port)); } return true; } diff --git a/Telegram/SourceFiles/mtproto/dcenter.cpp b/Telegram/SourceFiles/mtproto/dcenter.cpp index 7d83a73c77..d88b9595d7 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.cpp +++ b/Telegram/SourceFiles/mtproto/dcenter.cpp @@ -52,9 +52,8 @@ void Dcenter::setKey(AuthKeyPtr &&key) { DEBUG_LOG(("AuthKey Info: MTProtoDC::setKey(%1), emitting authKeyCreated, dc %2").arg(key ? key->keyId() : 0).arg(_id)); _key = std::move(key); _connectionInited = false; - emit authKeyCreated(); - _instance->setKeyForWrite(_id, _key); + emit authKeyCreated(); } QReadWriteLock *Dcenter::keyMutex() const { @@ -76,9 +75,15 @@ ConfigLoader::ConfigLoader(Instance *instance, RPCDoneHandlerPtr onDone, RPCFail } void ConfigLoader::load() { - sendRequest(_instance->mainDcId()); - - _enumDCTimer.start(kEnumerateDcTimeout); + if (!_instance->isKeysDestroyer()) { + sendRequest(_instance->mainDcId()); + _enumDCTimer.start(kEnumerateDcTimeout); + } else { + auto ids = _instance->dcOptions()->sortedDcIds(); + t_assert(!ids.empty()); + _enumCurrent = ids.front(); + enumDC(); + } } mtpRequestId ConfigLoader::sendRequest(ShiftedDcId shiftedDcId) { @@ -86,12 +91,11 @@ mtpRequestId ConfigLoader::sendRequest(ShiftedDcId shiftedDcId) { } ConfigLoader::~ConfigLoader() { - _enumDCTimer.stop(); if (_enumRequest) { _instance->cancel(_enumRequest); } if (_enumCurrent) { - _instance->killSession(MTP::cfgDcId(_enumCurrent)); + _instance->killSession(MTP::configDcId(_enumCurrent)); } } @@ -103,7 +107,7 @@ void ConfigLoader::enumDC() { if (!_enumCurrent) { _enumCurrent = _instance->mainDcId(); } else { - _instance->killSession(MTP::cfgDcId(_enumCurrent)); + _instance->killSession(MTP::configDcId(_enumCurrent)); } auto ids = _instance->dcOptions()->sortedDcIds(); t_assert(!ids.empty()); @@ -114,7 +118,7 @@ void ConfigLoader::enumDC() { } else { _enumCurrent = *i; } - _enumRequest = sendRequest(MTP::cfgDcId(_enumCurrent)); + _enumRequest = sendRequest(MTP::configDcId(_enumCurrent)); _enumDCTimer.start(kEnumerateDcTimeout); } diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index 1711ab336f..9ee5d273b0 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -32,6 +32,14 @@ bool paused(); void pause(); void unpause(); +constexpr auto kDcShift = ShiftedDcId(10000); +constexpr auto kConfigDcShift = 0x01; +constexpr auto kLogoutDcShift = 0x02; +constexpr auto kMaxMediaDcCount = 0x10; +constexpr auto kBaseDownloadDcShift = 0x10; +constexpr auto kBaseUploadDcShift = 0x20; +constexpr auto kDestroyKeyStartDcShift = 0x100; + } // namespace internal class PauseHolder { @@ -40,12 +48,12 @@ public: restart(); } void restart() { - if (!base::take(_paused, true)) { + if (!std::exchange(_paused, true)) { internal::pause(); } } void release() { - if (base::take(_paused)) { + if (std::exchange(_paused, false)) { internal::unpause(); } } @@ -58,64 +66,68 @@ private: }; -constexpr ShiftedDcId DCShift = 10000; constexpr DcId bareDcId(ShiftedDcId shiftedDcId) { - return (shiftedDcId % DCShift); + return (shiftedDcId % internal::kDcShift); } constexpr ShiftedDcId shiftDcId(DcId dcId, int value) { - return dcId + DCShift * value; + return dcId + internal::kDcShift * value; } constexpr int getDcIdShift(ShiftedDcId shiftedDcId) { - return (shiftedDcId - bareDcId(shiftedDcId)) / DCShift; + return shiftedDcId / internal::kDcShift; } -// send(MTPhelp_GetConfig(), MTP::cfgDcId(dc)) - for dc enumeration -constexpr ShiftedDcId cfgDcId(DcId dcId) { - return shiftDcId(dcId, 0x01); +// send(MTPhelp_GetConfig(), MTP::configDcId(dc)) - for dc enumeration +constexpr ShiftedDcId configDcId(DcId dcId) { + return shiftDcId(dcId, internal::kConfigDcShift); } -// send(MTPauth_LogOut(), MTP::lgtDcId(dc)) - for logout of guest dcs enumeration -constexpr ShiftedDcId lgtDcId(DcId dcId) { - return shiftDcId(dcId, 0x02); +// send(MTPauth_LogOut(), MTP::logoutDcId(dc)) - for logout of guest dcs enumeration +constexpr ShiftedDcId logoutDcId(DcId dcId) { + return shiftDcId(dcId, internal::kLogoutDcShift); } namespace internal { constexpr ShiftedDcId downloadDcId(DcId dcId, int index) { - static_assert(MTPDownloadSessionsCount < 0x10, "Too large MTPDownloadSessionsCount!"); - return shiftDcId(dcId, 0x10 + index); + static_assert(MTPDownloadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!"); + return shiftDcId(dcId, internal::kBaseDownloadDcShift + index); }; } // namespace internal -// send(req, callbacks, MTP::dldDcId(dc, index)) - for download shifted dc id -inline ShiftedDcId dldDcId(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); return internal::downloadDcId(dcId, index); } -constexpr bool isDldDcId(ShiftedDcId shiftedDcId) { - return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, MTPDownloadSessionsCount - 1) + DCShift); +constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) { + return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, MTPDownloadSessionsCount - 1) + internal::kDcShift); } namespace internal { constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { - static_assert(MTPUploadSessionsCount < 0x10, "Too large MTPUploadSessionsCount!"); - return shiftDcId(dcId, 0x20 + index); + static_assert(MTPUploadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPUploadSessionsCount!"); + return shiftDcId(dcId, internal::kBaseUploadDcShift + index); }; } // namespace internal -// send(req, callbacks, MTP::uplDcId(index)) - for upload shifted dc id +// send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id // uploading always to the main dc so bareDcId == 0 -inline ShiftedDcId uplDcId(int index) { +inline ShiftedDcId uploadDcId(int index) { t_assert(index >= 0 && index < MTPUploadSessionsCount); return internal::uploadDcId(0, index); }; -constexpr bool isUplDcId(ShiftedDcId shiftedDcId) { - return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, MTPUploadSessionsCount - 1) + DCShift); +constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) { + return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, MTPUploadSessionsCount - 1) + internal::kDcShift); +} + +inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) { + auto shift = getDcIdShift(shiftedDcId); + return shiftDcId(bareDcId(shiftedDcId), shift ? (shift + 1) : internal::kDestroyKeyStartDcShift); } enum { diff --git a/Telegram/SourceFiles/mtproto/file_download.cpp b/Telegram/SourceFiles/mtproto/file_download.cpp index f3a5ce567d..ab5723726e 100644 --- a/Telegram/SourceFiles/mtproto/file_download.cpp +++ b/Telegram/SourceFiles/mtproto/file_download.cpp @@ -343,9 +343,10 @@ mtpFileLoader::mtpFileLoader(const StorageImageLocation *location, int32 size, L : FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading) , _dc(location->dc()) , _location(location) { - LoaderQueues::iterator i = queues.find(MTP::dldDcId(_dc, 0)); + auto shiftedDcId = MTP::downloadDcId(_dc, 0); + auto i = queues.find(shiftedDcId); if (i == queues.cend()) { - i = queues.insert(MTP::dldDcId(_dc, 0), FileLoaderQueue(MaxFileQueries)); + i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries)); } _queue = &i.value(); } @@ -356,9 +357,10 @@ mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, i , _id(id) , _access(access) , _version(version) { - LoaderQueues::iterator i = queues.find(MTP::dldDcId(_dc, 0)); + auto shiftedDcId = MTP::downloadDcId(_dc, 0); + auto i = queues.find(shiftedDcId); if (i == queues.cend()) { - i = queues.insert(MTP::dldDcId(_dc, 0), FileLoaderQueue(MaxFileQueries)); + i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries)); } _queue = &i.value(); } @@ -420,7 +422,7 @@ bool mtpFileLoader::loadPart() { App::app()->killDownloadSessionsStop(_dc); - mtpRequestId reqId = MTP::send(MTPupload_GetFile(loc, MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::dldDcId(_dc, dcIndex), 50); + mtpRequestId reqId = MTP::send(MTPupload_GetFile(loc, MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::downloadDcId(_dc, dcIndex), 50); ++_queue->queries; dr.v[dcIndex] += limit; diff --git a/Telegram/SourceFiles/mtproto/file_download.h b/Telegram/SourceFiles/mtproto/file_download.h index 5cb7c544fd..2d8d730750 100644 --- a/Telegram/SourceFiles/mtproto/file_download.h +++ b/Telegram/SourceFiles/mtproto/file_download.h @@ -220,6 +220,9 @@ protected: mutable QByteArray _imageFormat; mutable QPixmap _imagePixmap; +private: + void deleteLater(); + }; class StorageImageLocation; diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 43b3f9c12c..55aa692e04 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -31,7 +31,7 @@ namespace MTP { class Instance::Private { public: - Private(Instance *instance, DcOptions *options); + Private(Instance *instance, DcOptions *options, Instance::Mode mode); void start(Config &&config); @@ -40,7 +40,8 @@ public: DcId mainDcId() const; void setKeyForWrite(DcId dcId, const AuthKeyPtr &key); - AuthKeysMap getKeysForWrite() const; + AuthKeysList getKeysForWrite() const; + void addKeysForDestroy(AuthKeysList &&keys); DcOptions *dcOptions(); @@ -54,10 +55,11 @@ public: void cancel(mtpRequestId requestId); int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms void killSession(ShiftedDcId shiftedDcId); + void killSession(std::unique_ptr session); void stopSession(ShiftedDcId shiftedDcId); void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); - internal::DcenterPtr getDcById(DcId dcId); + internal::DcenterPtr getDcById(ShiftedDcId shiftedDcId); void unpaused(); void queueQuittingConnection(std::unique_ptr connection); @@ -91,6 +93,16 @@ public: internal::Session *getSession(ShiftedDcId shiftedDcId); + bool isKeysDestroyer() const { + return (_mode == Instance::Mode::KeysDestroyer); + } + + void scheduleKeyDestroy(ShiftedDcId shiftedDcId); + void performKeyDestroy(ShiftedDcId shiftedDcId); + void completedKeyDestroy(ShiftedDcId shiftedDcId); + + void clearKilledSessions(); + ~Private(); private: @@ -109,8 +121,8 @@ private: void checkDelayedRequests(); Instance *_instance = nullptr; - DcOptions *_dcOptions = nullptr; + Instance::Mode _mode = Instance::Mode::Normal; DcId _mainDcId = Config::kDefaultMainDc; bool _mainDcIdForced = false; @@ -118,6 +130,7 @@ private: internal::Session *_mainSession = nullptr; std::map> _sessions; + std::vector> _killedSessions; // delayed delete base::set_of_unique_ptr _quittingConnections; @@ -160,41 +173,64 @@ private: }; -Instance::Private::Private(Instance *instance, DcOptions *options) : _instance(instance) -, _dcOptions(options) { +Instance::Private::Private(Instance *instance, DcOptions *options, Instance::Mode mode) : _instance(instance) +, _dcOptions(options) +, _mode(mode) { } void Instance::Private::start(Config &&config) { - unixtimeInit(); + if (isKeysDestroyer()) { + _instance->connect(_instance, SIGNAL(keyDestroyed(qint32)), _instance, SLOT(onKeyDestroyed(qint32)), Qt::QueuedConnection); + } else { + unixtimeInit(); + } - for (auto &keyData : config.keys) { - auto dcId = keyData.first; - auto key = std::make_shared(); - key->setDC(dcId); - key->setKey(keyData.second); + for (auto &key : config.keys) { + auto dcId = key->dcId(); - _keysForWrite[dcId] = key; + auto shiftedDcId = dcId; + if (isKeysDestroyer()) { + shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId); + + // There could be several keys for one dc if we're destroying them. + // Place them all in separate shiftedDcId so that they won't conflict. + while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) { + shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId); + } + } + _keysForWrite[shiftedDcId] = key; auto dc = std::make_shared(_instance, dcId, std::move(key)); - _dcenters.emplace(dcId, std::move(dc)); + _dcenters.emplace(shiftedDcId, std::move(dc)); } if (config.mainDcId != Config::kNotSetMainDc) { _mainDcId = config.mainDcId; _mainDcIdForced = true; } - if (_mainDcId != Config::kNoneMainDc) { + + if (isKeysDestroyer()) { + for (auto &dc : _dcenters) { + auto shiftedDcId = dc.first; + auto session = std::make_unique(_instance, shiftedDcId); + auto it = _sessions.emplace(shiftedDcId, std::move(session)).first; + it->second->start(); + } + } else if (_mainDcId != Config::kNoneMainDc) { auto main = std::make_unique(_instance, _mainDcId); _mainSession = main.get(); - auto newMainDcId = main->getDcWithShift(); - _sessions.emplace(newMainDcId, std::move(main)); + _sessions.emplace(_mainDcId, std::move(main)); + _mainSession->start(); } _checkDelayedTimer.setTimeoutHandler([this] { checkDelayedRequests(); }); - configLoadRequest(); + t_assert((_mainDcId == Config::kNoneMainDc) == isKeysDestroyer()); + if (!isKeysDestroyer()) { + configLoadRequest(); + } } void Instance::Private::suggestMainDcId(DcId mainDcId) { @@ -335,27 +371,29 @@ int32 Instance::Private::state(mtpRequestId requestId) { // < 0 means waiting fo } void Instance::Private::killSession(ShiftedDcId shiftedDcId) { - auto it = _sessions.find(shiftedDcId); - if (it != _sessions.cend()) { - bool wasMain = (it->second.get() == _mainSession); - - it->second->kill(); - it->second.release()->deleteLater(); - _sessions.erase(it); - - if (wasMain) { - auto main = std::make_unique(_instance, _mainDcId); - _mainSession = main.get(); - auto newMainDcId = main->getDcWithShift(); - it = _sessions.find(newMainDcId); - if (it != _sessions.cend()) { - it->second->kill(); - it->second.release()->deleteLater(); - _sessions.erase(it); - } - _sessions.insert(std::make_pair(newMainDcId, std::move(main))); + auto checkIfMainAndKill = [this](ShiftedDcId shiftedDcId) { + auto it = _sessions.find(shiftedDcId); + if (it != _sessions.cend()) { + _killedSessions.push_back(std::move(it->second)); + _sessions.erase(it); + _killedSessions.back()->kill(); + return (_killedSessions.back().get() == _mainSession); } + return false; + }; + if (checkIfMainAndKill(shiftedDcId)) { + checkIfMainAndKill(_mainDcId); + + auto main = std::make_unique(_instance, _mainDcId); + _mainSession = main.get(); + _sessions.emplace(_mainDcId, std::move(main)); + _mainSession->start(); } + QMetaObject::invokeMethod(_instance, "onClearKilledSessions", Qt::QueuedConnection); +} + +void Instance::Private::clearKilledSessions() { + _killedSessions.clear(); } void Instance::Private::stopSession(ShiftedDcId shiftedDcId) { @@ -380,13 +418,13 @@ void Instance::Private::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFai } for (auto dcId : dcIds) { if (dcId != mainDcId()) { - auto shiftedDcId = MTP::lgtDcId(dcId); + auto shiftedDcId = MTP::logoutDcId(dcId); auto requestId = _instance->send(MTPauth_LogOut(), rpcDone([this](mtpRequestId requestId) { logoutGuestDone(requestId); }), rpcFail([this](mtpRequestId requestId) { return logoutGuestDone(requestId); }), shiftedDcId); - _logoutGuestRequestIds.insert(std::make_pair(shiftedDcId, requestId)); + _logoutGuestRequestIds.emplace(shiftedDcId, requestId); } } } @@ -402,12 +440,15 @@ bool Instance::Private::logoutGuestDone(mtpRequestId requestId) { return false; } -internal::DcenterPtr Instance::Private::getDcById(DcId dcId) { - auto it = _dcenters.find(dcId); +internal::DcenterPtr Instance::Private::getDcById(ShiftedDcId shiftedDcId) { + auto it = _dcenters.find(shiftedDcId); if (it == _dcenters.cend()) { - auto result = std::make_shared(_instance, dcId, AuthKeyPtr()); - auto insert = std::make_pair(dcId, std::move(result)); - it = _dcenters.insert(std::move(insert)).first; + auto dcId = bareDcId(shiftedDcId); + it = _dcenters.find(dcId); + if (it == _dcenters.cend()) { + auto result = std::make_shared(_instance, dcId, AuthKeyPtr()); + it = _dcenters.emplace(dcId, std::move(result)).first; + } } return it->second; } @@ -421,15 +462,43 @@ void Instance::Private::setKeyForWrite(DcId dcId, const AuthKeyPtr &key) { } } -AuthKeysMap Instance::Private::getKeysForWrite() const { - auto result = AuthKeysMap(); +AuthKeysList Instance::Private::getKeysForWrite() const { + auto result = AuthKeysList(); + QReadLocker lock(&_keysForWriteLock); + result.reserve(_keysForWrite.size()); for (auto &key : _keysForWrite) { result.push_back(key.second); } return result; } +void Instance::Private::addKeysForDestroy(AuthKeysList &&keys) { + t_assert(isKeysDestroyer()); + + for (auto &key : keys) { + auto dcId = key->dcId(); + auto shiftedDcId = MTP::destroyKeyNextDcId(dcId); + + { + QWriteLocker lock(&_keysForWriteLock); + // There could be several keys for one dc if we're destroying them. + // Place them all in separate shiftedDcId so that they won't conflict. + while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) { + shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId); + } + _keysForWrite[shiftedDcId] = key; + } + + auto dc = std::make_shared(_instance, dcId, std::move(key)); + _dcenters.emplace(shiftedDcId, std::move(dc)); + + auto session = std::make_unique(_instance, shiftedDcId); + auto it = _sessions.emplace(shiftedDcId, std::move(session)).first; + it->second->start(); + } +} + DcOptions *Instance::Private::dcOptions() { return _dcOptions; } @@ -1046,10 +1115,57 @@ internal::Session *Instance::Private::getSession(ShiftedDcId shiftedDcId) { auto it = _sessions.find(shiftedDcId); if (it == _sessions.cend()) { it = _sessions.emplace(shiftedDcId, std::make_unique(_instance, shiftedDcId)).first; + it->second->start(); } return it->second.get(); } +void Instance::Private::scheduleKeyDestroy(ShiftedDcId shiftedDcId) { + t_assert(isKeysDestroyer()); + + _instance->send(MTPauth_LogOut(), rpcDone([this, shiftedDcId](const MTPBool &result) { + performKeyDestroy(shiftedDcId); + }), rpcFail([this, shiftedDcId](const RPCError &error) { + if (isDefaultHandledError(error)) return false; + performKeyDestroy(shiftedDcId); + return true; + }), shiftedDcId); +} + +void Instance::Private::performKeyDestroy(ShiftedDcId shiftedDcId) { + t_assert(isKeysDestroyer()); + + _instance->send(MTPDestroy_auth_key(), rpcDone([this, shiftedDcId](const MTPDestroyAuthKeyRes &result) { + switch (result.type()) { + case mtpc_destroy_auth_key_ok: LOG(("MTP Info: key %1 destroyed.").arg(shiftedDcId)); break; + case mtpc_destroy_auth_key_fail: { + LOG(("MTP Error: key %1 destruction fail, leave it for now.").arg(shiftedDcId)); + killSession(shiftedDcId); + } break; + case mtpc_destroy_auth_key_none: LOG(("MTP Info: key %1 already destroyed.").arg(shiftedDcId)); break; + } + emit _instance->keyDestroyed(shiftedDcId); + }), rpcFail([this, shiftedDcId](const RPCError &error) { + LOG(("MTP Error: key %1 destruction resulted in error: %2").arg(shiftedDcId).arg(error.type())); + emit _instance->keyDestroyed(shiftedDcId); + return true; + }), shiftedDcId); +} + +void Instance::Private::completedKeyDestroy(ShiftedDcId shiftedDcId) { + t_assert(isKeysDestroyer()); + + _dcenters.erase(shiftedDcId); + { + QWriteLocker lock(&_keysForWriteLock); + _keysForWrite.erase(shiftedDcId); + } + killSession(shiftedDcId); + if (_dcenters.empty()) { + emit _instance->allKeysDestroyed(); + } +} + void Instance::Private::setUpdatesHandler(RPCDoneHandlerPtr onDone) { _globalHandler.onDone = onDone; } @@ -1077,10 +1193,13 @@ Instance::Private::~Private() { for (auto &session : base::take(_sessions)) { session.second->kill(); } + + // It accesses Instance in destructor, so it should be destroyed first. + _configLoader.reset(); } -Instance::Instance(DcOptions *options, Config &&config) : QObject() -, _private(std::make_unique(this, options)) { +Instance::Instance(DcOptions *options, Mode mode, Config &&config) : QObject() +, _private(std::make_unique(this, options, mode)) { _private->start(std::move(config)); } @@ -1144,18 +1263,22 @@ void Instance::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) { _private->logout(onDone, onFail); } -internal::DcenterPtr Instance::getDcById(DcId dcId) { - return _private->getDcById(dcId); +internal::DcenterPtr Instance::getDcById(ShiftedDcId shiftedDcId) { + return _private->getDcById(shiftedDcId); } void Instance::setKeyForWrite(DcId dcId, const AuthKeyPtr &key) { _private->setKeyForWrite(dcId, key); } -AuthKeysMap Instance::getKeysForWrite() const { +AuthKeysList Instance::getKeysForWrite() const { return _private->getKeysForWrite(); } +void Instance::addKeysForDestroy(AuthKeysList &&keys) { + _private->addKeysForDestroy(std::move(keys)); +} + DcOptions *Instance::dcOptions() { return _private->dcOptions(); } @@ -1232,6 +1355,22 @@ internal::Session *Instance::getSession(ShiftedDcId shiftedDcId) { return _private->getSession(shiftedDcId); } +bool Instance::isKeysDestroyer() const { + return _private->isKeysDestroyer(); +} + +void Instance::scheduleKeyDestroy(ShiftedDcId shiftedDcId) { + _private->scheduleKeyDestroy(shiftedDcId); +} + +void Instance::onKeyDestroyed(qint32 shiftedDcId) { + _private->completedKeyDestroy(shiftedDcId); +} + +void Instance::onClearKilledSessions() { + _private->clearKilledSessions(); +} + Instance::~Instance() = default; } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.h b/Telegram/SourceFiles/mtproto/mtp_instance.h index 82e5a05fc0..5b4293ca8f 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.h +++ b/Telegram/SourceFiles/mtproto/mtp_instance.h @@ -39,9 +39,13 @@ public: static constexpr auto kDefaultMainDc = 2; DcId mainDcId = kNotSetMainDc; - std::map keys; + AuthKeysList keys; }; - Instance(DcOptions *options, Config &&config); + enum class Mode { + Normal, + KeysDestroyer, + }; + Instance(DcOptions *options, Mode mode, Config &&config); Instance(const Instance &other) = delete; Instance &operator=(const Instance &other) = delete; @@ -50,8 +54,9 @@ public: void setMainDcId(DcId mainDcId); DcId mainDcId() const; - void Instance::setKeyForWrite(DcId dcId, const AuthKeyPtr &key); - AuthKeysMap Instance::getKeysForWrite() const; + void setKeyForWrite(DcId dcId, const AuthKeyPtr &key); + AuthKeysList getKeysForWrite() const; + void addKeysForDestroy(AuthKeysList &&keys); DcOptions *dcOptions(); @@ -85,7 +90,7 @@ public: void stopSession(ShiftedDcId shiftedDcId); void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); - internal::DcenterPtr getDcById(DcId dcId); + internal::DcenterPtr getDcById(ShiftedDcId shiftedDcId); void unpaused(); void queueQuittingConnection(std::unique_ptr connection); @@ -111,6 +116,9 @@ public: // return true if need to clean request data bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); + bool isKeysDestroyer() const; + void scheduleKeyDestroy(ShiftedDcId shiftedDcId); + ~Instance(); public slots: @@ -119,6 +127,12 @@ public slots: signals: void configLoaded(); + void keyDestroyed(qint32 shiftedDcId); + void allKeysDestroyed(); + +private slots: + void onKeyDestroyed(qint32 shiftedDcId); + void onClearKilledSessions(); private: internal::Session *getSession(ShiftedDcId shiftedDcId); diff --git a/Telegram/SourceFiles/mtproto/session.cpp b/Telegram/SourceFiles/mtproto/session.cpp index dbe1498774..ee09e0f17c 100644 --- a/Telegram/SourceFiles/mtproto/session.cpp +++ b/Telegram/SourceFiles/mtproto/session.cpp @@ -70,33 +70,30 @@ void SessionData::clear(Instance *instance) { instance->clearCallbacksDelayed(clearCallbacks); } - -Session::Session(Instance *instance, ShiftedDcId requestedShiftedDcId) : QObject() +Session::Session(Instance *instance, ShiftedDcId shiftedDcId) : QObject() , _instance(instance) -, data(this) { +, data(this) +, dcWithShift(shiftedDcId) { connect(&timeouter, SIGNAL(timeout()), this, SLOT(checkRequestsByTimer())); timeouter.start(1000); connect(&sender, SIGNAL(timeout()), this, SLOT(needToResumeAndSend())); +} - _connection = std::make_unique(_instance); - dcWithShift = _connection->prepare(&data, requestedShiftedDcId); - if (!dcWithShift) { - _connection.reset(); - DEBUG_LOG(("Session Info: could not start connection to dc %1").arg(requestedShiftedDcId)); - return; - } +void Session::start() { createDcData(); - _connection->start(); + _connection = std::make_unique(_instance); + _connection->start(&data, dcWithShift); + if (_instance->isKeysDestroyer()) { + _instance->scheduleKeyDestroy(dcWithShift); + } } void Session::createDcData() { if (dc) { return; } - auto dcId = bareDcId(dcWithShift); - - dc = _instance->getDcById(dcId); + dc = _instance->getDcById(dcWithShift); ReadLockerAttempt lock(keyMutex()); data.setKey(lock ? dc->getKey() : AuthKeyPtr()); @@ -193,15 +190,9 @@ void Session::needToResumeAndSend() { } if (!_connection) { DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(dcWithShift)); - _connection = std::make_unique(_instance); - if (!_connection->prepare(&data, dcWithShift)) { - _connection.reset(); - DEBUG_LOG(("Session Info: could not start connection to dcWithShift %1").arg(dcWithShift)); - dcWithShift = 0; - return; - } createDcData(); - _connection->start(); + _connection = std::make_unique(_instance); + _connection->start(&data, dcWithShift); } if (_ping) { _ping = false; diff --git a/Telegram/SourceFiles/mtproto/session.h b/Telegram/SourceFiles/mtproto/session.h index 9136bd65d5..f9bf6df542 100644 --- a/Telegram/SourceFiles/mtproto/session.h +++ b/Telegram/SourceFiles/mtproto/session.h @@ -278,8 +278,9 @@ class Session : public QObject { Q_OBJECT public: - Session(Instance *instance, ShiftedDcId requestedShiftedDcId); + Session(Instance *instance, ShiftedDcId shiftedDcId); + void start(); void restart(); void stop(); void kill(); diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp index 97cabcb91a..49f35d928b 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp @@ -169,7 +169,7 @@ void PeerListWidget::mouseReleaseEvent(QMouseEvent *e) { void PeerListWidget::mousePressReleased(Qt::MouseButton button) { repaintRow(_pressed); - auto pressed = base::take(_pressed, -1); + auto pressed = std::exchange(_pressed, -1); auto pressedRemove = base::take(_pressedRemove); if (pressed >= 0 && pressed < _items.size()) { if (auto &ripple = _items[pressed]->ripple) { diff --git a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp index b824e7046a..879fe33c17 100644 --- a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp +++ b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp @@ -306,7 +306,7 @@ void InnerWidget::mouseMoveEvent(QMouseEvent *e) { void InnerWidget::mouseReleaseEvent(QMouseEvent *e) { updateRow(_pressed); - auto pressed = base::take(_pressed, -1); + auto pressed = std::exchange(_pressed, -1); if (pressed >= 0 && pressed < _items.size()) { if (auto &ripple = _items[pressed]->ripple) { ripple->lastStop(); diff --git a/Telegram/SourceFiles/serialize/serialize_common.h b/Telegram/SourceFiles/serialize/serialize_common.h index 4cbb0a5a7d..725a8b4eba 100644 --- a/Telegram/SourceFiles/serialize/serialize_common.h +++ b/Telegram/SourceFiles/serialize/serialize_common.h @@ -40,4 +40,18 @@ void writeStorageImageLocation(QDataStream &stream, const StorageImageLocation & StorageImageLocation readStorageImageLocation(QDataStream &stream); int storageImageLocationSize(); +template +inline T read(QDataStream &stream) { + auto result = T(); + stream >> result; + return result; +} + +template <> +inline MTP::AuthKey::Data read(QDataStream &stream) { + auto result = MTP::AuthKey::Data(); + stream.readRawData(result.data(), result.size()); + return result; +} + } // namespace Serialize diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index e62522c2ee..bf536d9232 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -1133,8 +1133,8 @@ void StickerPanInner::setPressedFeaturedSetAdd(int newPressedFeaturedSetAdd) { void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) { _previewTimer.stop(); - auto pressed = base::take(_pressed, -1); - auto pressedFeaturedSet = base::take(_pressedFeaturedSet, -1); + auto pressed = std::exchange(_pressed, -1); + auto pressedFeaturedSet = std::exchange(_pressedFeaturedSet, -1); auto pressedFeaturedSetAdd = _pressedFeaturedSetAdd; setPressedFeaturedSetAdd(-1); if (pressedFeaturedSetAdd != _selectedFeaturedSetAdd) { diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index d3895c0302..9e65e2ac85 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_history.h" #include "window/themes/window_theme.h" #include "auth_session.h" +#include "messenger.h" namespace { @@ -1530,26 +1531,27 @@ void DocumentData::performActionOnLoad() { bool DocumentData::loaded(FilePathResolveType type) const { if (loading() && _loader->done()) { if (_loader->fileType() == mtpc_storage_fileUnknown) { - _loader->deleteLater(); - _loader->stop(); - _loader = CancelledMtpFileLoader; + destroyLoaderDelayed(CancelledMtpFileLoader); } else { - DocumentData *that = const_cast(this); + auto that = const_cast(this); that->_location = FileLocation(mtpToStorageType(_loader->fileType()), _loader->fileName()); that->_data = _loader->bytes(); if (that->sticker() && !_loader->imagePixmap().isNull()) { that->sticker()->img = ImagePtr(_data, _loader->imageFormat(), _loader->imagePixmap()); } - - _loader->deleteLater(); - _loader->stop(); - _loader = nullptr; + destroyLoaderDelayed(); } notifyLayoutChanged(); } return !data().isEmpty() || !filepath(type).isEmpty(); } +void DocumentData::destroyLoaderDelayed(mtpFileLoader *newValue) const { + _loader->stop(); + auto loader = std::unique_ptr(std::exchange(_loader, newValue)); + Messenger::Instance().delayedDestroyLoader(std::move(loader)); +} + bool DocumentData::loading() const { return _loader && _loader != CancelledMtpFileLoader; } @@ -1632,11 +1634,10 @@ void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMs void DocumentData::cancel() { if (!loading()) return; - auto loader = base::take(_loader); - _loader = CancelledMtpFileLoader; + auto loader = std::exchange(_loader, CancelledMtpFileLoader); loader->cancel(); - loader->deleteLater(); loader->stop(); + Messenger::Instance().delayedDestroyLoader(std::unique_ptr(loader)); notifyLayoutChanged(); if (auto main = App::main()) { @@ -1803,9 +1804,7 @@ bool DocumentData::setRemoteVersion(int32 version) { _data = QByteArray(); status = FileReady; if (loading()) { - _loader->deleteLater(); - _loader->stop(); - _loader = nullptr; + destroyLoaderDelayed(); } return true; } @@ -1849,9 +1848,7 @@ void DocumentData::collectLocalData(DocumentData *local) { DocumentData::~DocumentData() { if (loading()) { - _loader->deleteLater(); - _loader->stop(); - _loader = nullptr; + destroyLoaderDelayed(); } } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index bbee9c0cab..674e3a608e 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -1256,6 +1256,8 @@ private: void notifyLayoutChanged() const; + void destroyLoaderDelayed(mtpFileLoader *newValue = nullptr) const; + }; VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit); diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index c4c7c2a5e7..6ab649bb14 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -23,8 +23,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "localstorage.h" - #include "pspecific.h" +#include "messenger.h" namespace Images { namespace { @@ -828,9 +828,7 @@ void RemoteImage::doCheckload() const { QPixmap data = _loader->imagePixmap(shrinkBox()); if (data.isNull()) { - _loader->deleteLater(); - _loader->stop(); - _loader = CancelledFileLoader; + destroyLoaderDelayed(CancelledFileLoader); return; } @@ -846,13 +844,17 @@ void RemoteImage::doCheckload() const { invalidateSizeCache(); - _loader->deleteLater(); - _loader->stop(); - _loader = nullptr; + destroyLoaderDelayed(); _forgot = false; } +void RemoteImage::destroyLoaderDelayed(FileLoader *newValue) const { + _loader->stop(); + auto loader = std::unique_ptr(std::exchange(_loader, newValue)); + Messenger::Instance().delayedDestroyLoader(std::move(loader)); +} + void RemoteImage::loadLocal() { if (loaded() || amLoading()) return; @@ -875,9 +877,7 @@ void RemoteImage::setData(QByteArray &bytes, const QByteArray &bytesFormat) { invalidateSizeCache(); if (amLoading()) { - _loader->deleteLater(); - _loader->stop(); - _loader = nullptr; + destroyLoaderDelayed(); } _saved = bytes; _format = fmt; @@ -930,9 +930,7 @@ RemoteImage::~RemoteImage() { globalAcquiredSize -= int64(_data.width()) * _data.height() * 4; } if (amLoading()) { - _loader->deleteLater(); - _loader->stop(); - _loader = 0; + destroyLoaderDelayed(); } } @@ -948,13 +946,10 @@ bool RemoteImage::displayLoading() const { void RemoteImage::cancel() { if (!amLoading()) return; - FileLoader *l = _loader; - _loader = CancelledFileLoader; - if (l) { - l->cancel(); - l->deleteLater(); - l->stop(); - } + auto loader = std::exchange(_loader, CancelledFileLoader); + loader->cancel(); + loader->stop(); + Messenger::Instance().delayedDestroyLoader(std::unique_ptr(loader)); } float64 RemoteImage::progress() const { diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index 2f0931dddf..99b198fece 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -282,10 +282,6 @@ inline StorageKey storageKey(const StorageImageLocation &location) { class RemoteImage : public Image { public: - - RemoteImage() : _loader(0) { - } - void automaticLoad(const HistoryItem *item); // auto load photo void automaticLoadSettingsChanged(); @@ -320,12 +316,14 @@ protected: void loadLocal(); private: - mutable FileLoader *_loader; + mutable FileLoader *_loader = nullptr; bool amLoading() const { return _loader && _loader != CancelledFileLoader; } void doCheckload() const; + void destroyLoaderDelayed(FileLoader *newValue = nullptr) const; + }; class StorageImage : public RemoteImage { diff --git a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp index 41c3d3eb5f..426a43c5aa 100644 --- a/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/discrete_sliders.cpp @@ -122,7 +122,7 @@ void DiscreteSlider::mouseMoveEvent(QMouseEvent *e) { } void DiscreteSlider::mouseReleaseEvent(QMouseEvent *e) { - auto pressed = base::take(_pressed, -1); + auto pressed = std::exchange(_pressed, -1); if (pressed < 0) return; auto index = getIndexFromPosition(e->pos()); diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp index b94950795e..804f514d66 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/menu.cpp @@ -233,7 +233,7 @@ void Menu::itemPressed(TriggeredSource source) { } void Menu::itemReleased(TriggeredSource source) { - auto pressed = base::take(_pressed, -1); + auto pressed = std::exchange(_pressed, -1); if (pressed >= 0 && pressed < _actions.size()) { if (source == TriggeredSource::Mouse && _actionsData[pressed].ripple) { _actionsData[pressed].ripple->lastStop(); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp index c189b11b4a..7bfaa26385 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp @@ -528,7 +528,7 @@ void EditorBlock::saveEditing(QColor value) { auto &row = _data[_editing]; auto name = row.name(); if (_type == Type::New) { - auto removing = base::take(_editing, -1); + auto removing = std::exchange(_editing, -1); setSelected(-1); setPressed(-1);