tdesktop/Telegram/SourceFiles/mtproto/config_loader.cpp

223 lines
5.6 KiB
C++
Raw Normal View History

2017-06-26 17:38:16 +00:00
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
2017-06-26 17:38:16 +00:00
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
2017-06-26 17:38:16 +00:00
*/
#include "mtproto/config_loader.h"
#include "base/openssl_help.h"
2019-12-02 13:10:19 +00:00
#include "mtproto/special_config_request.h"
2019-11-13 08:31:12 +00:00
#include "mtproto/facade.h"
#include "mtproto/mtproto_dc_options.h"
#include "mtproto/mtproto_config.h"
2017-06-26 17:38:16 +00:00
#include "mtproto/mtp_instance.h"
#include "facades.h"
2017-06-26 17:38:16 +00:00
namespace MTP {
2019-12-02 13:10:19 +00:00
namespace details {
2017-06-26 17:38:16 +00:00
namespace {
constexpr auto kEnumerateDcTimeout = 8000; // 8 seconds timeout for help_getConfig to work (then move to other dc)
constexpr auto kSpecialRequestTimeoutMs = 6000; // 4 seconds timeout for it to work in a specially requested dc.
} // namespace
ConfigLoader::ConfigLoader(
not_null<Instance*> instance,
2018-05-02 19:27:03 +00:00
const QString &phone,
RPCDoneHandlerPtr onDone,
RPCFailHandlerPtr onFail)
: _instance(instance)
2018-05-02 19:27:03 +00:00
, _phone(phone)
, _doneHandler(onDone)
, _failHandler(onFail) {
2017-06-26 17:38:16 +00:00
_enumDCTimer.setCallback([this] { enumerate(); });
_specialEnumTimer.setCallback([this] { sendSpecialRequest(); });
}
void ConfigLoader::load() {
if (!_instance->isKeysDestroyer()) {
sendRequest(_instance->mainDcId());
_enumDCTimer.callOnce(kEnumerateDcTimeout);
} else {
auto ids = _instance->dcOptions().configEnumDcIds();
Assert(!ids.empty());
2017-06-26 17:38:16 +00:00
_enumCurrent = ids.front();
enumerate();
}
}
mtpRequestId ConfigLoader::sendRequest(ShiftedDcId shiftedDcId) {
return _instance->send(
MTPhelp_GetConfig(),
base::duplicate(_doneHandler),
base::duplicate(_failHandler),
shiftedDcId);
2017-06-26 17:38:16 +00:00
}
DcId ConfigLoader::specialToRealDcId(DcId specialDcId) {
return getTemporaryIdFromRealDcId(specialDcId);
2017-06-26 17:38:16 +00:00
}
void ConfigLoader::terminateRequest() {
if (_enumRequest) {
_instance->cancel(base::take(_enumRequest));
}
if (_enumCurrent) {
_instance->killSession(MTP::configDcId(_enumCurrent));
}
}
void ConfigLoader::terminateSpecialRequest() {
if (_specialEnumRequest) {
_instance->cancel(base::take(_specialEnumRequest));
}
if (_specialEnumCurrent) {
_instance->killSession(_specialEnumCurrent);
}
}
ConfigLoader::~ConfigLoader() {
terminateRequest();
terminateSpecialRequest();
}
void ConfigLoader::enumerate() {
terminateRequest();
if (!_enumCurrent) {
_enumCurrent = _instance->mainDcId();
}
auto ids = _instance->dcOptions().configEnumDcIds();
Assert(!ids.empty());
2017-06-26 17:38:16 +00:00
auto i = std::find(ids.cbegin(), ids.cend(), _enumCurrent);
if (i == ids.cend() || (++i) == ids.cend()) {
_enumCurrent = ids.front();
} else {
_enumCurrent = *i;
}
_enumRequest = sendRequest(MTP::configDcId(_enumCurrent));
_enumDCTimer.callOnce(kEnumerateDcTimeout);
2018-05-02 19:27:03 +00:00
refreshSpecialLoader();
2017-06-26 17:38:16 +00:00
}
2018-05-02 19:27:03 +00:00
void ConfigLoader::refreshSpecialLoader() {
if (Global::ProxySettings() == ProxyData::Settings::Enabled) {
2017-06-26 17:38:16 +00:00
_specialLoader.reset();
return;
}
2018-05-02 19:27:03 +00:00
if (!_specialLoader
|| (!_specialEnumRequest && _specialEndpoints.empty())) {
createSpecialLoader();
}
}
void ConfigLoader::setPhone(const QString &phone) {
if (_phone != phone) {
_phone = phone;
if (_specialLoader) {
createSpecialLoader();
}
2017-06-26 17:38:16 +00:00
}
}
2018-05-02 19:27:03 +00:00
void ConfigLoader::createSpecialLoader() {
_triedSpecialEndpoints.clear();
_specialLoader = std::make_unique<SpecialConfigRequest>([=](
DcId dcId,
const std::string &ip,
int port,
bytes::const_span secret) {
2019-10-02 08:14:38 +00:00
if (ip.empty()) {
_specialLoader = nullptr;
} else {
addSpecialEndpoint(dcId, ip, port, secret);
}
}, _instance->configValues().txtDomainString, _phone);
2018-05-02 19:27:03 +00:00
}
void ConfigLoader::addSpecialEndpoint(
DcId dcId,
const std::string &ip,
int port,
bytes::const_span secret) {
const auto endpoint = SpecialEndpoint {
2018-05-02 19:27:03 +00:00
dcId,
ip,
port,
bytes::make_vector(secret)
};
2017-06-26 17:38:16 +00:00
if (base::contains(_specialEndpoints, endpoint)
|| base::contains(_triedSpecialEndpoints, endpoint)) {
return;
}
DEBUG_LOG(("MTP Info: Special endpoint received, '%1:%2'").arg(ip.c_str()).arg(port));
_specialEndpoints.push_back(endpoint);
if (!_specialEnumTimer.isActive()) {
_specialEnumTimer.callOnce(1);
}
}
void ConfigLoader::sendSpecialRequest() {
terminateSpecialRequest();
if (Global::ProxySettings() == ProxyData::Settings::Enabled) {
2017-06-26 17:38:16 +00:00
_specialLoader.reset();
return;
}
if (_specialEndpoints.empty()) {
2018-05-02 19:27:03 +00:00
refreshSpecialLoader();
2017-06-26 17:38:16 +00:00
return;
}
const auto weak = base::make_weak(this);
const auto index = openssl::RandomValue<uint32>()
% _specialEndpoints.size();
const auto endpoint = _specialEndpoints.begin() + index;
2017-06-26 17:38:16 +00:00
_specialEnumCurrent = specialToRealDcId(endpoint->dcId);
using Flag = MTPDdcOption::Flag;
const auto flags = Flag::f_tcpo_only
| (endpoint->secret.empty() ? Flag(0) : Flag::f_secret);
_instance->dcOptions().constructAddOne(
_specialEnumCurrent,
flags,
endpoint->ip,
2018-04-25 09:24:48 +00:00
endpoint->port,
endpoint->secret);
_specialEnumRequest = _instance->send(
MTPhelp_GetConfig(),
rpcDone([weak](const MTPConfig &result) {
if (const auto strong = weak.get()) {
strong->specialConfigLoaded(result);
}
}),
base::duplicate(_failHandler),
_specialEnumCurrent);
2017-06-26 17:38:16 +00:00
_triedSpecialEndpoints.push_back(*endpoint);
_specialEndpoints.erase(endpoint);
_specialEnumTimer.callOnce(kSpecialRequestTimeoutMs);
}
void ConfigLoader::specialConfigLoaded(const MTPConfig &result) {
Expects(result.type() == mtpc_config);
2017-11-20 19:54:05 +00:00
const auto &data = result.c_config();
2019-07-05 13:38:38 +00:00
if (data.vdc_options().v.empty()) {
2017-06-26 17:38:16 +00:00
LOG(("MTP Error: config with empty dc_options received!"));
return;
}
// We use special config only for dc options.
// For everything else we wait for normal config from main dc.
_instance->dcOptions().setFromList(data.vdc_options());
2017-06-26 17:38:16 +00:00
}
2019-12-02 13:10:19 +00:00
} // namespace details
2017-06-26 17:38:16 +00:00
} // namespace MTP