Encapsulated DcOptions to an independent class.

This commit is contained in:
John Preston 2017-02-23 09:57:04 +03:00
parent 90b3b5c3e5
commit a35947141c
24 changed files with 606 additions and 329 deletions

View file

@ -39,6 +39,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "history/history_location_manager.h" #include "history/history_location_manager.h"
#include "core/task_queue.h" #include "core/task_queue.h"
#include "mtproto/dc_options.h"
namespace { namespace {
@ -340,7 +341,10 @@ void Application::closeApplication() {
manager->clearAllFast(); manager->clearAllFast();
} }
if (AppObject) {
AppClass::Instance().joinThreads();
delete base::take(AppObject); delete base::take(AppObject);
}
Sandbox::finish(); Sandbox::finish();
@ -682,7 +686,6 @@ namespace Sandbox {
new AppClass(); new AppClass();
} }
} }
AppClass::AppClass() : QObject() { AppClass::AppClass() : QObject() {
@ -692,7 +695,9 @@ AppClass::AppClass() : QObject() {
ThirdParty::start(); ThirdParty::start();
Global::start(); Global::start();
Local::start();
startLocalStorage();
if (Local::oldSettingsVersion() < AppVersion) { if (Local::oldSettingsVersion() < AppVersion) {
psNewVersion(); psNewVersion();
} }
@ -816,6 +821,18 @@ void AppClass::loadLanguage() {
application()->installTranslator(_translator = new Translator()); application()->installTranslator(_translator = new Translator());
} }
void AppClass::startLocalStorage() {
_dcOptions = std::make_unique<MTP::DcOptions>();
_dcOptions->constructFromBuiltIn();
Local::start();
subscribe(_dcOptions->changed(), [](const MTP::DcOptions::Ids &ids) {
Local::writeSettings();
for (auto id : ids) {
MTP::restart(id);
}
});
}
void AppClass::regPhotoUpdate(const PeerId &peer, const FullMsgId &msgId) { void AppClass::regPhotoUpdate(const PeerId &peer, const FullMsgId &msgId) {
photoUpdates.insert(msgId, peer); photoUpdates.insert(msgId, peer);
} }
@ -1096,6 +1113,10 @@ void AppClass::checkMapVersion() {
} }
} }
void AppClass::joinThreads() {
MTP::finish();
}
AppClass::~AppClass() { AppClass::~AppClass() {
Shortcuts::finish(); Shortcuts::finish();
@ -1110,8 +1131,6 @@ AppClass::~AppClass() {
App::deinitMedia(); App::deinitMedia();
deinitLocationManager(); deinitLocationManager();
MTP::finish();
AppObject = nullptr; AppObject = nullptr;
delete base::take(_uploader); delete base::take(_uploader);
delete base::take(_translator); delete base::take(_translator);

View file

@ -23,6 +23,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mainwindow.h" #include "mainwindow.h"
#include "pspecific.h" #include "pspecific.h"
#include "core/single_timer.h" #include "core/single_timer.h"
#include "core/observer.h"
namespace MTP {
class DcOptions;
} // namespace MTP
class UpdateChecker; class UpdateChecker;
class Application : public QApplication { class Application : public QApplication {
@ -143,17 +148,28 @@ class MainWidget;
class FileUploader; class FileUploader;
class Translator; class Translator;
class AppClass : public QObject, public RPCSender { class AppClass : public QObject, public RPCSender, private base::Subscriber {
Q_OBJECT Q_OBJECT
public: public:
AppClass(); AppClass();
void joinThreads();
~AppClass(); ~AppClass();
static AppClass *app(); static AppClass *app();
static MainWindow *wnd(); static MainWindow *wnd();
static MainWidget *main(); static MainWidget *main();
static AppClass &Instance() {
auto result = app();
t_assert(result != nullptr);
return *result;
}
MTP::DcOptions *dcOptions() {
return _dcOptions.get();
}
FileUploader *uploader(); FileUploader *uploader();
void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId); void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId);
void regPhotoUpdate(const PeerId &peer, const FullMsgId &msgId); void regPhotoUpdate(const PeerId &peer, const FullMsgId &msgId);
@ -179,7 +195,6 @@ public:
void handleAppDeactivated(); void handleAppDeactivated();
signals: signals:
void peerPhotoDone(PeerId peer); void peerPhotoDone(PeerId peer);
void peerPhotoFail(PeerId peer); void peerPhotoFail(PeerId peer);
@ -202,6 +217,7 @@ public slots:
void call_handleObservables(); void call_handleObservables();
private: private:
void startLocalStorage();
void loadLanguage(); void loadLanguage();
QMap<FullMsgId, PeerId> photoUpdates; QMap<FullMsgId, PeerId> photoUpdates;
@ -215,4 +231,6 @@ private:
FileUploader *_uploader = nullptr; FileUploader *_uploader = nullptr;
Translator *_translator = nullptr; Translator *_translator = nullptr;
std::unique_ptr<MTP::DcOptions> _dcOptions;
}; };

View file

@ -51,8 +51,6 @@ enum {
MTPDownloadSessionsCount = 2, // max 2 download sessions is created MTPDownloadSessionsCount = 2, // max 2 download sessions is created
MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill
MTPEnumDCTimeout = 8000, // 8 seconds timeout for help_getConfig to work (then move to other dc)
MTPDebugBufferSize = 1024 * 1024, // 1 mb start size MTPDebugBufferSize = 1024 * 1024, // 1 mb start size
MaxUsersPerInvite = 100, // max users in one super group invite request MaxUsersPerInvite = 100, // max users in one super group invite request

View file

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "core/basic_types.h" #include "core/basic_types.h"
#include <array> #include <array>
#include <algorithm>
namespace base { namespace base {
@ -82,6 +83,12 @@ scope_guard_helper<Lambda> scope_guard(Lambda on_scope_exit) {
return scope_guard_helper<Lambda>(std::move(on_scope_exit)); return scope_guard_helper<Lambda>(std::move(on_scope_exit));
} }
template <typename Container, typename T>
inline bool contains(const Container &container, const T &value) {
auto end = std::end(container);
return std::find(std::begin(container), end, value) != end;
}
} // namespace base } // namespace base
// using for_const instead of plain range-based for loop to ensure usage of const_iterator // using for_const instead of plain range-based for loop to ensure usage of const_iterator

View file

@ -671,8 +671,6 @@ struct Data {
TimeMs LastFeaturedStickersUpdate = 0; TimeMs LastFeaturedStickersUpdate = 0;
Stickers::Order ArchivedStickerSetsOrder; Stickers::Order ArchivedStickerSetsOrder;
MTP::DcOptions DcOptions;
CircleMasksMap CircleMasks; CircleMasksMap CircleMasks;
base::Observable<void> SelfChanged; base::Observable<void> SelfChanged;
@ -795,8 +793,6 @@ DefineRefVar(Global, base::Observable<void>, FeaturedStickerSetsUnreadCountChang
DefineVar(Global, TimeMs, LastFeaturedStickersUpdate); DefineVar(Global, TimeMs, LastFeaturedStickersUpdate);
DefineVar(Global, Stickers::Order, ArchivedStickerSetsOrder); DefineVar(Global, Stickers::Order, ArchivedStickerSetsOrder);
DefineVar(Global, MTP::DcOptions, DcOptions);
DefineRefVar(Global, CircleMasksMap, CircleMasks); DefineRefVar(Global, CircleMasksMap, CircleMasks);
DefineRefVar(Global, base::Observable<void>, SelfChanged); DefineRefVar(Global, base::Observable<void>, SelfChanged);

View file

@ -369,8 +369,6 @@ DeclareRefVar(base::Observable<void>, FeaturedStickerSetsUnreadCountChanged);
DeclareVar(TimeMs, LastFeaturedStickersUpdate); DeclareVar(TimeMs, LastFeaturedStickersUpdate);
DeclareVar(Stickers::Order, ArchivedStickerSetsOrder); DeclareVar(Stickers::Order, ArchivedStickerSetsOrder);
DeclareVar(MTP::DcOptions, DcOptions);
typedef QMap<uint64, QPixmap> CircleMasksMap; typedef QMap<uint64, QPixmap> CircleMasksMap;
DeclareRefVar(CircleMasksMap, CircleMasks); DeclareRefVar(CircleMasksMap, CircleMasks);

View file

@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang.h" #include "lang.h"
#include "media/media_audio.h" #include "media/media_audio.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "mtproto/dc_options.h"
#include "application.h" #include "application.h"
#include "apiwrap.h" #include "apiwrap.h"
@ -563,6 +564,7 @@ enum {
dbiTheme = 0x47, dbiTheme = 0x47,
dbiDialogsWidthRatio = 0x48, dbiDialogsWidthRatio = 0x48,
dbiUseExternalVideoPlayer = 0x49, dbiUseExternalVideoPlayer = 0x49,
dbiDcOptions = 0x4a,
dbiEncryptedWithSalt = 333, dbiEncryptedWithSalt = 333,
dbiEncrypted = 444, dbiEncrypted = 444,
@ -829,8 +831,15 @@ void _readReportSpamStatuses() {
} }
} }
MTP::DcOptions *_dcOpts = 0; struct ReadSettingsContext {
bool _readSetting(quint32 blockId, QDataStream &stream, int version) { MTP::DcOptions dcOptions;
};
void applyReadContext(const ReadSettingsContext &context) {
AppClass::Instance().dcOptions()->addFromOther(context.dcOptions);
}
bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSettingsContext &context) {
switch (blockId) { switch (blockId) {
case dbiDcOptionOld: { case dbiDcOptionOld: {
quint32 dcId, port; quint32 dcId, port;
@ -838,7 +847,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) {
stream >> dcId >> host >> ip >> port; stream >> dcId >> host >> ip >> port;
if (!_checkStreamStatus(stream)) return false; if (!_checkStreamStatus(stream)) return false;
if (_dcOpts) _dcOpts->insert(dcId, MTP::DcOption(dcId, 0, ip.toUtf8().constData(), port)); context.dcOptions.constructAddOne(dcId, 0, ip.toStdString(), port);
} break; } break;
case dbiDcOption: { case dbiDcOption: {
@ -848,7 +857,15 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) {
stream >> dcIdWithShift >> flags >> ip >> port; stream >> dcIdWithShift >> flags >> ip >> port;
if (!_checkStreamStatus(stream)) return false; if (!_checkStreamStatus(stream)) return false;
if (_dcOpts) _dcOpts->insert(dcIdWithShift, MTP::DcOption(MTP::bareDcId(dcIdWithShift), MTPDdcOption::Flags(flags), ip.toUtf8().constData(), port)); context.dcOptions.constructAddOne(dcIdWithShift, MTPDdcOption::Flags(flags), ip.toStdString(), port);
} break;
case dbiDcOptions: {
QByteArray serialized;
stream >> serialized;
if (!_checkStreamStatus(stream)) return false;
context.dcOptions.constructFromSerialized(serialized);
} break; } break;
case dbiChatSizeMax: { case dbiChatSizeMax: {
@ -1445,7 +1462,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) {
return true; return true;
} }
bool _readOldSettings(bool remove = true) { bool _readOldSettings(bool remove, ReadSettingsContext &context) {
bool result = false; bool result = false;
QFile file(cWorkingDir() + qsl("tdata/config")); QFile file(cWorkingDir() + qsl("tdata/config"));
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
@ -1464,7 +1481,7 @@ bool _readOldSettings(bool remove = true) {
if (!_checkStreamStatus(stream)) break; if (!_checkStreamStatus(stream)) break;
if (version > AppVersion) break; if (version > AppVersion) break;
} else if (!_readSetting(blockId, stream, version)) { } else if (!_readSetting(blockId, stream, version, context)) {
break; break;
} }
} }
@ -1475,7 +1492,7 @@ bool _readOldSettings(bool remove = true) {
return result; return result;
} }
void _readOldUserSettingsFields(QIODevice *device, qint32 &version) { void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettingsContext &context) {
QDataStream stream(device); QDataStream stream(device);
stream.setVersion(QDataStream::Qt_5_1); stream.setVersion(QDataStream::Qt_5_1);
@ -1531,32 +1548,20 @@ void _readOldUserSettingsFields(QIODevice *device, qint32 &version) {
decryptedStream.seek(4); // skip size decryptedStream.seek(4); // skip size
LOG(("App Info: reading encrypted old user config...")); LOG(("App Info: reading encrypted old user config..."));
_readOldUserSettingsFields(&decryptedStream, version); _readOldUserSettingsFields(&decryptedStream, version, context);
} else if (!_readSetting(blockId, stream, version)) { } else if (!_readSetting(blockId, stream, version, context)) {
return; return;
} }
} }
} }
bool _readOldUserSettings(bool remove = true) { bool _readOldUserSettings(bool remove, ReadSettingsContext &context) {
bool result = false; bool result = false;
QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString()) + qsl("_config")); QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString()) + qsl("_config"));
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
LOG(("App Info: reading old user config...")); LOG(("App Info: reading old user config..."));
qint32 version = 0; qint32 version = 0;
_readOldUserSettingsFields(&file, version, context);
MTP::DcOptions dcOpts;
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = Global::DcOptions();
}
_dcOpts = &dcOpts;
_readOldUserSettingsFields(&file, version);
{
QWriteLocker lock(MTP::dcOptionsMutex());
Global::SetDcOptions(dcOpts);
}
file.close(); file.close();
result = true; result = true;
} }
@ -1564,7 +1569,7 @@ bool _readOldUserSettings(bool remove = true) {
return result; return result;
} }
void _readOldMtpDataFields(QIODevice *device, qint32 &version) { void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsContext &context) {
QDataStream stream(device); QDataStream stream(device);
stream.setVersion(QDataStream::Qt_5_1); stream.setVersion(QDataStream::Qt_5_1);
@ -1618,32 +1623,20 @@ void _readOldMtpDataFields(QIODevice *device, qint32 &version) {
decryptedStream.seek(4); // skip size decryptedStream.seek(4); // skip size
LOG(("App Info: reading encrypted old keys...")); LOG(("App Info: reading encrypted old keys..."));
_readOldMtpDataFields(&decryptedStream, version); _readOldMtpDataFields(&decryptedStream, version, context);
} else if (!_readSetting(blockId, stream, version)) { } else if (!_readSetting(blockId, stream, version, context)) {
return; return;
} }
} }
} }
bool _readOldMtpData(bool remove = true) { bool _readOldMtpData(bool remove, ReadSettingsContext &context) {
bool result = false; bool result = false;
QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString())); QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString()));
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
LOG(("App Info: reading old keys...")); LOG(("App Info: reading old keys..."));
qint32 version = 0; qint32 version = 0;
_readOldMtpDataFields(&file, version, context);
MTP::DcOptions dcOpts;
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = Global::DcOptions();
}
_dcOpts = &dcOpts;
_readOldMtpDataFields(&file, version);
{
QWriteLocker lock(MTP::dcOptionsMutex());
Global::SetDcOptions(dcOpts);
}
file.close(); file.close();
result = true; result = true;
} }
@ -1739,10 +1732,14 @@ void _writeUserSettings() {
} }
void _readUserSettings() { void _readUserSettings() {
ReadSettingsContext context;
FileReadDescriptor userSettings; FileReadDescriptor userSettings;
if (!readEncryptedFile(userSettings, _userSettingsKey)) { if (!readEncryptedFile(userSettings, _userSettingsKey)) {
LOG(("App Info: could not read encrypted user settings...")); LOG(("App Info: could not read encrypted user settings..."));
_readOldUserSettings();
_readOldUserSettings(true, context);
applyReadContext(context);
return _writeUserSettings(); return _writeUserSettings();
} }
@ -1756,13 +1753,15 @@ void _readUserSettings() {
return _writeUserSettings(); return _writeUserSettings();
} }
if (!_readSetting(blockId, userSettings.stream, userSettings.version)) { if (!_readSetting(blockId, userSettings.stream, userSettings.version, context)) {
_readingUserSettings = false; _readingUserSettings = false;
return _writeUserSettings(); return _writeUserSettings();
} }
} }
_readingUserSettings = false; _readingUserSettings = false;
LOG(("App Info: encrypted user settings read.")); LOG(("App Info: encrypted user settings read."));
applyReadContext(context);
} }
void _writeMtpData() { void _writeMtpData() {
@ -1788,10 +1787,13 @@ void _writeMtpData() {
} }
void _readMtpData() { void _readMtpData() {
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.created()) {
_readOldMtpData(); _readOldMtpData(true, context);
applyReadContext(context);
_writeMtpData(); _writeMtpData();
} }
return; return;
@ -1805,10 +1807,11 @@ void _readMtpData() {
return _writeMtpData(); return _writeMtpData();
} }
if (!_readSetting(blockId, mtp.stream, mtp.version)) { if (!_readSetting(blockId, mtp.stream, mtp.version, context)) {
return _writeMtpData(); return _writeMtpData();
} }
} }
applyReadContext(context);
} }
ReadMapState _readMap(const QByteArray &pass) { ReadMapState _readMap(const QByteArray &pass) {
@ -2163,11 +2166,14 @@ void start() {
_basePath = cWorkingDir() + qsl("tdata/"); _basePath = cWorkingDir() + qsl("tdata/");
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
ReadSettingsContext context;
FileReadDescriptor settingsData; FileReadDescriptor settingsData;
if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe)) { if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe)) {
_readOldSettings(); _readOldSettings(true, context);
_readOldUserSettings(false); // needed further in _readUserSettings _readOldUserSettings(false, context); // needed further in _readUserSettings
_readOldMtpData(false); // needed further in _readMtpData _readOldMtpData(false, context); // needed further in _readMtpData
applyReadContext(context);
return writeSettings(); return writeSettings();
} }
LOG(("App Info: reading settings...")); LOG(("App Info: reading settings..."));
@ -2189,12 +2195,7 @@ void start() {
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();
} }
MTP::DcOptions dcOpts;
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = Global::DcOptions();
}
_dcOpts = &dcOpts;
LOG(("App Info: reading encrypted settings...")); LOG(("App Info: reading encrypted settings..."));
while (!settings.stream.atEnd()) { while (!settings.stream.atEnd()) {
quint32 blockId; quint32 blockId;
@ -2203,36 +2204,17 @@ void start() {
return writeSettings(); return writeSettings();
} }
if (!_readSetting(blockId, settings.stream, settingsData.version)) { if (!_readSetting(blockId, settings.stream, settingsData.version, context)) {
return writeSettings(); return writeSettings();
} }
} }
if (dcOpts.isEmpty()) {
const BuiltInDc *bdcs = builtInDcs();
for (int i = 0, l = builtInDcsCount(); i < l; ++i) {
MTPDdcOption::Flags flags = 0;
MTP::ShiftedDcId idWithShift = MTP::shiftDcId(bdcs[i].id, flags);
dcOpts.insert(idWithShift, MTP::DcOption(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));
}
const BuiltInDc *bdcsipv6 = builtInDcsIPv6();
for (int i = 0, l = builtInDcsCountIPv6(); i < l; ++i) {
MTPDdcOption::Flags flags = MTPDdcOption::Flag::f_ipv6;
MTP::ShiftedDcId idWithShift = MTP::shiftDcId(bdcsipv6[i].id, flags);
dcOpts.insert(idWithShift, MTP::DcOption(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));
}
}
{
QWriteLocker lock(MTP::dcOptionsMutex());
Global::SetDcOptions(dcOpts);
}
_oldSettingsVersion = settingsData.version; _oldSettingsVersion = settingsData.version;
_settingsSalt = salt; _settingsSalt = salt;
readTheme(); readTheme();
applyReadContext(context);
} }
void writeSettings() { void writeSettings() {
@ -2251,37 +2233,10 @@ void writeSettings() {
} }
settings.writeData(_settingsSalt); settings.writeData(_settingsSalt);
MTP::DcOptions dcOpts; auto dcOptionsSerialized = AppClass::Instance().dcOptions()->serialize();
{
QReadLocker lock(MTP::dcOptionsMutex());
dcOpts = Global::DcOptions();
}
if (dcOpts.isEmpty()) {
const BuiltInDc *bdcs = builtInDcs();
for (int i = 0, l = builtInDcsCount(); i < l; ++i) {
MTPDdcOption::Flags flags = 0;
MTP::ShiftedDcId idWithShift = MTP::shiftDcId(bdcs[i].id, flags);
dcOpts.insert(idWithShift, MTP::DcOption(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));
}
const BuiltInDc *bdcsipv6 = builtInDcsIPv6();
for (int i = 0, l = builtInDcsCountIPv6(); i < l; ++i) {
MTPDdcOption::Flags flags = MTPDdcOption::Flag::f_ipv6;
MTP::ShiftedDcId idWithShift = MTP::shiftDcId(bdcsipv6[i].id, flags);
dcOpts.insert(idWithShift, MTP::DcOption(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));
}
QWriteLocker lock(MTP::dcOptionsMutex());
Global::SetDcOptions(dcOpts);
}
quint32 size = 12 * (sizeof(quint32) + sizeof(qint32)); quint32 size = 12 * (sizeof(quint32) + sizeof(qint32));
for (auto i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { size += sizeof(quint32) + Serialize::bytearraySize(dcOptionsSerialized);
size += sizeof(quint32) + sizeof(quint32) + sizeof(quint32);
size += sizeof(quint32) + Serialize::stringSize(QString::fromUtf8(i->ip.data(), i->ip.size()));
}
size += sizeof(quint32) + Serialize::stringSize(cLangFile()); size += sizeof(quint32) + Serialize::stringSize(cLangFile());
size += sizeof(quint32) + sizeof(qint32); size += sizeof(quint32) + sizeof(qint32);
@ -2308,11 +2263,7 @@ void writeSettings() {
data.stream << quint32(dbiLastUpdateCheck) << qint32(cLastUpdateCheck()); data.stream << quint32(dbiLastUpdateCheck) << qint32(cLastUpdateCheck());
data.stream << quint32(dbiScale) << qint32(cConfigScale()); data.stream << quint32(dbiScale) << qint32(cConfigScale());
data.stream << quint32(dbiLang) << qint32(cLang()); data.stream << quint32(dbiLang) << qint32(cLang());
for (auto i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { data.stream << quint32(dbiDcOptions) << dcOptionsSerialized;
data.stream << quint32(dbiDcOption) << quint32(i.key());
data.stream << qint32(i->flags) << QString::fromUtf8(i->ip.data(), i->ip.size());
data.stream << quint32(i->port);
}
data.stream << quint32(dbiLangFile) << cLangFile(); data.stream << quint32(dbiLangFile) << cLangFile();
data.stream << quint32(dbiConnectionType) << qint32(Global::ConnectionType()); data.stream << quint32(dbiConnectionType) << qint32(Global::ConnectionType());

View file

@ -60,6 +60,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "window/player_wrap_widget.h" #include "window/player_wrap_widget.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "mtproto/dc_options.h"
StackItemSection::StackItemSection(std::unique_ptr<Window::SectionMemento> &&memento) : StackItem(nullptr) StackItemSection::StackItemSection(std::unique_ptr<Window::SectionMemento> &&memento) : StackItem(nullptr)
, _memento(std::move(memento)) { , _memento(std::move(memento)) {
@ -4907,7 +4908,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
case mtpc_updateDcOptions: { case mtpc_updateDcOptions: {
auto &d = update.c_updateDcOptions(); auto &d = update.c_updateDcOptions();
MTP::updateDcOptions(d.vdc_options.c_vector().v); AppClass::Instance().dcOptions()->addFromList(d.vdc_options);
} break; } break;
case mtpc_updateUserPhone: { case mtpc_updateUserPhone: {

View file

@ -32,6 +32,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang.h" #include "lang.h"
#include "mtproto/rsa_public_key.h" #include "mtproto/rsa_public_key.h"
#include "application.h"
#include "mtproto/dc_options.h"
#include "mtproto/connection_abstract.h"
using std::string; using std::string;
@ -454,14 +457,7 @@ ConnectionPrivate::ConnectionPrivate(QThread *thread, Connection *owner, Session
moveToThread(thread); moveToThread(thread);
if (!dc) { if (!dc) {
QReadLocker lock(dcOptionsMutex()); dc = AppClass::Instance().dcOptions()->getDefaultDcId();
const auto &options(Global::DcOptions());
if (options.isEmpty()) {
LOG(("MTP Error: connect failed, no DCs"));
dc = 0;
return;
}
dc = options.cbegin().value().id;
DEBUG_LOG(("MTP Info: searching for any DC, %1 selected...").arg(dc)); DEBUG_LOG(("MTP Info: searching for any DC, %1 selected...").arg(dc));
} }
@ -1082,71 +1078,24 @@ void ConnectionPrivate::socketStart(bool afterConfig) {
DEBUG_LOG(("MTP Error: socketStart() called for finished connection!")); DEBUG_LOG(("MTP Error: socketStart() called for finished connection!"));
return; return;
} }
auto dcType = DcOptions::DcType::Regular;
bool isDldDc = isDldDcId(dc); bool isDldDc = isDldDcId(dc);
if (isDldDc) { // using media_only addresses only if key for this dc is already created if (isDldDcId(dc)) { // using media_only addresses only if key for this dc is already created
QReadLocker lockFinished(&sessionDataMutex); QReadLocker lockFinished(&sessionDataMutex);
if (sessionData) { if (!sessionData || sessionData->getKey()) {
if (!sessionData->getKey()) { dcType = DcOptions::DcType::MediaDownload;
isDldDc = false;
} }
} }
auto bareDc = bareDcId(dc);
} using Variants = DcOptions::Variants;
int32 bareDc = bareDcId(dc); auto kIPv4 = Variants::IPv4;
auto kIPv6 = Variants::IPv6;
static const int IPv4address = 0, IPv6address = 1; auto kTcp = Variants::Tcp;
static const int TcpProtocol = 0, HttpProtocol = 1; auto kHttp = Variants::Http;
MTPDdcOption::Flags flags[2][2] = { { 0 } }; auto variants = AppClass::Instance().dcOptions()->lookup(bareDcId(dc), dcType);
string ip[2][2]; auto noIPv4 = (variants.data[kIPv4][kHttp].port == 0);
uint32 port[2][2] = { { 0 } }; auto noIPv6 = (!Global::TryIPv6() || (variants.data[kIPv6][kHttp].port == 0));
{
QReadLocker lock(dcOptionsMutex());
const auto &options(Global::DcOptions());
int32 shifts[2][2][4] = {
{ // IPv4
{ // TCP IPv4
isDldDc ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only) : -1,
qFlags(MTPDdcOption::Flag::f_tcpo_only),
isDldDc ? qFlags(MTPDdcOption::Flag::f_media_only) : -1,
0
}, { // HTTP IPv4
-1,
-1,
isDldDc ? qFlags(MTPDdcOption::Flag::f_media_only) : -1,
0
},
}, { // IPv6
{ // TCP IPv6
isDldDc ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6) : -1,
MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6,
isDldDc ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1,
qFlags(MTPDdcOption::Flag::f_ipv6)
}, { // HTTP IPv6
-1,
-1,
isDldDc ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1,
qFlags(MTPDdcOption::Flag::f_ipv6)
},
},
};
for (int32 address = 0, acount = sizeof(shifts) / sizeof(shifts[0]); address < acount; ++address) {
for (int32 protocol = 0, pcount = sizeof(shifts[0]) / sizeof(shifts[0][0]); protocol < pcount; ++protocol) {
for (int32 shift = 0, scount = sizeof(shifts[0][0]) / sizeof(shifts[0][0][0]); shift < scount; ++shift) {
int32 mask = shifts[address][protocol][shift];
if (mask < 0) continue;
auto index = options.constFind(shiftDcId(bareDc, mask));
if (index != options.cend()) {
ip[address][protocol] = index->ip;
flags[address][protocol] = index->flags;
port[address][protocol] = index->port;
break;
}
}
}
}
}
bool noIPv4 = !port[IPv4address][HttpProtocol], noIPv6 = (!Global::TryIPv6() || !port[IPv6address][HttpProtocol]);
if (noIPv4 && noIPv6) { if (noIPv4 && noIPv6) {
if (afterConfig) { if (afterConfig) {
if (noIPv4) LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found right after config load!").arg(dc)); if (noIPv4) LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found right after config load!").arg(dc));
@ -1170,21 +1119,21 @@ void ConnectionPrivate::socketStart(bool afterConfig) {
_pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0; _pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0;
_pingSender.stop(); _pingSender.stop();
if (!noIPv4) DEBUG_LOG(("MTP Info: creating IPv4 connection to %1:%2 (tcp) and %3:%4 (http)...").arg(ip[IPv4address][TcpProtocol].c_str()).arg(port[IPv4address][TcpProtocol]).arg(ip[IPv4address][HttpProtocol].c_str()).arg(port[IPv4address][HttpProtocol])); if (!noIPv4) DEBUG_LOG(("MTP Info: creating IPv4 connection to %1:%2 (tcp) and %3:%4 (http)...").arg(variants.data[kIPv4][kTcp].ip.c_str()).arg(variants.data[kIPv4][kTcp].port).arg(variants.data[kIPv4][kHttp].ip.c_str()).arg(variants.data[kIPv4][kHttp].port));
if (!noIPv6) DEBUG_LOG(("MTP Info: creating IPv6 connection to [%1]:%2 (tcp) and [%3]:%4 (http)...").arg(ip[IPv6address][TcpProtocol].c_str()).arg(port[IPv6address][TcpProtocol]).arg(ip[IPv4address][HttpProtocol].c_str()).arg(port[IPv4address][HttpProtocol])); if (!noIPv6) DEBUG_LOG(("MTP Info: creating IPv6 connection to [%1]:%2 (tcp) and [%3]:%4 (http)...").arg(variants.data[kIPv6][kTcp].ip.c_str()).arg(variants.data[kIPv6][kTcp].port).arg(variants.data[kIPv4][kHttp].ip.c_str()).arg(variants.data[kIPv4][kHttp].port));
_waitForConnectedTimer.start(_waitForConnected); _waitForConnectedTimer.start(_waitForConnected);
if (auto conn = _conn4) { if (auto conn = _conn4) {
connect(conn, SIGNAL(connected()), this, SLOT(onConnected4())); connect(conn, SIGNAL(connected()), this, SLOT(onConnected4()));
connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected4())); connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected4()));
conn->connectTcp(ip[IPv4address][TcpProtocol].c_str(), port[IPv4address][TcpProtocol], flags[IPv4address][TcpProtocol]); conn->connectTcp(variants.data[kIPv4][kTcp]);
conn->connectHttp(ip[IPv4address][HttpProtocol].c_str(), port[IPv4address][HttpProtocol], flags[IPv4address][HttpProtocol]); conn->connectHttp(variants.data[kIPv4][kHttp]);
} }
if (auto conn = _conn6) { if (auto conn = _conn6) {
connect(conn, SIGNAL(connected()), this, SLOT(onConnected6())); connect(conn, SIGNAL(connected()), this, SLOT(onConnected6()));
connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected6())); connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected6()));
conn->connectTcp(ip[IPv6address][TcpProtocol].c_str(), port[IPv6address][TcpProtocol], flags[IPv6address][TcpProtocol]); conn->connectTcp(variants.data[kIPv6][kTcp]);
conn->connectHttp(ip[IPv6address][HttpProtocol].c_str(), port[IPv6address][HttpProtocol], flags[IPv6address][HttpProtocol]); conn->connectHttp(variants.data[kIPv6][kHttp]);
} }
} }

