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