From a35947141c300463b2bda43fa850f00f22148954 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 23 Feb 2017 09:57:04 +0300 Subject: [PATCH] Encapsulated DcOptions to an independent class. --- Telegram/SourceFiles/application.cpp | 29 +- Telegram/SourceFiles/application.h | 22 +- Telegram/SourceFiles/config.h | 2 - Telegram/SourceFiles/core/utils.h | 7 + Telegram/SourceFiles/facades.cpp | 4 - Telegram/SourceFiles/facades.h | 2 - Telegram/SourceFiles/localstorage.cpp | 169 ++++------ Telegram/SourceFiles/mainwidget.cpp | 3 +- Telegram/SourceFiles/mtproto/connection.cpp | 99 ++---- Telegram/SourceFiles/mtproto/connection.h | 2 +- .../SourceFiles/mtproto/connection_abstract.h | 6 +- .../SourceFiles/mtproto/connection_auto.cpp | 21 +- .../SourceFiles/mtproto/connection_auto.h | 4 +- .../SourceFiles/mtproto/connection_http.cpp | 12 +- .../SourceFiles/mtproto/connection_http.h | 4 +- .../SourceFiles/mtproto/connection_tcp.cpp | 8 +- Telegram/SourceFiles/mtproto/connection_tcp.h | 4 +- Telegram/SourceFiles/mtproto/dc_options.cpp | 306 ++++++++++++++++++ Telegram/SourceFiles/mtproto/dc_options.h | 94 ++++++ Telegram/SourceFiles/mtproto/dcenter.cpp | 93 ++---- Telegram/SourceFiles/mtproto/dcenter.h | 7 +- Telegram/SourceFiles/mtproto/facade.cpp | 20 +- Telegram/SourceFiles/mtproto/facade.h | 15 - Telegram/gyp/Telegram.gyp | 2 + 24 files changed, 606 insertions(+), 329 deletions(-) create mode 100644 Telegram/SourceFiles/mtproto/dc_options.cpp create mode 100644 Telegram/SourceFiles/mtproto/dc_options.h diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 3c600bba3..8f44917c8 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -39,6 +39,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/notifications_manager.h" #include "history/history_location_manager.h" #include "core/task_queue.h" +#include "mtproto/dc_options.h" namespace { @@ -340,7 +341,10 @@ void Application::closeApplication() { manager->clearAllFast(); } - delete base::take(AppObject); + if (AppObject) { + AppClass::Instance().joinThreads(); + delete base::take(AppObject); + } Sandbox::finish(); @@ -682,7 +686,6 @@ namespace Sandbox { new AppClass(); } - } AppClass::AppClass() : QObject() { @@ -692,7 +695,9 @@ AppClass::AppClass() : QObject() { ThirdParty::start(); Global::start(); - Local::start(); + + startLocalStorage(); + if (Local::oldSettingsVersion() < AppVersion) { psNewVersion(); } @@ -816,6 +821,18 @@ void AppClass::loadLanguage() { application()->installTranslator(_translator = new Translator()); } +void AppClass::startLocalStorage() { + _dcOptions = std::make_unique(); + _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) { photoUpdates.insert(msgId, peer); } @@ -1096,6 +1113,10 @@ void AppClass::checkMapVersion() { } } +void AppClass::joinThreads() { + MTP::finish(); +} + AppClass::~AppClass() { Shortcuts::finish(); @@ -1110,8 +1131,6 @@ AppClass::~AppClass() { App::deinitMedia(); deinitLocationManager(); - MTP::finish(); - AppObject = nullptr; delete base::take(_uploader); delete base::take(_translator); diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h index 4d73fe573..28b8ae213 100644 --- a/Telegram/SourceFiles/application.h +++ b/Telegram/SourceFiles/application.h @@ -23,6 +23,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "pspecific.h" #include "core/single_timer.h" +#include "core/observer.h" + +namespace MTP { +class DcOptions; +} // namespace MTP class UpdateChecker; class Application : public QApplication { @@ -143,17 +148,28 @@ class MainWidget; class FileUploader; class Translator; -class AppClass : public QObject, public RPCSender { +class AppClass : public QObject, public RPCSender, private base::Subscriber { Q_OBJECT public: AppClass(); + + void joinThreads(); ~AppClass(); static AppClass *app(); static MainWindow *wnd(); static MainWidget *main(); + static AppClass &Instance() { + auto result = app(); + t_assert(result != nullptr); + return *result; + } + MTP::DcOptions *dcOptions() { + return _dcOptions.get(); + } + FileUploader *uploader(); void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId); void regPhotoUpdate(const PeerId &peer, const FullMsgId &msgId); @@ -179,7 +195,6 @@ public: void handleAppDeactivated(); signals: - void peerPhotoDone(PeerId peer); void peerPhotoFail(PeerId peer); @@ -202,6 +217,7 @@ public slots: void call_handleObservables(); private: + void startLocalStorage(); void loadLanguage(); QMap photoUpdates; @@ -215,4 +231,6 @@ private: FileUploader *_uploader = nullptr; Translator *_translator = nullptr; + std::unique_ptr _dcOptions; + }; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 8a4ad1e43..30d034e22 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -51,8 +51,6 @@ enum { MTPDownloadSessionsCount = 2, // max 2 download sessions is created 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 MaxUsersPerInvite = 100, // max users in one super group invite request diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 3e63aba87..3e7213199 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "core/basic_types.h" #include +#include namespace base { @@ -82,6 +83,12 @@ scope_guard_helper scope_guard(Lambda on_scope_exit) { return scope_guard_helper(std::move(on_scope_exit)); } +template +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 // using for_const instead of plain range-based for loop to ensure usage of const_iterator diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index e1a5df983..f78473eae 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -671,8 +671,6 @@ struct Data { TimeMs LastFeaturedStickersUpdate = 0; Stickers::Order ArchivedStickerSetsOrder; - MTP::DcOptions DcOptions; - CircleMasksMap CircleMasks; base::Observable SelfChanged; @@ -795,8 +793,6 @@ DefineRefVar(Global, base::Observable, FeaturedStickerSetsUnreadCountChang DefineVar(Global, TimeMs, LastFeaturedStickersUpdate); DefineVar(Global, Stickers::Order, ArchivedStickerSetsOrder); -DefineVar(Global, MTP::DcOptions, DcOptions); - DefineRefVar(Global, CircleMasksMap, CircleMasks); DefineRefVar(Global, base::Observable, SelfChanged); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index af50759d2..6845106a2 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -369,8 +369,6 @@ DeclareRefVar(base::Observable, FeaturedStickerSetsUnreadCountChanged); DeclareVar(TimeMs, LastFeaturedStickersUpdate); DeclareVar(Stickers::Order, ArchivedStickerSetsOrder); -DeclareVar(MTP::DcOptions, DcOptions); - typedef QMap CircleMasksMap; DeclareRefVar(CircleMasksMap, CircleMasks); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index cbcf62a74..31c4c8b71 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "lang.h" #include "media/media_audio.h" #include "ui/widgets/input_fields.h" +#include "mtproto/dc_options.h" #include "application.h" #include "apiwrap.h" @@ -563,6 +564,7 @@ enum { dbiTheme = 0x47, dbiDialogsWidthRatio = 0x48, dbiUseExternalVideoPlayer = 0x49, + dbiDcOptions = 0x4a, dbiEncryptedWithSalt = 333, dbiEncrypted = 444, @@ -829,8 +831,15 @@ void _readReportSpamStatuses() { } } -MTP::DcOptions *_dcOpts = 0; -bool _readSetting(quint32 blockId, QDataStream &stream, int version) { +struct ReadSettingsContext { + 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) { case dbiDcOptionOld: { quint32 dcId, port; @@ -838,7 +847,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { stream >> dcId >> host >> ip >> port; 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; case dbiDcOption: { @@ -848,7 +857,15 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { stream >> dcIdWithShift >> flags >> ip >> port; 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; case dbiChatSizeMax: { @@ -1445,7 +1462,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) { return true; } -bool _readOldSettings(bool remove = true) { +bool _readOldSettings(bool remove, ReadSettingsContext &context) { bool result = false; QFile file(cWorkingDir() + qsl("tdata/config")); if (file.open(QIODevice::ReadOnly)) { @@ -1464,7 +1481,7 @@ bool _readOldSettings(bool remove = true) { if (!_checkStreamStatus(stream)) break; if (version > AppVersion) break; - } else if (!_readSetting(blockId, stream, version)) { + } else if (!_readSetting(blockId, stream, version, context)) { break; } } @@ -1475,7 +1492,7 @@ bool _readOldSettings(bool remove = true) { return result; } -void _readOldUserSettingsFields(QIODevice *device, qint32 &version) { +void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettingsContext &context) { QDataStream stream(device); stream.setVersion(QDataStream::Qt_5_1); @@ -1531,32 +1548,20 @@ void _readOldUserSettingsFields(QIODevice *device, qint32 &version) { decryptedStream.seek(4); // skip size LOG(("App Info: reading encrypted old user config...")); - _readOldUserSettingsFields(&decryptedStream, version); - } else if (!_readSetting(blockId, stream, version)) { + _readOldUserSettingsFields(&decryptedStream, version, context); + } else if (!_readSetting(blockId, stream, version, context)) { return; } } } -bool _readOldUserSettings(bool remove = true) { +bool _readOldUserSettings(bool remove, ReadSettingsContext &context) { bool result = false; QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString()) + qsl("_config")); if (file.open(QIODevice::ReadOnly)) { LOG(("App Info: reading old user config...")); qint32 version = 0; - - MTP::DcOptions dcOpts; - { - QReadLocker lock(MTP::dcOptionsMutex()); - dcOpts = Global::DcOptions(); - } - _dcOpts = &dcOpts; - _readOldUserSettingsFields(&file, version); - { - QWriteLocker lock(MTP::dcOptionsMutex()); - Global::SetDcOptions(dcOpts); - } - + _readOldUserSettingsFields(&file, version, context); file.close(); result = true; } @@ -1564,7 +1569,7 @@ bool _readOldUserSettings(bool remove = true) { return result; } -void _readOldMtpDataFields(QIODevice *device, qint32 &version) { +void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsContext &context) { QDataStream stream(device); stream.setVersion(QDataStream::Qt_5_1); @@ -1618,32 +1623,20 @@ void _readOldMtpDataFields(QIODevice *device, qint32 &version) { decryptedStream.seek(4); // skip size LOG(("App Info: reading encrypted old keys...")); - _readOldMtpDataFields(&decryptedStream, version); - } else if (!_readSetting(blockId, stream, version)) { + _readOldMtpDataFields(&decryptedStream, version, context); + } else if (!_readSetting(blockId, stream, version, context)) { return; } } } -bool _readOldMtpData(bool remove = true) { +bool _readOldMtpData(bool remove, ReadSettingsContext &context) { bool result = false; QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString())); if (file.open(QIODevice::ReadOnly)) { LOG(("App Info: reading old keys...")); qint32 version = 0; - - MTP::DcOptions dcOpts; - { - QReadLocker lock(MTP::dcOptionsMutex()); - dcOpts = Global::DcOptions(); - } - _dcOpts = &dcOpts; - _readOldMtpDataFields(&file, version); - { - QWriteLocker lock(MTP::dcOptionsMutex()); - Global::SetDcOptions(dcOpts); - } - + _readOldMtpDataFields(&file, version, context); file.close(); result = true; } @@ -1739,10 +1732,14 @@ void _writeUserSettings() { } void _readUserSettings() { + ReadSettingsContext context; FileReadDescriptor userSettings; if (!readEncryptedFile(userSettings, _userSettingsKey)) { LOG(("App Info: could not read encrypted user settings...")); - _readOldUserSettings(); + + _readOldUserSettings(true, context); + applyReadContext(context); + return _writeUserSettings(); } @@ -1756,13 +1753,15 @@ void _readUserSettings() { return _writeUserSettings(); } - if (!_readSetting(blockId, userSettings.stream, userSettings.version)) { + if (!_readSetting(blockId, userSettings.stream, userSettings.version, context)) { _readingUserSettings = false; return _writeUserSettings(); } } _readingUserSettings = false; LOG(("App Info: encrypted user settings read.")); + + applyReadContext(context); } void _writeMtpData() { @@ -1788,10 +1787,13 @@ void _writeMtpData() { } void _readMtpData() { + ReadSettingsContext context; FileReadDescriptor mtp; if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), FileOption::Safe)) { if (_localKey.created()) { - _readOldMtpData(); + _readOldMtpData(true, context); + applyReadContext(context); + _writeMtpData(); } return; @@ -1805,10 +1807,11 @@ void _readMtpData() { return _writeMtpData(); } - if (!_readSetting(blockId, mtp.stream, mtp.version)) { + if (!_readSetting(blockId, mtp.stream, mtp.version, context)) { return _writeMtpData(); } } + applyReadContext(context); } ReadMapState _readMap(const QByteArray &pass) { @@ -2163,11 +2166,14 @@ void start() { _basePath = cWorkingDir() + qsl("tdata/"); if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); + ReadSettingsContext context; FileReadDescriptor settingsData; if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe)) { - _readOldSettings(); - _readOldUserSettings(false); // needed further in _readUserSettings - _readOldMtpData(false); // needed further in _readMtpData + _readOldSettings(true, context); + _readOldUserSettings(false, context); // needed further in _readUserSettings + _readOldMtpData(false, context); // needed further in _readMtpData + applyReadContext(context); + return writeSettings(); } LOG(("App Info: reading settings...")); @@ -2189,12 +2195,7 @@ void start() { LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode...")); return writeSettings(); } - MTP::DcOptions dcOpts; - { - QReadLocker lock(MTP::dcOptionsMutex()); - dcOpts = Global::DcOptions(); - } - _dcOpts = &dcOpts; + LOG(("App Info: reading encrypted settings...")); while (!settings.stream.atEnd()) { quint32 blockId; @@ -2203,36 +2204,17 @@ void start() { return writeSettings(); } - if (!_readSetting(blockId, settings.stream, settingsData.version)) { + if (!_readSetting(blockId, settings.stream, settingsData.version, context)) { 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; _settingsSalt = salt; readTheme(); + + applyReadContext(context); } void writeSettings() { @@ -2251,37 +2233,10 @@ void writeSettings() { } settings.writeData(_settingsSalt); - MTP::DcOptions dcOpts; - { - 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); - } + auto dcOptionsSerialized = AppClass::Instance().dcOptions()->serialize(); quint32 size = 12 * (sizeof(quint32) + sizeof(qint32)); - for (auto i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { - size += sizeof(quint32) + sizeof(quint32) + sizeof(quint32); - size += sizeof(quint32) + Serialize::stringSize(QString::fromUtf8(i->ip.data(), i->ip.size())); - } + size += sizeof(quint32) + Serialize::bytearraySize(dcOptionsSerialized); size += sizeof(quint32) + Serialize::stringSize(cLangFile()); size += sizeof(quint32) + sizeof(qint32); @@ -2308,11 +2263,7 @@ void writeSettings() { data.stream << quint32(dbiLastUpdateCheck) << qint32(cLastUpdateCheck()); data.stream << quint32(dbiScale) << qint32(cConfigScale()); data.stream << quint32(dbiLang) << qint32(cLang()); - for (auto i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { - 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(dbiDcOptions) << dcOptionsSerialized; data.stream << quint32(dbiLangFile) << cLangFile(); data.stream << quint32(dbiConnectionType) << qint32(Global::ConnectionType()); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index cad11e8dd..7b1fa9158 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -60,6 +60,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/themes/window_theme.h" #include "window/player_wrap_widget.h" #include "styles/style_boxes.h" +#include "mtproto/dc_options.h" StackItemSection::StackItemSection(std::unique_ptr &&memento) : StackItem(nullptr) , _memento(std::move(memento)) { @@ -4907,7 +4908,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updateDcOptions: { auto &d = update.c_updateDcOptions(); - MTP::updateDcOptions(d.vdc_options.c_vector().v); + AppClass::Instance().dcOptions()->addFromList(d.vdc_options); } break; case mtpc_updateUserPhone: { diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index cf67febb1..d57dba8d8 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -32,6 +32,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "lang.h" #include "mtproto/rsa_public_key.h" +#include "application.h" +#include "mtproto/dc_options.h" +#include "mtproto/connection_abstract.h" using std::string; @@ -454,14 +457,7 @@ ConnectionPrivate::ConnectionPrivate(QThread *thread, Connection *owner, Session moveToThread(thread); if (!dc) { - QReadLocker lock(dcOptionsMutex()); - const auto &options(Global::DcOptions()); - if (options.isEmpty()) { - LOG(("MTP Error: connect failed, no DCs")); - dc = 0; - return; - } - dc = options.cbegin().value().id; + dc = AppClass::Instance().dcOptions()->getDefaultDcId(); 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!")); return; } + auto dcType = DcOptions::DcType::Regular; 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); - if (sessionData) { - if (!sessionData->getKey()) { - isDldDc = false; - } - } - - } - int32 bareDc = bareDcId(dc); - - static const int IPv4address = 0, IPv6address = 1; - static const int TcpProtocol = 0, HttpProtocol = 1; - MTPDdcOption::Flags flags[2][2] = { { 0 } }; - string ip[2][2]; - uint32 port[2][2] = { { 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; - } - } - } + if (!sessionData || sessionData->getKey()) { + dcType = DcOptions::DcType::MediaDownload; } } - bool noIPv4 = !port[IPv4address][HttpProtocol], noIPv6 = (!Global::TryIPv6() || !port[IPv6address][HttpProtocol]); + auto bareDc = bareDcId(dc); + + using Variants = DcOptions::Variants; + auto kIPv4 = Variants::IPv4; + auto kIPv6 = Variants::IPv6; + auto kTcp = Variants::Tcp; + auto kHttp = Variants::Http; + auto variants = AppClass::Instance().dcOptions()->lookup(bareDcId(dc), dcType); + auto noIPv4 = (variants.data[kIPv4][kHttp].port == 0); + auto noIPv6 = (!Global::TryIPv6() || (variants.data[kIPv6][kHttp].port == 0)); if (noIPv4 && noIPv6) { if (afterConfig) { if (noIPv4) LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found right after config load!").arg(dc)); @@ -1170,21 +1119,21 @@ void ConnectionPrivate::socketStart(bool afterConfig) { _pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0; _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 (!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 (!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(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); if (auto conn = _conn4) { connect(conn, SIGNAL(connected()), this, SLOT(onConnected4())); connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected4())); - conn->connectTcp(ip[IPv4address][TcpProtocol].c_str(), port[IPv4address][TcpProtocol], flags[IPv4address][TcpProtocol]); - conn->connectHttp(ip[IPv4address][HttpProtocol].c_str(), port[IPv4address][HttpProtocol], flags[IPv4address][HttpProtocol]); + conn->connectTcp(variants.data[kIPv4][kTcp]); + conn->connectHttp(variants.data[kIPv4][kHttp]); } if (auto conn = _conn6) { connect(conn, SIGNAL(connected()), this, SLOT(onConnected6())); connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected6())); - conn->connectTcp(ip[IPv6address][TcpProtocol].c_str(), port[IPv6address][TcpProtocol], flags[IPv6address][TcpProtocol]); - conn->connectHttp(ip[IPv6address][HttpProtocol].c_str(), port[IPv6address][HttpProtocol], flags[IPv6address][HttpProtocol]); + conn->connectTcp(variants.data[kIPv6][kTcp]); + conn->connectHttp(variants.data[kIPv6][kHttp]); } } diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index 59704ea9e..f38f3af20 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -22,12 +22,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mtproto/core_types.h" #include "mtproto/auth_key.h" -#include "mtproto/connection_abstract.h" #include "core/single_timer.h" namespace MTP { namespace internal { +class AbstractConnection; class ConnectionPrivate; class SessionData; diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h index 4228ba107..b9d784a2d 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.h +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "mtproto/core_types.h" +#include "mtproto/dc_options.h" namespace MTP { namespace internal { @@ -29,7 +30,6 @@ class AbstractConnection : public QObject { Q_OBJECT public: - AbstractConnection(QThread *thread) : _sentEncrypted(false) { 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 disconnectFromServer() = 0; - virtual void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) = 0; - virtual void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) = 0; + virtual void connectTcp(const DcOptions::Endpoint &endpoint) = 0; + virtual void connectHttp(const DcOptions::Endpoint &endpoint) = 0; virtual bool isConnected() const = 0; virtual bool usingHttpWait() { return false; diff --git a/Telegram/SourceFiles/mtproto/connection_auto.cpp b/Telegram/SourceFiles/mtproto/connection_auto.cpp index 3d228f9ac..6b4535c4d 100644 --- a/Telegram/SourceFiles/mtproto/connection_auto.cpp +++ b/Telegram/SourceFiles/mtproto/connection_auto.cpp @@ -162,24 +162,25 @@ void AutoConnection::disconnectFromServer() { httpStartTimer.stop(); } -void AutoConnection::connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { - _addrTcp = addr; - _portTcp = port; - _flagsTcp = flags; +void AutoConnection::connectTcp(const DcOptions::Endpoint &endpoint) { + _addrTcp = QString::fromStdString(endpoint.ip); + _portTcp = endpoint.port; + _flagsTcp = endpoint.flags; connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead())); sock.connectToHost(QHostAddress(_addrTcp), _portTcp); } -void AutoConnection::connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { - 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 +void AutoConnection::connectHttp(const DcOptions::Endpoint &endpoint) { + _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())); connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); - _addrHttp = addr; - _portHttp = port; - _flagsHttp = flags; - mtpBuffer buffer(preparePQFake(httpNonce)); DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); diff --git a/Telegram/SourceFiles/mtproto/connection_auto.h b/Telegram/SourceFiles/mtproto/connection_auto.h index 746075916..6ce0eda82 100644 --- a/Telegram/SourceFiles/mtproto/connection_auto.h +++ b/Telegram/SourceFiles/mtproto/connection_auto.h @@ -35,8 +35,8 @@ public: void sendData(mtpBuffer &buffer) override; void disconnectFromServer() override; - void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; - void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; + void connectTcp(const DcOptions::Endpoint &endpoint) override; + void connectHttp(const DcOptions::Endpoint &endpoint) override; bool isConnected() const override; bool usingHttpWait() override; bool needHttpWait() override; diff --git a/Telegram/SourceFiles/mtproto/connection_http.cpp b/Telegram/SourceFiles/mtproto/connection_http.cpp index e68b2eb06..5fa8afe87 100644 --- a/Telegram/SourceFiles/mtproto/connection_http.cpp +++ b/Telegram/SourceFiles/mtproto/connection_http.cpp @@ -136,16 +136,18 @@ void HTTPConnection::disconnectFromServer() { address = QUrl(); } -void HTTPConnection::connectHttp(const QString &addr, int32 p, MTPDdcOption::Flags flags) { - 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 +void HTTPConnection::connectHttp(const DcOptions::Endpoint &endpoint) { + _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())); connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); - _flags = flags; - 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); } diff --git a/Telegram/SourceFiles/mtproto/connection_http.h b/Telegram/SourceFiles/mtproto/connection_http.h index 809fc7610..3395c3161 100644 --- a/Telegram/SourceFiles/mtproto/connection_http.h +++ b/Telegram/SourceFiles/mtproto/connection_http.h @@ -34,9 +34,9 @@ public: void sendData(mtpBuffer &buffer) 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 usingHttpWait() override; bool needHttpWait() override; diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp index bf173e219..ccbb65cef 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -339,10 +339,10 @@ void TCPConnection::disconnectFromServer() { sock.close(); } -void TCPConnection::connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { - _addr = addr; - _port = port; - _flags = flags; +void TCPConnection::connectTcp(const DcOptions::Endpoint &endpoint) { + _addr = QString::fromStdString(endpoint.ip); + _port = endpoint.port; + _flags = endpoint.flags; connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead())); sock.connectToHost(QHostAddress(_addr), _port); diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.h b/Telegram/SourceFiles/mtproto/connection_tcp.h index ec28e9961..e638d1468 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.h +++ b/Telegram/SourceFiles/mtproto/connection_tcp.h @@ -75,8 +75,8 @@ public: void sendData(mtpBuffer &buffer) override; void disconnectFromServer() override; - void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; - void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override { // not supported + void connectTcp(const DcOptions::Endpoint &endpoint) override; + void connectHttp(const DcOptions::Endpoint &endpoint) override { // not supported } bool isConnected() const override; diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp new file mode 100644 index 000000000..c78aed9ad --- /dev/null +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -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 &options, bool overwrite) { + if (options.empty()) { + return; + } + + auto idsChanged = std::vector(); + idsChanged.reserve(options.size()); + auto shiftedIdsProcessed = std::vector(); + 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 &options) { + processFromList(options.c_vector().v, true); +} + +void DcOptions::addFromList(const MTPVector &options) { + processFromList(options.c_vector().v, false); +} + +void DcOptions::addFromOther(const DcOptions &options) { + if (this == &options) { + return; + } + + auto idsChanged = std::vector(); + { + 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 diff --git a/Telegram/SourceFiles/mtproto/dc_options.h b/Telegram/SourceFiles/mtproto/dc_options.h new file mode 100644 index 000000000..d5a69c2bc --- /dev/null +++ b/Telegram/SourceFiles/mtproto/dc_options.h @@ -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 +#include +#include + +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; + base::Observable &changed() const { + return _changed; + } + void setFromList(const MTPVector &options); + void addFromList(const MTPVector &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 &options, bool overwrite); + + std::map _data; + mutable QReadWriteLock _mutex; + + mutable base::Observable _changed; + +}; + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/dcenter.cpp b/Telegram/SourceFiles/mtproto/dcenter.cpp index ab1ca14a2..5a3c6c50b 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.cpp +++ b/Telegram/SourceFiles/mtproto/dcenter.cpp @@ -19,25 +19,29 @@ 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/dcenter.h" #include "mtproto/facade.h" +#include "mtproto/dc_options.h" +#include "application.h" #include "localstorage.h" namespace MTP { namespace internal { - namespace { - DcenterMap gDCs; - bool configLoadedOnce = false; - bool mainDCChanged = false; - int32 _mainDC = 2; - int32 userId = 0; - typedef QMap _KeysMapForWrite; - _KeysMapForWrite _keysMapForWrite; - QMutex _keysMapForWriteMutex; +DcenterMap gDCs; +bool configLoadedOnce = false; +bool mainDCChanged = false; +int32 _mainDC = 2; +int32 userId = 0; + +typedef QMap _KeysMapForWrite; +_KeysMapForWrite _keysMapForWrite; +QMutex _keysMapForWriteMutex; + +constexpr auto kEnumerateDcTimeout = 8000; // 8 seconds timeout for help_getConfig to work (then move to other dc) + } // namespace 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())); - 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::SetMegagroupSizeMax(data.vmegagroup_size_max.v); @@ -191,47 +199,7 @@ bool configFailed(const RPCError &error) { }; -void updateDcOptions(const QVector &options) { - QSet already, restart; - { - MTP::DcOptions opts; - { - QReadLocker lock(dcOptionsMutex()); - opts = Global::DcOptions(); - } - for (QVector::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::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) { +ConfigLoader::ConfigLoader() { connect(&_enumDCTimer, SIGNAL(timeout()), this, SLOT(enumDC())); } @@ -241,7 +209,7 @@ void ConfigLoader::load() { MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed)); - _enumDCTimer.start(MTPEnumDCTimeout); + _enumDCTimer.start(kEnumerateDcTimeout); } void ConfigLoader::done() { @@ -267,23 +235,18 @@ void ConfigLoader::enumDC() { } else { MTP::killSession(MTP::cfgDcId(_enumCurrent)); } - OrderedSet dcs; - { - QReadLocker lock(dcOptionsMutex()); - const auto &options(Global::DcOptions()); - for (auto i = options.cbegin(), e = options.cend(); i != e; ++i) { - dcs.insert(MTP::bareDcId(i.key())); - } - } - auto i = dcs.constFind(_enumCurrent); - if (i == dcs.cend() || (++i) == dcs.cend()) { - _enumCurrent = *dcs.cbegin(); + auto ids = AppClass::Instance().dcOptions()->sortedDcIds(); + t_assert(!ids.empty()); + + auto i = std::find(ids.cbegin(), ids.cend(), _enumCurrent); + if (i == ids.cend() || (++i) == ids.cend()) { + _enumCurrent = ids.front(); } else { _enumCurrent = *i; } _enumRequest = MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed), MTP::cfgDcId(_enumCurrent)); - _enumDCTimer.start(MTPEnumDCTimeout); + _enumDCTimer.start(kEnumerateDcTimeout); } ConfigLoader *configLoader() { diff --git a/Telegram/SourceFiles/mtproto/dcenter.h b/Telegram/SourceFiles/mtproto/dcenter.h index b76b31c13..281f16b7f 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.h +++ b/Telegram/SourceFiles/mtproto/dcenter.h @@ -81,8 +81,8 @@ signals: private: SingleTimer _enumDCTimer; - int32 _enumCurrent; - mtpRequestId _enumRequest; + DcId _enumCurrent = 0; + mtpRequestId _enumRequest = 0; }; @@ -101,8 +101,5 @@ void authed(int32 uid); AuthKeysMap getAuthKeys(); void setAuthKey(int32 dc, AuthKeyPtr key); -void updateDcOptions(const QVector &options); -QReadWriteLock *dcOptionsMutex(); - } // namespace internal } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/facade.cpp b/Telegram/SourceFiles/mtproto/facade.cpp index 7d846bea6..3b66cdc8a 100644 --- a/Telegram/SourceFiles/mtproto/facade.cpp +++ b/Telegram/SourceFiles/mtproto/facade.cpp @@ -829,14 +829,13 @@ int32 state(mtpRequestId requestId) { } void finish() { - for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) { - i.value()->kill(); - delete i.value(); - } - sessions.clear(); 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(); delete connection; } @@ -886,11 +885,6 @@ void clearGlobalHandlers() { setSessionResetHandler(0); } -void updateDcOptions(const QVector &options) { - internal::updateDcOptions(options); - Local::writeSettings(); -} - AuthKeysMap getKeys() { return internal::getAuthKeys(); } @@ -903,8 +897,4 @@ void setKey(int dc, const AuthKey::Data &key) { return internal::setAuthKey(dc, std::move(keyPtr)); } -QReadWriteLock *dcOptionsMutex() { - return internal::dcOptionsMutex(); -} - } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index 94f49743e..e99271d3a 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -217,24 +217,9 @@ void setStateChangedHandler(MTPStateChangedHandler handler); void setSessionResetHandler(MTPSessionResetHandler handler); void clearGlobalHandlers(); -void updateDcOptions(const QVector &options); - AuthKeysMap getKeys(); 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 DcOptions; - namespace internal { template diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 9376175df..8741076e8 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -337,6 +337,8 @@ '<(src_loc)/mtproto/core_types.h', '<(src_loc)/mtproto/dcenter.cpp', '<(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.h', '<(src_loc)/mtproto/rsa_public_key.cpp',