View file

@ -22,12 +22,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mtproto/core_types.h" #include "mtproto/core_types.h"
#include "mtproto/auth_key.h" #include "mtproto/auth_key.h"
#include "mtproto/connection_abstract.h"
#include "core/single_timer.h" #include "core/single_timer.h"
namespace MTP { namespace MTP {
namespace internal { namespace internal {
class AbstractConnection;
class ConnectionPrivate; class ConnectionPrivate;
class SessionData; class SessionData;

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include "mtproto/core_types.h" #include "mtproto/core_types.h"
#include "mtproto/dc_options.h"
namespace MTP { namespace MTP {
namespace internal { namespace internal {
@ -29,7 +30,6 @@ class AbstractConnection : public QObject {
Q_OBJECT Q_OBJECT
public: public:
AbstractConnection(QThread *thread) : _sentEncrypted(false) { AbstractConnection(QThread *thread) : _sentEncrypted(false) {
moveToThread(thread); moveToThread(thread);
} }
@ -46,8 +46,8 @@ public:
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32 virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
virtual void disconnectFromServer() = 0; virtual void disconnectFromServer() = 0;
virtual void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) = 0; virtual void connectTcp(const DcOptions::Endpoint &endpoint) = 0;
virtual void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) = 0; virtual void connectHttp(const DcOptions::Endpoint &endpoint) = 0;
virtual bool isConnected() const = 0; virtual bool isConnected() const = 0;
virtual bool usingHttpWait() { virtual bool usingHttpWait() {
return false; return false;

View file

@ -162,24 +162,25 @@ void AutoConnection::disconnectFromServer() {
httpStartTimer.stop(); httpStartTimer.stop();
} }
void AutoConnection::connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { void AutoConnection::connectTcp(const DcOptions::Endpoint &endpoint) {
_addrTcp = addr; _addrTcp = QString::fromStdString(endpoint.ip);
_portTcp = port; _portTcp = endpoint.port;
_flagsTcp = flags; _flagsTcp = endpoint.flags;
connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead())); connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead()));
sock.connectToHost(QHostAddress(_addrTcp), _portTcp); sock.connectToHost(QHostAddress(_addrTcp), _portTcp);
} }
void AutoConnection::connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { void AutoConnection::connectHttp(const DcOptions::Endpoint &endpoint) {
address = QUrl(((flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));//not p - always 80 port for http transport _addrHttp = QString::fromStdString(endpoint.ip);
_portHttp = endpoint.port;
_flagsHttp = endpoint.flags;
// not endpoint.port - always 80 port for http transport
address = QUrl(((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(_addrHttp).arg(80));
TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString())); TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
_addrHttp = addr;
_portHttp = port;
_flagsHttp = flags;
mtpBuffer buffer(preparePQFake(httpNonce)); mtpBuffer buffer(preparePQFake(httpNonce));
DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));

View file

@ -35,8 +35,8 @@ public:
void sendData(mtpBuffer &buffer) override; void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override; void disconnectFromServer() override;
void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; void connectTcp(const DcOptions::Endpoint &endpoint) override;
void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; void connectHttp(const DcOptions::Endpoint &endpoint) override;
bool isConnected() const override; bool isConnected() const override;
bool usingHttpWait() override; bool usingHttpWait() override;
bool needHttpWait() override; bool needHttpWait() override;

View file

@ -136,16 +136,18 @@ void HTTPConnection::disconnectFromServer() {
address = QUrl(); address = QUrl();
} }
void HTTPConnection::connectHttp(const QString &addr, int32 p, MTPDdcOption::Flags flags) { void HTTPConnection::connectHttp(const DcOptions::Endpoint &endpoint) {
address = QUrl(((flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));//not p - always 80 port for http transport _flags = endpoint.flags;
auto addr = QString::fromStdString(endpoint.ip);
// not endpoint.port - always 80 port for http transport
address = QUrl(((_flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));
TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString())); TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
_flags = flags;
mtpBuffer buffer(preparePQFake(httpNonce)); mtpBuffer buffer(preparePQFake(httpNonce));
DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
sendData(buffer); sendData(buffer);
} }

View file

@ -34,9 +34,9 @@ public:
void sendData(mtpBuffer &buffer) override; void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override; void disconnectFromServer() override;
void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override { // not supported void connectTcp(const DcOptions::Endpoint &endpoint) override { // not supported
} }
void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; void connectHttp(const DcOptions::Endpoint &endpoint) override;
bool isConnected() const override; bool isConnected() const override;
bool usingHttpWait() override; bool usingHttpWait() override;
bool needHttpWait() override; bool needHttpWait() override;

View file

@ -339,10 +339,10 @@ void TCPConnection::disconnectFromServer() {
sock.close(); sock.close();
} }
void TCPConnection::connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { void TCPConnection::connectTcp(const DcOptions::Endpoint &endpoint) {
_addr = addr; _addr = QString::fromStdString(endpoint.ip);
_port = port; _port = endpoint.port;
_flags = flags; _flags = endpoint.flags;
connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead())); connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead()));
sock.connectToHost(QHostAddress(_addr), _port); sock.connectToHost(QHostAddress(_addr), _port);

