From 2cd90eb3ce0acfe9a46b293a67ba2d944f17c80e Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 16 Apr 2026 14:50:06 +0700 Subject: [PATCH] Add proxy auto-rotation option. --- Telegram/CMakeLists.txt | 4 + Telegram/Resources/langs/lang.strings | 4 + Telegram/SourceFiles/boxes/connection_box.cpp | 263 +++++++------ Telegram/SourceFiles/boxes/connection_box.h | 2 + Telegram/SourceFiles/core/application.cpp | 15 + Telegram/SourceFiles/core/application.h | 2 + .../SourceFiles/core/core_settings_proxy.cpp | 164 +++++++- .../SourceFiles/core/core_settings_proxy.h | 33 +- .../core/proxy_rotation_manager.cpp | 359 ++++++++++++++++++ .../SourceFiles/core/proxy_rotation_manager.h | 80 ++++ Telegram/SourceFiles/main/main_account.cpp | 1 + Telegram/SourceFiles/mtproto/proxy_check.cpp | 111 ++++++ Telegram/SourceFiles/mtproto/proxy_check.h | 35 ++ .../details/storage_settings_scheme.cpp | 18 +- 14 files changed, 958 insertions(+), 133 deletions(-) create mode 100644 Telegram/SourceFiles/core/proxy_rotation_manager.cpp create mode 100644 Telegram/SourceFiles/core/proxy_rotation_manager.h create mode 100644 Telegram/SourceFiles/mtproto/proxy_check.cpp create mode 100644 Telegram/SourceFiles/mtproto/proxy_check.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index a1fae40a00..f5c3c1499c 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -503,6 +503,8 @@ PRIVATE chat_helpers/ttl_media_layer_widget.h core/application.cpp core/application.h + core/proxy_rotation_manager.cpp + core/proxy_rotation_manager.h core/cached_webview_availability.h core/bank_card_click_handler.cpp core/bank_card_click_handler.h @@ -1436,6 +1438,8 @@ PRIVATE mtproto/facade.h mtproto/mtp_instance.cpp mtproto/mtp_instance.h + mtproto/proxy_check.cpp + mtproto/proxy_check.h mtproto/sender.h mtproto/session.cpp mtproto/session.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 226614677b..0ee8c1f13d 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1239,6 +1239,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_proxy_use_system_settings" = "Use system proxy settings"; "lng_proxy_use_custom" = "Use custom proxy"; "lng_proxy_use_for_calls" = "Use proxy for calls"; +"lng_proxy_auto_switch" = "Auto-switch proxies"; +"lng_proxy_auto_switch_about" = "You can choose how quickly the app should auto-connect to the nearest active proxy if the current one stops working."; +"lng_proxy_auto_switch_timeout#one" = "{count} s"; +"lng_proxy_auto_switch_timeout#other" = "{count} s"; "lng_proxy_about" = "Proxy servers may be helpful in accessing Telegram if there is no connection in a specific region."; "lng_proxy_add" = "Add proxy"; "lng_proxy_share" = "Share"; diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index 0a172d31d4..7ca091ebf2 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_account.h" #include "main/main_session.h" #include "mtproto/facade.h" +#include "mtproto/proxy_check.h" #include "settings/settings_common.h" #include "storage/localstorage.h" #include "ui/basic_click_handlers.h" @@ -32,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/dropdown_menu.h" +#include "ui/widgets/discrete_sliders.h" #include "ui/widgets/fields/input_field.h" #include "ui/widgets/fields/number_input.h" #include "ui/widgets/fields/password_input.h" @@ -62,6 +64,20 @@ constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000); using ProxyData = MTP::ProxyData; +[[nodiscard]] int ClosestProxyRotationTimeoutSection(int value) { + auto result = 0; + auto bestDistance = 0; + for (auto i = 0; i != int(Core::SettingsProxy::kProxyRotationTimeouts.size()); ++i) { + const auto current = Core::SettingsProxy::kProxyRotationTimeouts[i]; + const auto distance = (current > value) ? (current - value) : (value - current); + if ((i == 0) || (distance < bestDistance)) { + result = i; + bestDistance = distance; + } + } + return result; +} + [[nodiscard]] std::vector ExtractUrlsSimple(const QString &input) { auto urls = std::vector(); static auto urlRegex = QRegularExpression(R"((https?:\/\/[^\s]+))"); @@ -376,12 +392,16 @@ private: void setupButtons(int id, not_null button); int rowHeight() const; void refreshProxyForCalls(); + void refreshProxyRotation(); not_null _controller; Core::SettingsProxy &_settings; QPointer _tryIPv6; std::shared_ptr> _proxySettings; QPointer> _proxyForCalls; + QPointer> _proxyRotation; + QPointer> _proxyRotationOptions; + QPointer _proxyRotationTimeout; QPointer _about; base::unique_qptr _noRows; object_ptr _initialWrap; @@ -900,6 +920,47 @@ void ProxiesBox::setupContent() { 0, st::proxyTryIPv6Padding.right(), st::proxyTryIPv6Padding.top())); + _proxyRotation = inner->add( + object_ptr>( + inner, + object_ptr( + inner, + tr::lng_proxy_auto_switch(tr::now), + _settings.proxyRotationEnabled()), + style::margins( + 0, + st::proxyUsePadding.top(), + 0, + st::proxyUsePadding.bottom())), + style::margins( + st::proxyTryIPv6Padding.left(), + 0, + st::proxyTryIPv6Padding.right(), + st::proxyTryIPv6Padding.top())); + _proxyRotationOptions = inner->add( + object_ptr>( + inner, + object_ptr(inner))); + _proxyRotationTimeout = _proxyRotationOptions->entity()->add( + object_ptr( + _proxyRotationOptions->entity(), + st::settingsSlider), + st::settingsBigScalePadding); + for (const auto seconds : Core::SettingsProxy::kProxyRotationTimeouts) { + _proxyRotationTimeout->addSection( + tr::lng_proxy_auto_switch_timeout( + tr::now, + lt_count, + seconds)); + } + _proxyRotationTimeout->setActiveSectionFast( + ClosestProxyRotationTimeoutSection(_settings.proxyRotationTimeout())); + _proxyRotationOptions->entity()->add( + object_ptr( + _proxyRotationOptions->entity(), + tr::lng_proxy_auto_switch_about(tr::now), + st::boxDividerLabel), + st::proxyAboutPadding); _about = inner->add( object_ptr( @@ -922,6 +983,7 @@ void ProxiesBox::setupContent() { addNewProxy(); } refreshProxyForCalls(); + refreshProxyRotation(); }); _tryIPv6->checkedChanges( ) | rpl::on_next([=](bool checked) { @@ -931,18 +993,33 @@ void ProxiesBox::setupContent() { _controller->proxySettingsValue( ) | rpl::on_next([=](ProxyData::Settings value) { _proxySettings->setValue(value); + refreshProxyForCalls(); + refreshProxyRotation(); }, inner->lifetime()); _proxyForCalls->entity()->checkedChanges( ) | rpl::on_next([=](bool checked) { _controller->setProxyForCalls(checked); }, _proxyForCalls->lifetime()); + _proxyRotation->entity()->checkedChanges( + ) | rpl::on_next([=](bool checked) { + _controller->setProxyRotationEnabled(checked); + refreshProxyRotation(); + }, _proxyRotation->lifetime()); + _proxyRotationTimeout->sectionActivated( + ) | rpl::on_next([=](int section) { + _controller->setProxyRotationTimeout( + Core::SettingsProxy::kProxyRotationTimeouts[section]); + }, _proxyRotationTimeout->lifetime()); if (_rows.empty()) { createNoRowsLabel(); } refreshProxyForCalls(); + refreshProxyRotation(); _proxyForCalls->finishAnimating(); + _proxyRotation->finishAnimating(); + _proxyRotationOptions->finishAnimating(); { const auto wrap = inner->add( @@ -987,6 +1064,20 @@ void ProxiesBox::refreshProxyForCalls() { anim::type::normal); } +void ProxiesBox::refreshProxyRotation() { + if (!_proxyRotation || !_proxyRotationOptions) { + return; + } + const auto visible = (_proxySettings->current() + == ProxyData::Settings::Enabled) + && _settings.selected() + && (_settings.list().size() > 1); + _proxyRotation->toggle(visible, anim::type::normal); + _proxyRotationOptions->toggle( + visible && _proxyRotation->entity()->checked(), + anim::type::normal); +} + int ProxiesBox::rowHeight() const { return st::proxyRowPadding.top() + st::semiboldFont->height @@ -1029,6 +1120,7 @@ void ProxiesBox::applyView(View &&view) { } else { i->second->updateFields(std::move(view)); } + refreshProxyRotation(); } void ProxiesBox::createNoRowsLabel() { @@ -1359,95 +1451,7 @@ void ProxyBox::addLabel( } using Connection = MTP::details::AbstractConnection; -using Checker = MTP::details::ConnectionPointer; - -void ResetProxyCheckers(Checker &v4, Checker &v6) { - v4 = nullptr; - v6 = nullptr; -} - -void DropProxyChecker(Checker &v4, Checker &v6, not_null raw) { - if (v4.get() == raw) { - v4 = nullptr; - } else if (v6.get() == raw) { - v6 = nullptr; - } -} - -[[nodiscard]] bool HasProxyCheckers(const Checker &v4, const Checker &v6) { - return v4 || v6; -} - -void StartProxyCheck( - not_null mtproto, - const ProxyData &proxy, - Checker &v4, - Checker &v6, - Fn done, - Fn fail) { - using Variants = MTP::DcOptions::Variants; - - ResetProxyCheckers(v4, v6); - const auto connType = (proxy.type == ProxyData::Type::Http) - ? Variants::Http - : Variants::Tcp; - const auto dcId = mtproto->mainDcId(); - const auto setup = [&](Checker &checker, const bytes::vector &secret) { - checker = Connection::Create( - mtproto, - connType, - QThread::currentThread(), - secret, - proxy); - const auto raw = checker.get(); - raw->connect(raw, &Connection::connected, [=] { - if (done) { - done(raw, raw->pingTime()); - } - }); - const auto failed = [=] { - if (fail) { - fail(raw); - } - }; - raw->connect(raw, &Connection::disconnected, failed); - raw->connect(raw, &Connection::error, failed); - }; - if (proxy.type == ProxyData::Type::Mtproto) { - const auto secret = proxy.secretFromMtprotoPassword(); - setup(v4, secret); - v4->connectToServer( - proxy.host, - proxy.port, - secret, - dcId, - false); - return; - } - const auto options = mtproto->dcOptions().lookup( - dcId, - MTP::DcType::Regular, - true); - const auto tryConnect = [&](Checker &checker, Variants::Address address) { - const auto &list = options.data[address][connType]; - if (list.empty() - || ((address == Variants::IPv6) - && !Core::App().settings().proxy().tryIPv6())) { - checker = nullptr; - return; - } - const auto &endpoint = list.front(); - setup(checker, endpoint.secret); - checker->connectToServer( - QString::fromStdString(endpoint.ip), - endpoint.port, - endpoint.secret, - dcId, - false); - }; - tryConnect(v4, Variants::IPv4); - tryConnect(v6, Variants::IPv6); -} +using Checker = MTP::ProxyCheckConnection; } // namespace @@ -1609,18 +1613,19 @@ void ProxiesBoxController::ShowApplyConfirmation( }; statusLabel->setTextColorOverride(st::proxyRowStatusFg->c); relayout(); - StartProxyCheck( + MTP::StartProxyCheck( &account->mtp(), proxy, + Core::App().settings().proxy().tryIPv6(), state->v4, state->v6, [=](Connection *raw, int ping) { if (!weak || state->finished) { return; } - DropProxyChecker(state->v4, state->v6, raw); + MTP::DropProxyChecker(state->v4, state->v6, raw); state->finished = true; - ResetProxyCheckers(state->v4, state->v6); + MTP::ResetProxyCheckers(state->v4, state->v6); state->statusValue = TextWithEntities{ tr::lng_proxy_box_table_available( tr::now, @@ -1635,13 +1640,13 @@ void ProxiesBoxController::ShowApplyConfirmation( if (!weak || state->finished) { return; } - DropProxyChecker(state->v4, state->v6, raw); - if (!HasProxyCheckers(state->v4, state->v6)) { + MTP::DropProxyChecker(state->v4, state->v6, raw); + if (!MTP::HasProxyCheckers(state->v4, state->v6)) { state->finished = true; setUnavailable(); } }); - if (!HasProxyCheckers(state->v4, state->v6)) { + if (!MTP::HasProxyCheckers(state->v4, state->v6)) { state->finished = true; setUnavailable(); } @@ -1681,9 +1686,9 @@ void ProxiesBoxController::ShowApplyConfirmation( const auto enableButton = box->addButton( tr::lng_proxy_box_table_button(), [=] { - auto &proxies = Core::App().settings().proxy().list(); - if (!ranges::contains(proxies, proxy)) { - proxies.push_back(proxy); + auto &settings = Core::App().settings().proxy(); + if (settings.indexInList(proxy) < 0) { + settings.addToList(proxy); } Core::App().setCurrentProxy( proxy, @@ -1719,9 +1724,10 @@ auto ProxiesBoxController::proxySettingsValue() const void ProxiesBoxController::refreshChecker(Item &item) { item.state = ItemState::Checking; const auto id = item.id; - StartProxyCheck( + MTP::StartProxyCheck( &_account->mtp(), item.data, + Core::App().settings().proxy().tryIPv6(), item.checker, item.checkerv6, [=](Connection *raw, int pingTime) { @@ -1732,8 +1738,8 @@ void ProxiesBoxController::refreshChecker(Item &item) { if (item == end(_list)) { return; } - DropProxyChecker(item->checker, item->checkerv6, raw); - ResetProxyCheckers(item->checker, item->checkerv6); + MTP::DropProxyChecker(item->checker, item->checkerv6, raw); + MTP::ResetProxyCheckers(item->checker, item->checkerv6); if (item->state == ItemState::Checking) { item->state = ItemState::Available; item->ping = pingTime; @@ -1748,14 +1754,14 @@ void ProxiesBoxController::refreshChecker(Item &item) { if (item == end(_list)) { return; } - DropProxyChecker(item->checker, item->checkerv6, raw); - if (!HasProxyCheckers(item->checker, item->checkerv6) + MTP::DropProxyChecker(item->checker, item->checkerv6, raw); + if (!MTP::HasProxyCheckers(item->checker, item->checkerv6) && item->state == ItemState::Checking) { item->state = ItemState::Unavailable; updateView(*item); } }); - if (!HasProxyCheckers(item.checker, item.checkerv6)) { + if (!MTP::HasProxyCheckers(item.checker, item.checkerv6)) { item.state = ItemState::Unavailable; } } @@ -1854,8 +1860,8 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) { item->deleted = deleted; if (deleted) { - auto &proxies = _settings.list(); - proxies.erase(ranges::remove(proxies, item->data), end(proxies)); + const auto removed = _settings.removeFromList(item->data); + Assert(removed); if (item->data == _settings.selected()) { _lastSelectedProxy = _settings.selected(); @@ -1871,16 +1877,19 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) { } } } else { - auto &proxies = _settings.list(); - if (ranges::find(proxies, item->data) == end(proxies)) { + if (_settings.indexInList(item->data) < 0) { + const auto &proxies = _settings.list(); auto insertBefore = item + 1; while (insertBefore != end(_list) && insertBefore->deleted) { ++insertBefore; } - auto insertBeforeIt = (insertBefore == end(_list)) - ? end(proxies) - : ranges::find(proxies, insertBefore->data); - proxies.insert(insertBeforeIt, item->data); + const auto foundIndex = (insertBefore == end(_list)) + ? int(proxies.size()) + : _settings.indexInList(insertBefore->data); + const auto insertIndex = (foundIndex >= 0) + ? foundIndex + : int(proxies.size()); + _settings.insertToList(insertIndex, item->data); } if (!_settings.selected() && _lastSelectedProxy == item->data) { @@ -1919,8 +1928,8 @@ object_ptr ProxiesBoxController::editItemBox(int id) { void ProxiesBoxController::replaceItemWith( std::vector::iterator which, std::vector::iterator with) { - auto &proxies = _settings.list(); - proxies.erase(ranges::remove(proxies, which->data), end(proxies)); + const auto removed = _settings.removeFromList(which->data); + Assert(removed); _views.fire({ which->id }); _list.erase(which); @@ -1939,10 +1948,8 @@ void ProxiesBoxController::replaceItemValue( restoreItem(which->id); } - auto &proxies = _settings.list(); - const auto i = ranges::find(proxies, which->data); - Assert(i != end(proxies)); - *i = proxy; + const auto replaced = _settings.replaceInList(which->data, proxy); + Assert(replaced); which->data = proxy; refreshChecker(*which); @@ -1978,8 +1985,7 @@ bool ProxiesBoxController::contains(const ProxyData &proxy) const { } void ProxiesBoxController::addNewItem(const ProxyData &proxy) { - auto &proxies = _settings.list(); - proxies.push_back(proxy); + _settings.addToList(proxy); _list.push_back({ ++_idCounter, proxy }); refreshChecker(_list.back()); @@ -2016,6 +2022,22 @@ void ProxiesBoxController::setProxyForCalls(bool enabled) { saveDelayed(); } +void ProxiesBoxController::setProxyRotationEnabled(bool enabled) { + if (_settings.proxyRotationEnabled() == enabled) { + return; + } + _settings.setProxyRotationEnabled(enabled); + saveDelayed(); +} + +void ProxiesBoxController::setProxyRotationTimeout(int value) { + if (_settings.proxyRotationTimeout() == value) { + return; + } + _settings.setProxyRotationTimeout(value); + saveDelayed(); +} + void ProxiesBoxController::setTryIPv6(bool enabled) { if (Core::App().settings().proxy().tryIPv6() == enabled) { return; @@ -2027,6 +2049,7 @@ void ProxiesBoxController::setTryIPv6(bool enabled) { } void ProxiesBoxController::saveDelayed() { + Core::App().proxyRotationSettingsChanged(); _saveTimer.callOnce(kSaveSettingsDelayedTimeout); } diff --git a/Telegram/SourceFiles/boxes/connection_box.h b/Telegram/SourceFiles/boxes/connection_box.h index c33f8ea0f4..5de98bc663 100644 --- a/Telegram/SourceFiles/boxes/connection_box.h +++ b/Telegram/SourceFiles/boxes/connection_box.h @@ -85,6 +85,8 @@ public: object_ptr addNewItemBox(); bool setProxySettings(ProxyData::Settings value); void setProxyForCalls(bool enabled); + void setProxyRotationEnabled(bool enabled); + void setProxyRotationTimeout(int value); void setTryIPv6(bool enabled); rpl::producer proxySettingsValue() const; diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index de4b20765a..6840b9eb35 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/sandbox.h" #include "core/local_url_handlers.h" #include "core/launcher.h" +#include "core/proxy_rotation_manager.h" #include "core/ui_integration.h" #include "chat_helpers/emoji_keywords.h" #include "chat_helpers/stickers_emoji_image_loader.h" @@ -146,6 +147,7 @@ struct Application::Private { base::Timer quitTimer; UiIntegration uiIntegration; Settings settings; + std::unique_ptr proxyRotation; }; Application::Application() @@ -173,6 +175,7 @@ Application::Application() , _setupEmailLock(false) , _autoLockTimer([=] { checkAutoLock(); }) { Ui::Integration::Set(&_private->uiIntegration); + _private->proxyRotation = std::make_unique(); _platformIntegration->init(); @@ -234,6 +237,7 @@ Application::~Application() { // Domain::finish() and there is a violation on Ensures(started()). closeAdditionalWindows(); + _private->proxyRotation = nullptr; _domain->finish(); Local::finish(); @@ -833,6 +837,17 @@ void Application::setCurrentProxy( refreshGlobalProxy(); _proxyChanges.fire({ was, now }); my.connectionTypeChangesNotify(); + proxyRotationSettingsChanged(); +} + +void Application::proxyRotationSettingsChanged() { + _private->proxyRotation->settingsChanged(); +} + +void Application::checkProxyRotation( + not_null account, + int32 state) { + _private->proxyRotation->handleConnectionStateChanged(account, state); } auto Application::proxyChanges() const -> rpl::producer { diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 104f194d3d..6c0bfecbd6 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -219,6 +219,8 @@ public: void setCurrentProxy( const MTP::ProxyData &proxy, MTP::ProxyData::Settings settings); + void proxyRotationSettingsChanged(); + void checkProxyRotation(not_null account, int32 state); [[nodiscard]] rpl::producer proxyChanges() const; void badMtprotoConfigurationError(); diff --git a/Telegram/SourceFiles/core/core_settings_proxy.cpp b/Telegram/SourceFiles/core/core_settings_proxy.cpp index 6f338de6aa..495bb0b846 100644 --- a/Telegram/SourceFiles/core/core_settings_proxy.cpp +++ b/Telegram/SourceFiles/core/core_settings_proxy.cpp @@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "storage/serialize_common.h" +#include + namespace Core { namespace { @@ -88,6 +90,22 @@ namespace { return result; } +std::vector NormalizeProxyRotationPreferredIndices( + std::vector indices, + int listSize) { + auto filtered = std::vector(); + filtered.reserve(indices.size()); + for (const auto index : indices) { + if (index < 0 + || index >= listSize + || ranges::contains(filtered, index)) { + continue; + } + filtered.push_back(index); + } + return filtered; +} + } // namespace SettingsProxy::SettingsProxy() @@ -108,7 +126,7 @@ QByteArray SettingsProxy::serialize() const { 0, ranges::plus(), &Serialize::bytearraySize) - + 1 * sizeof(qint32); // _checkIpWarningShown + + (4 + int(_proxyRotationPreferredIndices.size())) * sizeof(qint32); auto stream = Serialize::ByteArrayWriter(size); stream << qint32(_tryIPv6 ? 1 : 0) @@ -119,7 +137,14 @@ QByteArray SettingsProxy::serialize() const { for (const auto &i : serializedList) { stream << i; } - stream << qint32(_checkIpWarningShown ? 1 : 0); + stream + << qint32(_checkIpWarningShown ? 1 : 0) + << qint32(_proxyRotationEnabled ? 1 : 0) + << qint32(_proxyRotationTimeout) + << qint32(_proxyRotationPreferredIndices.size()); + for (const auto index : _proxyRotationPreferredIndices) { + stream << qint32(index); + } return std::move(stream).result(); } @@ -135,6 +160,7 @@ bool SettingsProxy::setFromSerialized(const QByteArray &serialized) { auto settings = ProxySettingsToInt(_settings); auto listCount = qint32(_list.size()); auto selectedProxy = QByteArray(); + auto list = std::vector(); if (!stream.atEnd()) { stream @@ -144,10 +170,14 @@ bool SettingsProxy::setFromSerialized(const QByteArray &serialized) { >> selectedProxy >> listCount; if (stream.ok()) { + if (listCount < 0) { + return false; + } + list.reserve(listCount); for (auto i = 0; i != listCount; ++i) { QByteArray data; stream >> data; - _list.push_back(DeserializeProxyData(data)); + list.push_back(DeserializeProxyData(data)); } } } @@ -156,6 +186,30 @@ bool SettingsProxy::setFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> checkIpWarningShown; } + auto proxyRotationEnabled = qint32(_proxyRotationEnabled ? 1 : 0); + if (!stream.atEnd()) { + stream >> proxyRotationEnabled; + } + auto proxyRotationTimeout = qint32(_proxyRotationTimeout); + if (!stream.atEnd()) { + stream >> proxyRotationTimeout; + } + auto preferredCount = qint32(0); + auto preferredIndices = std::vector(); + if (!stream.atEnd()) { + stream >> preferredCount; + if (stream.ok()) { + if (preferredCount < 0) { + return false; + } + preferredIndices.reserve(preferredCount); + for (auto i = 0; i != preferredCount; ++i) { + auto index = qint32(0); + stream >> index; + preferredIndices.push_back(index); + } + } + } if (!stream.ok()) { LOG(("App Error: " @@ -166,8 +220,12 @@ bool SettingsProxy::setFromSerialized(const QByteArray &serialized) { _tryIPv6 = (tryIPv6 == 1); _useProxyForCalls = (useProxyForCalls == 1); _checkIpWarningShown = (checkIpWarningShown == 1); + _proxyRotationEnabled = (proxyRotationEnabled == 1); + setProxyRotationTimeout(proxyRotationTimeout); _settings = IntToProxySettings(settings); _selected = DeserializeProxyData(selectedProxy); + _list = std::move(list); + setProxyRotationPreferredIndices(std::move(preferredIndices)); return true; } @@ -192,6 +250,32 @@ void SettingsProxy::setCheckIpWarningShown(bool value) { _checkIpWarningShown = value; } +const std::vector &SettingsProxy::proxyRotationPreferredIndices() const { + return _proxyRotationPreferredIndices; +} + +void SettingsProxy::setProxyRotationPreferredIndices(std::vector value) { + _proxyRotationPreferredIndices = NormalizeProxyRotationPreferredIndices( + std::move(value), + int(_list.size())); +} + +bool SettingsProxy::promoteProxyRotationPreferredIndex(int index) { + if (index < 0 || index >= int(_list.size())) { + return false; + } + auto &indices = _proxyRotationPreferredIndices; + const auto i = ranges::find(indices, index); + if (i == begin(indices)) { + return false; + } else if (i != end(indices)) { + std::rotate(begin(indices), i, std::next(i)); + } else { + indices.insert(begin(indices), index); + } + return true; +} + bool SettingsProxy::tryIPv6() const { return _tryIPv6; } @@ -208,6 +292,24 @@ void SettingsProxy::setUseProxyForCalls(bool value) { _useProxyForCalls = value; } +bool SettingsProxy::proxyRotationEnabled() const { + return _proxyRotationEnabled; +} + +void SettingsProxy::setProxyRotationEnabled(bool value) { + _proxyRotationEnabled = value; +} + +int SettingsProxy::proxyRotationTimeout() const { + return _proxyRotationTimeout; +} + +void SettingsProxy::setProxyRotationTimeout(int value) { + _proxyRotationTimeout = (value > 0) + ? value + : kDefaultProxyRotationTimeout; +} + MTP::ProxyData::Settings SettingsProxy::settings() const { return _settings; } @@ -232,6 +334,62 @@ std::vector &SettingsProxy::list() { return _list; } +void SettingsProxy::setList(std::vector value) { + _list = std::move(value); + _proxyRotationPreferredIndices.clear(); +} + +void SettingsProxy::addToList(MTP::ProxyData value) { + _list.push_back(std::move(value)); +} + +void SettingsProxy::insertToList(int index, MTP::ProxyData value) { + index = std::clamp(index, 0, int(_list.size())); + for (auto &existing : _proxyRotationPreferredIndices) { + if (existing >= index) { + ++existing; + } + } + _list.insert(begin(_list) + index, std::move(value)); +} + +bool SettingsProxy::removeFromList(const MTP::ProxyData &value) { + const auto i = ranges::find(_list, value); + if (i == end(_list)) { + return false; + } + const auto index = int(i - begin(_list)); + _list.erase(i); + for (auto &existing : _proxyRotationPreferredIndices) { + if (existing > index) { + --existing; + } + } + _proxyRotationPreferredIndices.erase( + std::remove( + begin(_proxyRotationPreferredIndices), + end(_proxyRotationPreferredIndices), + index), + end(_proxyRotationPreferredIndices)); + return true; +} + +bool SettingsProxy::replaceInList( + const MTP::ProxyData &was, + MTP::ProxyData value) { + const auto i = ranges::find(_list, was); + if (i == end(_list)) { + return false; + } + *i = std::move(value); + return true; +} + +int SettingsProxy::indexInList(const MTP::ProxyData &value) const { + const auto i = ranges::find(_list, value); + return (i == end(_list)) ? -1 : int(i - begin(_list)); +} + rpl::producer<> SettingsProxy::connectionTypeValue() const { return _connectionTypeChanges.events_starting_with({}); } diff --git a/Telegram/SourceFiles/core/core_settings_proxy.h b/Telegram/SourceFiles/core/core_settings_proxy.h index 340dd050d8..1f2ec5c516 100644 --- a/Telegram/SourceFiles/core/core_settings_proxy.h +++ b/Telegram/SourceFiles/core/core_settings_proxy.h @@ -9,10 +9,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_proxy_data.h" +#include + namespace Core { class SettingsProxy final { public: + static constexpr auto kProxyRotationTimeouts = std::array{ + 5, + 10, + 15, + 30, + 60, + }; + static constexpr auto kDefaultProxyRotationTimeout = 10; + SettingsProxy(); [[nodiscard]] bool isEnabled() const; @@ -29,6 +40,12 @@ public: [[nodiscard]] bool useProxyForCalls() const; void setUseProxyForCalls(bool value); + [[nodiscard]] bool proxyRotationEnabled() const; + void setProxyRotationEnabled(bool value); + + [[nodiscard]] int proxyRotationTimeout() const; + void setProxyRotationTimeout(int value); + [[nodiscard]] MTP::ProxyData::Settings settings() const; void setSettings(MTP::ProxyData::Settings value); @@ -38,8 +55,20 @@ public: [[nodiscard]] bool checkIpWarningShown() const; void setCheckIpWarningShown(bool value); + [[nodiscard]] const std::vector &proxyRotationPreferredIndices() const; + void setProxyRotationPreferredIndices(std::vector value); + [[nodiscard]] bool promoteProxyRotationPreferredIndex(int index); + [[nodiscard]] const std::vector &list() const; [[nodiscard]] std::vector &list(); + void setList(std::vector value); + void addToList(MTP::ProxyData value); + void insertToList(int index, MTP::ProxyData value); + [[nodiscard]] bool removeFromList(const MTP::ProxyData &value); + [[nodiscard]] bool replaceInList( + const MTP::ProxyData &was, + MTP::ProxyData value); + [[nodiscard]] int indexInList(const MTP::ProxyData &value) const; [[nodiscard]] QByteArray serialize() const; bool setFromSerialized(const QByteArray &serialized); @@ -47,14 +76,16 @@ public: private: bool _tryIPv6 = false; bool _useProxyForCalls = false; + bool _proxyRotationEnabled = false; bool _checkIpWarningShown = false; + int _proxyRotationTimeout = kDefaultProxyRotationTimeout; MTP::ProxyData::Settings _settings = MTP::ProxyData::Settings::System; MTP::ProxyData _selected; std::vector _list; + std::vector _proxyRotationPreferredIndices; rpl::event_stream<> _connectionTypeChanges; }; } // namespace Core - diff --git a/Telegram/SourceFiles/core/proxy_rotation_manager.cpp b/Telegram/SourceFiles/core/proxy_rotation_manager.cpp new file mode 100644 index 0000000000..a643f0aee6 --- /dev/null +++ b/Telegram/SourceFiles/core/proxy_rotation_manager.cpp @@ -0,0 +1,359 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "core/proxy_rotation_manager.h" + +#include "core/application.h" +#include "core/core_settings.h" +#include "main/main_account.h" +#include "main/main_domain.h" +#include "mtproto/facade.h" + +#include + +namespace Core { +namespace { + +constexpr auto kProxyRotationCheckInterval = 2 * crl::time(1000); +constexpr auto kProxyRotationCheckLifetime = 20 * crl::time(1000); +constexpr auto kProxyRotationMaxActiveChecks = 10; + +} // namespace + +ProxyRotationManager::ProxyRotationManager() +: _checkTimer([=] { runChecks(); }) +, _switchTimer([=] { switchTimerDone(); }) { + App().domain().accountsChanges( + ) | rpl::on_next([=] { + stopChecking(); + reevaluate(); + }, _lifetime); +} + +void ProxyRotationManager::settingsChanged() { + stopChecking(); + pruneRemovedEntries(); + reevaluate(); +} + +void ProxyRotationManager::handleConnectionStateChanged( + not_null account, + int32 state) { + (void)account; + (void)state; + reevaluate(); +} + +bool ProxyRotationManager::shouldObserve() const { + const auto &settings = App().settings().proxy(); + return settings.isEnabled() + && settings.selected() + && settings.proxyRotationEnabled() + && (settings.list().size() > 1); +} + +std::vector> ProxyRotationManager::productionAccounts() const { + auto result = std::vector>(); + for (const auto &entry : App().domain().accounts()) { + const auto account = entry.account.get(); + if (!account->sessionExists() || account->mtp().isTestMode()) { + continue; + } + result.push_back(account); + } + return result; +} + +not_null ProxyRotationManager::accountForChecks() const { + if (App().someSessionExists() + && App().activeAccount().sessionExists() + && !App().activeAccount().mtp().isTestMode()) { + return &App().activeAccount(); + } + const auto accounts = productionAccounts(); + Expects(!accounts.empty()); + return accounts.front(); +} + +auto ProxyRotationManager::find( + const MTP::ProxyData &proxy) -> Entry* { + const auto i = ranges::find( + _entries, + proxy, + [](const Entry &entry) { return entry.proxy; }); + return (i == end(_entries)) ? nullptr : &*i; +} + +auto ProxyRotationManager::ensure( + const MTP::ProxyData &proxy) -> Entry& { + if (const auto entry = find(proxy)) { + return *entry; + } + _entries.push_back({ .proxy = proxy }); + return _entries.back(); +} + +void ProxyRotationManager::reevaluate() { + if (!shouldObserve()) { + stopChecking(); + return; + } + const auto accounts = productionAccounts(); + if (accounts.empty()) { + stopChecking(); + return; + } + const auto stateProj = [](not_null account) { + return account->mtp().dcstate(); + }; + if (ranges::contains(accounts, MTP::ConnectedState, stateProj)) { + stopChecking(); + return; + } + startChecking(); +} + +void ProxyRotationManager::startChecking() { + if (_checking) { + return; + } + _checking = true; + _waitingToSwitch = false; + _switchStartedAt = crl::now(); + updateProbeOrder(); + runChecks(); + const auto timeout = App().settings().proxy().proxyRotationTimeout(); + _switchTimer.callOnce(timeout * crl::time(1000)); +} + +void ProxyRotationManager::stopChecking() { + _checkTimer.cancel(); + _switchTimer.cancel(); + _checking = false; + _waitingToSwitch = false; + _switchStartedAt = 0; + _probeOrder.clear(); + _nextCheckIndex = 0; + clearPendingChecks(); +} + +void ProxyRotationManager::pruneRemovedEntries() { + const auto &settings = App().settings().proxy(); + _entries.erase( + std::remove_if(begin(_entries), end(_entries), [&](const Entry &entry) { + return (settings.indexInList(entry.proxy) < 0); + }), + end(_entries)); +} + +void ProxyRotationManager::updateProbeOrder() { + const auto &settings = App().settings().proxy(); + const auto currentIndex = settings.indexInList(settings.selected()); + _probeOrder.clear(); + _probeOrder.reserve(settings.list().size()); + for (const auto index : settings.proxyRotationPreferredIndices()) { + if (index == currentIndex) { + continue; + } + _probeOrder.push_back(index); + } + for (auto i = 0, count = int(settings.list().size()); i != count; ++i) { + if (i == currentIndex || ranges::contains(_probeOrder, i)) { + continue; + } + _probeOrder.push_back(i); + } + _nextCheckIndex = 0; +} + +void ProxyRotationManager::continueChecking(crl::time delay) { + if (!_checking) { + return; + } + if (_checkTimer.isActive()) { + _checkTimer.cancel(); + } + _checkTimer.callOnce(delay); +} + +void ProxyRotationManager::runChecks() { + if (!_checking) { + return; + } + if (!shouldObserve()) { + stopChecking(); + return; + } + const auto accounts = productionAccounts(); + if (accounts.empty() + || ranges::contains( + accounts, + MTP::ConnectedState, + [](not_null account) { + return account->mtp().dcstate(); + })) { + stopChecking(); + return; + } + pruneExpiredChecks(); + startNextCheck(); + continueChecking(kProxyRotationCheckInterval); +} + +void ProxyRotationManager::pruneExpiredChecks() { + const auto now = crl::now(); + for (auto &entry : _entries) { + if (!entry.checking + || (now - entry.startedAt < kProxyRotationCheckLifetime)) { + continue; + } + MTP::ResetProxyCheckers(entry.v4, entry.v6); + entry.checking = false; + entry.startedAt = 0; + } +} + +void ProxyRotationManager::startNextCheck() { + if (_probeOrder.empty()) { + return; + } + if (ranges::count(_entries, true, &Entry::checking) + >= kProxyRotationMaxActiveChecks) { + return; + } + const auto &settings = App().settings().proxy(); + auto attemptsLeft = int(_probeOrder.size()); + while (attemptsLeft-- > 0) { + if (_nextCheckIndex >= int(_probeOrder.size())) { + _nextCheckIndex = 0; + } + const auto listIndex = _probeOrder[_nextCheckIndex++]; + if (listIndex < 0 || listIndex >= int(settings.list().size())) { + continue; + } + const auto &proxy = settings.list()[listIndex]; + auto &entry = ensure(proxy); + if (entry.checking) { + continue; + } + entry.checking = true; + entry.startedAt = crl::now(); + MTP::StartProxyCheck( + &accountForChecks()->mtp(), + proxy, + settings.tryIPv6(), + entry.v4, + entry.v6, + [=](MTP::details::AbstractConnection *raw, int ping) { + checkDone(proxy, raw, ping); + }, + [=](MTP::details::AbstractConnection *raw) { + checkFailed(proxy, raw); + }); + break; + } +} + +void ProxyRotationManager::switchTimerDone() { + if (!_checking || !shouldSwitchToAvailable()) { + return; + } + _waitingToSwitch = !switchToAvailable(); +} + +void ProxyRotationManager::clearPendingChecks() { + for (auto &entry : _entries) { + MTP::ResetProxyCheckers(entry.v4, entry.v6); + entry.checking = false; + entry.startedAt = 0; + } +} + +void ProxyRotationManager::checkDone( + const MTP::ProxyData &proxy, + not_null raw, + int ping) { + const auto entry = find(proxy); + if (!entry + || !entry->checking + || ((entry->v4.get() != raw) && (entry->v6.get() != raw))) { + return; + } + MTP::DropProxyChecker(entry->v4, entry->v6, raw); + MTP::ResetProxyCheckers(entry->v4, entry->v6); + entry->checking = false; + entry->startedAt = 0; + entry->availableAt = crl::now(); + const auto proxySettings = &App().settings().proxy(); + if (const auto index = proxySettings->indexInList(proxy); index >= 0) { + if (proxySettings->promoteProxyRotationPreferredIndex(index)) { + App().saveSettingsDelayed(); + } + } + updateProbeOrder(); + if (_waitingToSwitch && shouldSwitchToAvailable()) { + _waitingToSwitch = !switchToAvailable(); + } +} + +void ProxyRotationManager::checkFailed( + const MTP::ProxyData &proxy, + not_null raw) { + const auto entry = find(proxy); + if (!entry + || !entry->checking + || ((entry->v4.get() != raw) && (entry->v6.get() != raw))) { + return; + } + MTP::DropProxyChecker(entry->v4, entry->v6, raw); + if (MTP::HasProxyCheckers(entry->v4, entry->v6)) { + return; + } + entry->checking = false; + entry->startedAt = 0; +} + +bool ProxyRotationManager::switchToAvailable() { + if (!_checking) { + return false; + } + const auto &settings = App().settings().proxy(); + for (const auto index : _probeOrder) { + if (index < 0 || index >= int(settings.list().size())) { + continue; + } + const auto &proxy = settings.list()[index]; + const auto entry = find(proxy); + if (!entry || entry->checking || !entry->availableAt) { + continue; + } + if (entry->availableAt < _switchStartedAt) { + continue; + } + _waitingToSwitch = false; + App().setCurrentProxy(proxy, MTP::ProxyData::Settings::Enabled); + App().saveSettingsDelayed(); + return true; + } + return false; +} + +bool ProxyRotationManager::shouldSwitchToAvailable() const { + if (!_checking || !shouldObserve()) { + return false; + } + const auto accounts = productionAccounts(); + return !accounts.empty() + && !ranges::contains( + accounts, + MTP::ConnectedState, + [](not_null account) { + return account->mtp().dcstate(); + }); +} + +} // namespace Core diff --git a/Telegram/SourceFiles/core/proxy_rotation_manager.h b/Telegram/SourceFiles/core/proxy_rotation_manager.h new file mode 100644 index 0000000000..c1fee27696 --- /dev/null +++ b/Telegram/SourceFiles/core/proxy_rotation_manager.h @@ -0,0 +1,80 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/timer.h" +#include "mtproto/proxy_check.h" + +#include +#include + +namespace Main { +class Account; +} // namespace Main + +namespace Core { + +class ProxyRotationManager final { +public: + ProxyRotationManager(); + + void settingsChanged(); + void handleConnectionStateChanged( + not_null account, + int32 state); + +private: + struct Entry { + MTP::ProxyData proxy; + MTP::ProxyCheckConnection v4; + MTP::ProxyCheckConnection v6; + bool checking = false; + crl::time startedAt = 0; + crl::time availableAt = 0; + }; + + [[nodiscard]] bool shouldObserve() const; + [[nodiscard]] std::vector> productionAccounts() const; + [[nodiscard]] not_null accountForChecks() const; + [[nodiscard]] Entry *find(const MTP::ProxyData &proxy); + [[nodiscard]] Entry &ensure(const MTP::ProxyData &proxy); + + void reevaluate(); + void startChecking(); + void stopChecking(); + void pruneRemovedEntries(); + void updateProbeOrder(); + void continueChecking(crl::time delay); + void runChecks(); + void pruneExpiredChecks(); + void startNextCheck(); + void switchTimerDone(); + void clearPendingChecks(); + void checkDone( + const MTP::ProxyData &proxy, + not_null raw, + int ping); + void checkFailed( + const MTP::ProxyData &proxy, + not_null raw); + [[nodiscard]] bool switchToAvailable(); + [[nodiscard]] bool shouldSwitchToAvailable() const; + + base::Timer _checkTimer; + base::Timer _switchTimer; + std::vector _entries; + std::vector _probeOrder; + int _nextCheckIndex = 0; + bool _checking = false; + bool _waitingToSwitch = false; + crl::time _switchStartedAt = 0; + rpl::lifetime _lifetime; + +}; + +} // namespace Core diff --git a/Telegram/SourceFiles/main/main_account.cpp b/Telegram/SourceFiles/main/main_account.cpp index 4a2fe29ea0..2db2533187 100644 --- a/Telegram/SourceFiles/main/main_account.cpp +++ b/Telegram/SourceFiles/main/main_account.cpp @@ -462,6 +462,7 @@ void Account::startMtp(std::unique_ptr config) { _mtp->setStateChangedHandler([=](MTP::ShiftedDcId dc, int32 state) { if (dc == _mtp->mainDcId()) { Core::App().settings().proxy().connectionTypeChangesNotify(); + Core::App().checkProxyRotation(this, state); } }); _mtp->setSessionResetHandler([=](MTP::ShiftedDcId shiftedDcId) { diff --git a/Telegram/SourceFiles/mtproto/proxy_check.cpp b/Telegram/SourceFiles/mtproto/proxy_check.cpp new file mode 100644 index 0000000000..831c8453cf --- /dev/null +++ b/Telegram/SourceFiles/mtproto/proxy_check.cpp @@ -0,0 +1,111 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "mtproto/proxy_check.h" + +#include "mtproto/facade.h" +#include "mtproto/mtproto_dc_options.h" + +namespace MTP { + +using Connection = details::AbstractConnection; + +void ResetProxyCheckers( + ProxyCheckConnection &v4, + ProxyCheckConnection &v6) { + v4 = nullptr; + v6 = nullptr; +} + +void DropProxyChecker( + ProxyCheckConnection &v4, + ProxyCheckConnection &v6, + not_null raw) { + if (v4.get() == raw) { + v4 = nullptr; + } else if (v6.get() == raw) { + v6 = nullptr; + } +} + +bool HasProxyCheckers( + const ProxyCheckConnection &v4, + const ProxyCheckConnection &v6) { + return v4 || v6; +} + +void StartProxyCheck( + not_null mtproto, + const ProxyData &proxy, + bool tryIPv6, + ProxyCheckConnection &v4, + ProxyCheckConnection &v6, + Fn done, + Fn fail) { + using Variants = DcOptions::Variants; + + ResetProxyCheckers(v4, v6); + const auto connType = (proxy.type == ProxyData::Type::Http) + ? Variants::Http + : Variants::Tcp; + const auto dcId = mtproto->mainDcId(); + const auto setup = [&](ProxyCheckConnection &checker, const bytes::vector &secret) { + checker = Connection::Create( + mtproto, + connType, + QThread::currentThread(), + secret, + proxy); + const auto raw = checker.get(); + raw->connect(raw, &Connection::connected, [=] { + if (done) { + done(raw, raw->pingTime()); + } + }); + const auto failed = [=] { + if (fail) { + fail(raw); + } + }; + raw->connect(raw, &Connection::disconnected, failed); + raw->connect(raw, &Connection::error, failed); + }; + if (proxy.type == ProxyData::Type::Mtproto) { + const auto secret = proxy.secretFromMtprotoPassword(); + setup(v4, secret); + v4->connectToServer( + proxy.host, + proxy.port, + secret, + dcId, + false); + return; + } + const auto options = mtproto->dcOptions().lookup( + dcId, + DcType::Regular, + true); + const auto tryConnect = [&](ProxyCheckConnection &checker, Variants::Address address) { + const auto &list = options.data[address][connType]; + if (list.empty() || ((address == Variants::IPv6) && !tryIPv6)) { + checker = nullptr; + return; + } + const auto &endpoint = list.front(); + setup(checker, endpoint.secret); + checker->connectToServer( + QString::fromStdString(endpoint.ip), + endpoint.port, + endpoint.secret, + dcId, + false); + }; + tryConnect(v4, Variants::IPv4); + tryConnect(v6, Variants::IPv6); +} + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/proxy_check.h b/Telegram/SourceFiles/mtproto/proxy_check.h new file mode 100644 index 0000000000..1cbfb15fd2 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/proxy_check.h @@ -0,0 +1,35 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "mtproto/connection_abstract.h" + +namespace MTP { + +using ProxyCheckConnection = details::ConnectionPointer; + +void ResetProxyCheckers( + ProxyCheckConnection &v4, + ProxyCheckConnection &v6); +void DropProxyChecker( + ProxyCheckConnection &v4, + ProxyCheckConnection &v6, + not_null raw); +[[nodiscard]] bool HasProxyCheckers( + const ProxyCheckConnection &v4, + const ProxyCheckConnection &v6); +void StartProxyCheck( + not_null mtproto, + const ProxyData &proxy, + bool tryIPv6, + ProxyCheckConnection &v4, + ProxyCheckConnection &v6, + Fn done, + Fn fail); + +} // namespace MTP diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index 051436ad47..1d6796c20c 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -487,9 +487,9 @@ bool ReadSetting( proxySettings.setSettings(proxy ? MTP::ProxyData::Settings::Enabled : MTP::ProxyData::Settings::System); - proxySettings.list() = proxy - ? std::vector{ 1, proxy } - : std::vector{}; + proxySettings.setList(proxy + ? std::vector{ proxy } + : std::vector{}); Core::App().refreshGlobalProxy(); context.legacyRead = true; } break; @@ -562,16 +562,16 @@ bool ReadSetting( if (!CheckStreamStatus(stream)) { return false; } - proxySettings.list() = list; + proxySettings.setList(std::move(list)); if (connectionType == dbictProxiesListOld) { settings = static_cast( - (index > 0 && index <= list.size() + (index > 0 && index <= proxySettings.list().size() ? MTP::ProxyData::Settings::Enabled : MTP::ProxyData::Settings::System)); index = std::abs(index); } - proxySettings.setSelected((index > 0 && index <= list.size()) - ? list[index - 1] + proxySettings.setSelected((index > 0 && index <= proxySettings.list().size()) + ? proxySettings.list()[index - 1] : MTP::ProxyData()); const auto unchecked = static_cast(settings); @@ -596,14 +596,14 @@ bool ReadSetting( return false; } if (proxy) { - proxySettings.list() = { 1, proxy }; + proxySettings.setList({ proxy }); proxySettings.setSelected(proxy); proxySettings.setSettings((connectionType == dbictTcpProxy || connectionType == dbictHttpProxy) ? MTP::ProxyData::Settings::Enabled : MTP::ProxyData::Settings::System); } else { - proxySettings.list() = {}; + proxySettings.setList({}); proxySettings.setSelected(MTP::ProxyData()); proxySettings.setSettings(MTP::ProxyData::Settings::System); }