mirror of
https://github.com/vale981/tdesktop
synced 2025-03-08 19:21:39 -05:00

Currently the build without implicitly included precompiled header is not supported anyway (because Qt MOC source files do not include stdafx.h, they include plain headers). So when we decide to support building without implicitly included precompiled headers we'll have to fix all the headers anyway.
390 lines
11 KiB
C++
390 lines
11 KiB
C++
/*
|
|
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 "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.emplace(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.emplace(idWithShift, Option(bdcsipv6[i].id, flags, bdcsipv6[i].ip, bdcsipv6[i].port));
|
|
DEBUG_LOG(("MTP Info: adding built in DC %1 IPv6 connect option: %2:%3").arg(bdcsipv6[i].id).arg(bdcsipv6[i].ip).arg(bdcsipv6[i].port));
|
|
}
|
|
}
|
|
|
|
void DcOptions::processFromList(const QVector<MTPDcOption> &options, bool overwrite) {
|
|
if (options.empty() || _immutable) {
|
|
return;
|
|
}
|
|
|
|
auto idsChanged = std::vector<DcId>();
|
|
idsChanged.reserve(options.size());
|
|
auto shiftedIdsProcessed = std::vector<ShiftedDcId>();
|
|
shiftedIdsProcessed.reserve(options.size());
|
|
{
|
|
QWriteLocker lock(&_mutex);
|
|
if (overwrite) {
|
|
idsChanged.reserve(_data.size());
|
|
}
|
|
for (auto &mtpOption : options) {
|
|
if (mtpOption.type() != mtpc_dcOption) {
|
|
LOG(("Wrong type in DcOptions: %1").arg(mtpOption.type()));
|
|
continue;
|
|
}
|
|
|
|
auto &option = mtpOption.c_dcOption();
|
|
auto dcId = option.vid.v;
|
|
auto flags = option.vflags.v;
|
|
auto dcIdWithShift = MTP::shiftDcId(dcId, flags);
|
|
if (base::contains(shiftedIdsProcessed, dcIdWithShift)) {
|
|
continue;
|
|
}
|
|
|
|
shiftedIdsProcessed.push_back(dcIdWithShift);
|
|
auto &ip = option.vip_address.c_string().v;
|
|
auto port = option.vport.v;
|
|
if (applyOneGuarded(dcId, flags, ip, port)) {
|
|
if (!base::contains(idsChanged, dcId)) {
|
|
idsChanged.push_back(dcId);
|
|
}
|
|
}
|
|
}
|
|
if (overwrite && shiftedIdsProcessed.size() < _data.size()) {
|
|
for (auto i = _data.begin(); i != _data.end();) {
|
|
if (base::contains(shiftedIdsProcessed, i->first)) {
|
|
++i;
|
|
} else {
|
|
if (!base::contains(idsChanged, i->second.id)) {
|
|
idsChanged.push_back(i->second.id);
|
|
}
|
|
i = _data.erase(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!idsChanged.empty()) {
|
|
_changed.notify(std::move(idsChanged));
|
|
}
|
|
}
|
|
|
|
void DcOptions::setFromList(const MTPVector<MTPDcOption> &options) {
|
|
processFromList(options.c_vector().v, true);
|
|
}
|
|
|
|
void DcOptions::addFromList(const MTPVector<MTPDcOption> &options) {
|
|
processFromList(options.c_vector().v, false);
|
|
}
|
|
|
|
void DcOptions::addFromOther(const DcOptions &options) {
|
|
if (this == &options || _immutable) {
|
|
return;
|
|
}
|
|
|
|
auto idsChanged = std::vector<DcId>();
|
|
{
|
|
QReadLocker lock(&options._mutex);
|
|
if (options._data.empty()) {
|
|
return;
|
|
}
|
|
|
|
idsChanged.reserve(options._data.size());
|
|
{
|
|
QWriteLocker lock(&_mutex);
|
|
for (auto &item : options._data) {
|
|
auto dcId = item.second.id;
|
|
auto flags = item.second.flags;
|
|
auto &ip = item.second.ip;
|
|
auto port = item.second.port;
|
|
if (applyOneGuarded(dcId, flags, ip, port)) {
|
|
if (!base::contains(idsChanged, dcId)) {
|
|
idsChanged.push_back(dcId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!idsChanged.empty()) {
|
|
_changed.notify(std::move(idsChanged));
|
|
}
|
|
}
|
|
|
|
void DcOptions::constructAddOne(int id, MTPDdcOption::Flags flags, const std::string &ip, int port) {
|
|
QWriteLocker lock(&_mutex);
|
|
applyOneGuarded(bareDcId(id), flags, ip, port);
|
|
}
|
|
|
|
bool DcOptions::applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std::string &ip, int port) {
|
|
auto dcIdWithShift = MTP::shiftDcId(dcId, flags);
|
|
auto i = _data.find(dcIdWithShift);
|
|
if (i != _data.cend()) {
|
|
if (i->second.ip == ip && i->second.port == port) {
|
|
return false;
|
|
}
|
|
i->second.ip = ip;
|
|
i->second.port = port;
|
|
} else {
|
|
_data.emplace(dcIdWithShift, Option(dcId, flags, ip, port));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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);
|
|
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::constructFromSerialized()"));
|
|
return;
|
|
}
|
|
QDataStream stream(&buffer);
|
|
stream.setVersion(QDataStream::Qt_5_1);
|
|
qint32 count = 0;
|
|
stream >> count;
|
|
if (stream.status() != QDataStream::Ok) {
|
|
LOG(("MTP Error: Bad data for DcOptions::constructFromSerialized()"));
|
|
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::constructFromSerialized()"));
|
|
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());
|
|
|
|
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;
|
|
}
|
|
|
|
bool DcOptions::loadFromFile(const QString &path) {
|
|
QVector<MTPDcOption> 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<MTPDcOption>(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
|