View file

@ -75,8 +75,8 @@ public:
void sendData(mtpBuffer &buffer) override; void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override; void disconnectFromServer() override;
void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; void connectTcp(const DcOptions::Endpoint &endpoint) override;
void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override { // not supported void connectHttp(const DcOptions::Endpoint &endpoint) override { // not supported
} }
bool isConnected() const override; bool isConnected() const override;

View file

@ -0,0 +1,306 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "mtproto/dc_options.h"
namespace MTP {
void DcOptions::constructFromBuiltIn() {
QWriteLocker lock(&_mutex);
_data.clear();
auto bdcs = builtInDcs();
for (auto i = 0, l = builtInDcsCount(); i != l; ++i) {
auto flags = MTPDdcOption::Flags(0);
auto idWithShift = MTP::shiftDcId(bdcs[i].id, flags);
_data.insert(std::make_pair(idWithShift, Option(bdcs[i].id, flags, bdcs[i].ip, bdcs[i].port)));
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));
}
auto bdcsipv6 = builtInDcsIPv6();
for (auto i = 0, l = builtInDcsCountIPv6(); i != l; ++i) {
auto flags = MTPDdcOption::Flags(MTPDdcOption::Flag::f_ipv6);
auto idWithShift = MTP::shiftDcId(bdcsipv6[i].id, flags);
_data.insert(std::make_pair(idWithShift, Option(bdcsipv6[i].id, flags, bdcsipv6[i].ip, bdcsipv6[i].port)));
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));
}
}
void DcOptions::processFromList(const QVector<MTPDcOption> &options, bool overwrite) {
if (options.empty()) {
return;
}
auto idsChanged = std::vector<DcId>();
idsChanged.reserve(options.size());
auto shiftedIdsProcessed = std::vector<ShiftedDcId>();
shiftedIdsProcessed.reserve(options.size());
{
QWriteLocker lock(&_mutex);
if (overwrite) {
idsChanged.reserve(_data.size());
}
for (auto &mtpOption : options) {
if (mtpOption.type() != mtpc_dcOption) {
LOG(("Wrong type in DcOptions: %1").arg(mtpOption.type()));
continue;
}
auto &option = mtpOption.c_dcOption();
auto dcId = option.vid.v;
auto flags = option.vflags.v;
auto dcIdWithShift = MTP::shiftDcId(dcId, flags);
if (base::contains(shiftedIdsProcessed, dcIdWithShift)) {
continue;
}
shiftedIdsProcessed.push_back(dcIdWithShift);
auto &ip = option.vip_address.c_string().v;
auto port = option.vport.v;
if (applyOneGuarded(dcId, flags, ip, port)) {
if (!base::contains(idsChanged, dcId)) {
idsChanged.push_back(dcId);
}
}
}
if (overwrite && shiftedIdsProcessed.size() < _data.size()) {
for (auto i = _data.begin(); i != _data.end();) {
if (base::contains(shiftedIdsProcessed, i->first)) {
++i;
} else {
if (!base::contains(idsChanged, i->second.id)) {
idsChanged.push_back(i->second.id);
}
i = _data.erase(i);
}
}
}
}
if (!idsChanged.empty()) {
_changed.notify(std::move(idsChanged));
}
}
void DcOptions::setFromList(const MTPVector<MTPDcOption> &options) {
processFromList(options.c_vector().v, true);
}
void DcOptions::addFromList(const MTPVector<MTPDcOption> &options) {
processFromList(options.c_vector().v, false);
}
void DcOptions::addFromOther(const DcOptions &options) {
if (this == &options) {
return;
}
auto idsChanged = std::vector<DcId>();
{
QReadLocker lock(&options._mutex);
if (options._data.empty()) {
return;
}
idsChanged.reserve(options._data.size());
{
QWriteLocker lock(&_mutex);
for (auto &item : options._data) {
auto dcId = item.second.id;
auto flags = item.second.flags;
auto &ip = item.second.ip;
auto port = item.second.port;
if (applyOneGuarded(dcId, flags, ip, port)) {
if (!base::contains(idsChanged, dcId)) {
idsChanged.push_back(dcId);
}
}
}
}
}
if (!idsChanged.empty()) {
_changed.notify(std::move(idsChanged));
}
}
void DcOptions::constructAddOne(int id, MTPDdcOption::Flags flags, const std::string &ip, int port) {
QWriteLocker lock(&_mutex);
applyOneGuarded(bareDcId(id), flags, ip, port);
}
bool DcOptions::applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std::string &ip, int port) {
auto dcIdWithShift = MTP::shiftDcId(dcId, flags);
auto i = _data.find(dcIdWithShift);
if (i != _data.cend()) {
if (i->second.ip == ip && i->second.port == port) {
return false;
}
i->second.ip = ip;
i->second.port = port;
} else {
_data.insert(std::make_pair(dcIdWithShift, Option(dcId, flags, ip, port)));
}
return true;
}
QByteArray DcOptions::serialize() const {
QReadLocker lock(&_mutex);
auto size = sizeof(qint32);
for (auto &item : _data) {
size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32); // id + flags + port
size += sizeof(qint32) + item.second.ip.size();
}
auto result = QByteArray();
result.reserve(size);
{
QBuffer buffer(&result);
if (!buffer.open(QIODevice::WriteOnly)) {
LOG(("MTP Error: Can't open data for DcOptions::serialize()"));
return result;
}
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_1);
stream << qint32(_data.size());
for (auto &item : _data) {
stream << qint32(item.second.id) << qint32(item.second.flags) << qint32(item.second.port);
stream << qint32(item.second.ip.size());
stream.writeRawData(item.second.ip.data(), item.second.ip.size());
}
}
return result;
}
void DcOptions::constructFromSerialized(const QByteArray &serialized) {
auto readonly = serialized;
QBuffer buffer(&readonly);
if (!buffer.open(QIODevice::ReadOnly)) {
LOG(("MTP Error: Can't open data for DcOptions::setFromSerialized()"));
return;
}
QDataStream stream(&buffer);
qint32 count = 0;
stream >> count;
if (stream.status() != QDataStream::Ok) {
LOG(("MTP Error: Bad data for DcOptions::setFromSerialized()"));
return;
}
QWriteLocker lock(&_mutex);
_data.clear();
for (auto i = 0; i != count; ++i) {
qint32 id = 0, flags = 0, port = 0, ipSize = 0;
stream >> id >> flags >> port >> ipSize;
std::string ip(ipSize, ' ');
stream.readRawData(&ip[0], ipSize);
if (stream.status() != QDataStream::Ok) {
LOG(("MTP Error: Bad data inside DcOptions::setFromSerialized()"));
return;
}
applyOneGuarded(DcId(id), MTPDdcOption::Flags(flags), ip, port);
}
}
DcOptions::Ids DcOptions::sortedDcIds() const {
auto result = Ids();
{
QReadLocker lock(&_mutex);
result.reserve(_data.size());
for (auto &item : _data) {
if (!base::contains(result, item.second.id)) {
result.push_back(item.second.id);
}
}
}
std::sort(result.begin(), result.end());
return result;
}
DcId DcOptions::getDefaultDcId() const {
auto result = sortedDcIds();
t_assert(!result.empty());
auto main = internal::mainDC();
if (base::contains(result, main)) {
return main;
}
return result[0];
}
DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const {
auto isMediaDownload = (type == DcType::MediaDownload);
int shifts[2][2][4] = {
{ // IPv4
{ // TCP IPv4
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only) : -1,
qFlags(MTPDdcOption::Flag::f_tcpo_only),
isMediaDownload ? qFlags(MTPDdcOption::Flag::f_media_only) : -1,
0
}, { // HTTP IPv4
-1,
-1,
isMediaDownload ? qFlags(MTPDdcOption::Flag::f_media_only) : -1,
0
},
}, { // IPv6
{ // TCP IPv6
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6) : -1,
MTPDdcOption::Flag::f_tcpo_only | MTPDdcOption::Flag::f_ipv6,
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1,
qFlags(MTPDdcOption::Flag::f_ipv6)
}, { // HTTP IPv6
-1,
-1,
isMediaDownload ? (MTPDdcOption::Flag::f_media_only | MTPDdcOption::Flag::f_ipv6) : -1,
qFlags(MTPDdcOption::Flag::f_ipv6)
},
},
};
auto result = Variants();
{
QReadLocker lock(&_mutex);
for (auto address = 0; address != Variants::AddressTypeCount; ++address) {
for (auto protocol = 0; protocol != Variants::ProtocolCount; ++protocol) {
for (auto variant = 0; variant != base::array_size(shifts[address][protocol]); ++variant) {
auto shift = shifts[address][protocol][variant];
if (shift < 0) continue;
auto it = _data.find(shiftDcId(dcId, shift));
if (it != _data.cend()) {
result.data[address][protocol].ip = it->second.ip;
result.data[address][protocol].flags = it->second.flags;
result.data[address][protocol].port = it->second.port;
break;
}
}
}
}
}
return result;
}
} // namespace MTP

