diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp index 9b5e08307..787db5d2e 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.cpp +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -45,7 +45,7 @@ void DcOptions::constructFromBuiltIn() { } void DcOptions::processFromList(const QVector &options, bool overwrite) { - if (options.empty()) { + if (options.empty() || _immutable) { return; } @@ -109,7 +109,7 @@ void DcOptions::addFromList(const MTPVector &options) { } void DcOptions::addFromOther(const DcOptions &options) { - if (this == &options) { + if (this == &options || _immutable) { return; } @@ -163,6 +163,11 @@ bool DcOptions::applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std: } QByteArray DcOptions::serialize() const { + if (_immutable) { + // Don't write the overriden options to our settings. + return DcOptions().serialize(); + } + QReadLocker lock(&_mutex); auto size = sizeof(qint32); @@ -300,4 +305,87 @@ DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const { return result; } +bool DcOptions::loadFromFile(const QString &path) { + QVector options; + + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) { + LOG(("MTP Error: could not read '%1'").arg(path)); + return false; + } + QTextStream stream(&f); + stream.setCodec("UTF-8"); + while (!stream.atEnd()) { + auto line = stream.readLine(); + auto components = line.split(QRegularExpression(R"(\s)"), QString::SkipEmptyParts); + if (components.isEmpty() || components[0].startsWith('#')) { + continue; + } + + auto error = [line] { + LOG(("MTP Error: in .tdesktop-endpoints expected 'dcId host port [tcpo_only] [media_only]', got '%1'").arg(line)); + return false; + }; + if (components.size() < 3) { + return error(); + } + auto dcId = components[0].toInt(); + auto ip = components[1]; + auto port = components[2].toInt(); + auto host = QHostAddress(); + if (dcId <= 0 || dcId >= internal::kDcShift || !host.setAddress(ip) || port <= 0) { + return error(); + } + auto flags = MTPDdcOption::Flags(0); + if (host.protocol() == QAbstractSocket::IPv6Protocol) { + flags |= MTPDdcOption::Flag::f_ipv6; + } + for (auto &option : components.mid(3)) { + if (option.startsWith('#')) { + break; + } else if (option == qstr("tcpo_only")) { + flags |= MTPDdcOption::Flag::f_tcpo_only; + } else if (option == qstr("media_only")) { + flags |= MTPDdcOption::Flag::f_media_only; + } else { + return error(); + } + } + options.push_back(MTP_dcOption(MTP_flags(flags), MTP_int(dcId), MTP_string(ip), MTP_int(port))); + } + if (options.isEmpty()) { + LOG(("MTP Error: in .tdesktop-endpoints expected at least one endpoint being provided.")); + return false; + } + + _immutable = false; + setFromList(MTP_vector(options)); + _immutable = true; + + return true; +} + +bool DcOptions::writeToFile(const QString &path) const { + QFile f(path); + if (!f.open(QIODevice::WriteOnly)) { + return false; + } + QTextStream stream(&f); + stream.setCodec("UTF-8"); + + QReadLocker lock(&_mutex); + for (auto &item : _data) { + auto &endpoint = item.second; + stream << endpoint.id << ' ' << QString::fromStdString(endpoint.ip) << ' ' << endpoint.port; + if (endpoint.flags & MTPDdcOption::Flag::f_tcpo_only) { + stream << " tcpo_only"; + } + if (endpoint.flags & MTPDdcOption::Flag::f_media_only) { + stream << " media_only"; + } + stream << '\n'; + } + return true; +} + } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/dc_options.h b/Telegram/SourceFiles/mtproto/dc_options.h index d5a69c2bc..567525177 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.h +++ b/Telegram/SourceFiles/mtproto/dc_options.h @@ -70,6 +70,10 @@ public: }; Variants lookup(DcId dcId, DcType type) const; + // Debug feature for now. + bool loadFromFile(const QString &path); + bool writeToFile(const QString &path) const; + private: struct Option { Option(DcId id, MTPDdcOption::Flags flags, const std::string &ip, int port) : id(id), flags(flags), ip(ip), port(port) { @@ -89,6 +93,9 @@ private: mutable base::Observable _changed; + // True when we have overriden options from a .tdesktop-endpoints file. + bool _immutable = false; + }; } // namespace MTP diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index d91450aa4..3aac48f5a 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -35,6 +35,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "lang.h" #include "messenger.h" +#include "mtproto/mtp_instance.h" +#include "mtproto/dc_options.h" #include "core/file_utilities.h" #include "window/themes/window_theme.h" #include "window/themes/window_theme_editor.h" @@ -110,6 +112,15 @@ void fillCodes() { Ui::hideLayer(); })); }); + Codes.insert(qsl("endpoints"), []() { + FileDialog::GetOpenPath("Open DC endpoints", "DC Endpoints (*.tdesktop-endpoints)", [](const FileDialog::OpenResult &result) { + if (!result.paths.isEmpty()) { + if (!Messenger::Instance().mtp()->dcOptions()->loadFromFile(result.paths.front())) { + Ui::show(Box("Could not load endpoints :( Errors in 'log.txt'.")); + } + } + }); + }); } void codesFeedString(const QString &text) {