View file

@ -0,0 +1,94 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "core/observer.h"
#include <string>
#include <vector>
#include <map>
namespace MTP {
class DcOptions {
public:
// construct methods don't notify "changed" subscribers.
void constructFromSerialized(const QByteArray &serialized);
void constructFromBuiltIn();
void constructAddOne(int id, MTPDdcOption::Flags flags, const std::string &ip, int port);
QByteArray serialize() const;
using Ids = std::vector<DcId>;
base::Observable<Ids> &changed() const {
return _changed;
}
void setFromList(const MTPVector<MTPDcOption> &options);
void addFromList(const MTPVector<MTPDcOption> &options);
void addFromOther(const DcOptions &options);
Ids sortedDcIds() const;
DcId getDefaultDcId() const;
struct Endpoint {
std::string ip;
int port = 0;
MTPDdcOption::Flags flags = 0;
};
struct Variants {
enum {
IPv4 = 0,
IPv6 = 1,
AddressTypeCount = 2,
};
enum {
Tcp = 0,
Http = 1,
ProtocolCount = 2,
};
Endpoint data[AddressTypeCount][ProtocolCount];
};
enum class DcType {
Regular,
MediaDownload,
};
Variants lookup(DcId dcId, DcType type) const;
private:
struct Option {
Option(DcId id, MTPDdcOption::Flags flags, const std::string &ip, int port) : id(id), flags(flags), ip(ip), port(port) {
}
DcId id;
MTPDdcOption::Flags flags;
std::string ip;
int port;
};
bool applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std::string &ip, int port);
void processFromList(const QVector<MTPDcOption> &options, bool overwrite);
std::map<int, Option> _data;
mutable QReadWriteLock _mutex;
mutable base::Observable<Ids> _changed;
};
} // namespace MTP

View file

@ -19,16 +19,17 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/ */
#include "stdafx.h" #include "stdafx.h"
#include "mtproto/dcenter.h" #include "mtproto/dcenter.h"
#include "mtproto/facade.h" #include "mtproto/facade.h"
#include "mtproto/dc_options.h"
#include "application.h"
#include "localstorage.h" #include "localstorage.h"
namespace MTP { namespace MTP {
namespace internal { namespace internal {
namespace { namespace {
DcenterMap gDCs; DcenterMap gDCs;
bool configLoadedOnce = false; bool configLoadedOnce = false;
bool mainDCChanged = false; bool mainDCChanged = false;
@ -38,6 +39,9 @@ namespace {
typedef QMap<int32, AuthKeyPtr> _KeysMapForWrite; typedef QMap<int32, AuthKeyPtr> _KeysMapForWrite;
_KeysMapForWrite _keysMapForWrite; _KeysMapForWrite _keysMapForWrite;
QMutex _keysMapForWriteMutex; QMutex _keysMapForWriteMutex;
constexpr auto kEnumerateDcTimeout = 8000; // 8 seconds timeout for help_getConfig to work (then move to other dc)
} // namespace } // namespace
int32 authed() { int32 authed() {
@ -156,7 +160,11 @@ void configLoaded(const MTPConfig &result) {
DEBUG_LOG(("MTP Info: got config, chat_size_max: %1, date: %2, test_mode: %3, this_dc: %4, dc_options.length: %5").arg(data.vchat_size_max.v).arg(data.vdate.v).arg(mtpIsTrue(data.vtest_mode)).arg(data.vthis_dc.v).arg(data.vdc_options.c_vector().v.size())); DEBUG_LOG(("MTP Info: got config, chat_size_max: %1, date: %2, test_mode: %3, this_dc: %4, dc_options.length: %5").arg(data.vchat_size_max.v).arg(data.vdate.v).arg(mtpIsTrue(data.vtest_mode)).arg(data.vthis_dc.v).arg(data.vdc_options.c_vector().v.size()));
updateDcOptions(data.vdc_options.c_vector().v); if (data.vdc_options.c_vector().v.empty()) {
LOG(("MTP Error: config with empty dc_options received!"));
} else {
AppClass::Instance().dcOptions()->setFromList(data.vdc_options);
}
Global::SetChatSizeMax(data.vchat_size_max.v); Global::SetChatSizeMax(data.vchat_size_max.v);
Global::SetMegagroupSizeMax(data.vmegagroup_size_max.v); Global::SetMegagroupSizeMax(data.vmegagroup_size_max.v);
@ -191,47 +199,7 @@ bool configFailed(const RPCError &error) {
}; };
void updateDcOptions(const QVector<MTPDcOption> &options) { ConfigLoader::ConfigLoader() {
QSet<int32> already, restart;
{
MTP::DcOptions opts;
{
QReadLocker lock(dcOptionsMutex());
opts = Global::DcOptions();
}
for (QVector<MTPDcOption>::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) {
const auto &optData(i->c_dcOption());
int32 id = optData.vid.v, idWithShift = MTP::shiftDcId(id, optData.vflags.v);
if (already.constFind(idWithShift) == already.cend()) {
already.insert(idWithShift);
auto a = opts.constFind(idWithShift);
if (a != opts.cend()) {
if (a.value().ip != optData.vip_address.c_string().v || a.value().port != optData.vport.v) {
restart.insert(id);
}
}
opts.insert(idWithShift, MTP::DcOption(id, optData.vflags.v, optData.vip_address.c_string().v, optData.vport.v));
}
}
{
QWriteLocker lock(dcOptionsMutex());
Global::SetDcOptions(opts);
}
}
for (QSet<int32>::const_iterator i = restart.cbegin(), e = restart.cend(); i != e; ++i) {
MTP::restart(*i);
}
}
namespace {
QReadWriteLock _dcOptionsMutex;
}
QReadWriteLock *dcOptionsMutex() {
return &_dcOptionsMutex;
}
ConfigLoader::ConfigLoader() : _enumCurrent(0), _enumRequest(0) {
connect(&_enumDCTimer, SIGNAL(timeout()), this, SLOT(enumDC())); connect(&_enumDCTimer, SIGNAL(timeout()), this, SLOT(enumDC()));
} }
@ -241,7 +209,7 @@ void ConfigLoader::load() {
MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed)); MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed));
_enumDCTimer.start(MTPEnumDCTimeout); _enumDCTimer.start(kEnumerateDcTimeout);
} }
void ConfigLoader::done() { void ConfigLoader::done() {
@ -267,23 +235,18 @@ void ConfigLoader::enumDC() {
} else { } else {
MTP::killSession(MTP::cfgDcId(_enumCurrent)); MTP::killSession(MTP::cfgDcId(_enumCurrent));
} }
OrderedSet<int32> dcs; auto ids = AppClass::Instance().dcOptions()->sortedDcIds();
{ t_assert(!ids.empty());
QReadLocker lock(dcOptionsMutex());
const auto &options(Global::DcOptions()); auto i = std::find(ids.cbegin(), ids.cend(), _enumCurrent);
for (auto i = options.cbegin(), e = options.cend(); i != e; ++i) { if (i == ids.cend() || (++i) == ids.cend()) {
dcs.insert(MTP::bareDcId(i.key())); _enumCurrent = ids.front();
}
}
auto i = dcs.constFind(_enumCurrent);
if (i == dcs.cend() || (++i) == dcs.cend()) {
_enumCurrent = *dcs.cbegin();
} else { } else {
_enumCurrent = *i; _enumCurrent = *i;
} }
_enumRequest = MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed), MTP::cfgDcId(_enumCurrent)); _enumRequest = MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed), MTP::cfgDcId(_enumCurrent));
_enumDCTimer.start(MTPEnumDCTimeout); _enumDCTimer.start(kEnumerateDcTimeout);
} }
ConfigLoader *configLoader() { ConfigLoader *configLoader() {

View file

@ -81,8 +81,8 @@ signals:
private: private:
SingleTimer _enumDCTimer; SingleTimer _enumDCTimer;
int32 _enumCurrent; DcId _enumCurrent = 0;
mtpRequestId _enumRequest; mtpRequestId _enumRequest = 0;
}; };
@ -101,8 +101,5 @@ void authed(int32 uid);
AuthKeysMap getAuthKeys(); AuthKeysMap getAuthKeys();
void setAuthKey(int32 dc, AuthKeyPtr key); void setAuthKey(int32 dc, AuthKeyPtr key);
void updateDcOptions(const QVector<MTPDcOption> &options);
QReadWriteLock *dcOptionsMutex();
} // namespace internal } // namespace internal
} // namespace MTP } // namespace MTP

View file

@ -829,14 +829,13 @@ int32 state(mtpRequestId requestId) {
} }
void finish() { void finish() {
for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) {
i.value()->kill();
delete i.value();
}
sessions.clear();
mainSession = nullptr; mainSession = nullptr;
for (auto session : base::take(sessions)) {
session->kill();
delete session;
}
for_const (internal::Connection *connection, quittingConnections) { for_const (auto connection, quittingConnections) {
connection->waitTillFinish(); connection->waitTillFinish();
delete connection; delete connection;
} }
@ -886,11 +885,6 @@ void clearGlobalHandlers() {
setSessionResetHandler(0); setSessionResetHandler(0);
} }
void updateDcOptions(const QVector<MTPDcOption> &options) {
internal::updateDcOptions(options);
Local::writeSettings();
}
AuthKeysMap getKeys() { AuthKeysMap getKeys() {
return internal::getAuthKeys(); return internal::getAuthKeys();
} }
@ -903,8 +897,4 @@ void setKey(int dc, const AuthKey::Data &key) {
return internal::setAuthKey(dc, std::move(keyPtr)); return internal::setAuthKey(dc, std::move(keyPtr));
} }
QReadWriteLock *dcOptionsMutex() {
return internal::dcOptionsMutex();
}
} // namespace MTP } // namespace MTP

View file

@ -217,24 +217,9 @@ void setStateChangedHandler(MTPStateChangedHandler handler);
void setSessionResetHandler(MTPSessionResetHandler handler); void setSessionResetHandler(MTPSessionResetHandler handler);
void clearGlobalHandlers(); void clearGlobalHandlers();
void updateDcOptions(const QVector<MTPDcOption> &options);
AuthKeysMap getKeys(); AuthKeysMap getKeys();
void setKey(int dc, const AuthKey::Data &key); void setKey(int dc, const AuthKey::Data &key);
QReadWriteLock *dcOptionsMutex();
struct DcOption {
DcOption(int id, MTPDdcOption::Flags flags, const std::string &ip, int port) : id(id), flags(flags), ip(ip), port(port) {
}
int id;
MTPDdcOption::Flags flags;
std::string ip;
int port;
};
typedef QMap<int, DcOption> DcOptions;
namespace internal { namespace internal {
template <typename TRequest> template <typename TRequest>

View file

@ -337,6 +337,8 @@
'<(src_loc)/mtproto/core_types.h', '<(src_loc)/mtproto/core_types.h',
'<(src_loc)/mtproto/dcenter.cpp', '<(src_loc)/mtproto/dcenter.cpp',
'<(src_loc)/mtproto/dcenter.h', '<(src_loc)/mtproto/dcenter.h',
'<(src_loc)/mtproto/dc_options.cpp',
'<(src_loc)/mtproto/dc_options.h',
'<(src_loc)/mtproto/file_download.cpp', '<(src_loc)/mtproto/file_download.cpp',
'<(src_loc)/mtproto/file_download.h', '<(src_loc)/mtproto/file_download.h',
'<(src_loc)/mtproto/rsa_public_key.cpp', '<(src_loc)/mtproto/rsa_public_key.cpp',