ZaStoGram_desktop/Telegram/SourceFiles/wallet/wallet_api.cpp
loop-uh 6ec6116e2c
All checks were successful
Desktop source guards / guards (push) Successful in 6s
Поправить сборку после слияния с Telegram Desktop v7.3.1
Сборка падала на двух ошибках компилятора. Первая: wallet_api.cpp(221) C2027, тип MTP::Instance неполный, потому что оригинал перенёс заголовок в mtproto/instance/mtp_instance.h. Я добавил этот include. Вторая: wallet_vault.cpp(1119) C2039, у MTP::AuthKey больше нет публичного data(). Гард test_mtproto_security_contracts.py запрещает публичный data(), поэтому я добавил в AuthKey friend на узкую структуру Wallet::AuthKeyAccess, определённую в wallet_vault.cpp. Она отдаёт байты ключа для HKDF. Возможность кошелька (ключ по пасскоду) сохранена. Окружение VM не трогал. Гард libs-keep проходит. Проверил только гарды, сама сборка не запускалась. В test_mtproto_security_contracts.py падает test_wss_connect_to_host_declares_relay_contract (нет Q_UNUSED(address) в wss-сокете). Я его не чинил: он не связан с моими правками, и этого файла нет в release_guards.txt.
2026-10-16 01:12:56 +03:00

227 lines
5.5 KiB
C++

/*
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 "wallet/wallet_api.h"
#include "apiwrap.h"
#include "main/main_session.h"
#include "mtproto/config/mtproto_config.h"
#include "mtproto/instance/mtp_instance.h"
namespace Wallet {
namespace {
constexpr auto kKillSessionTimeout = 10 * crl::time(1000);
constexpr auto kRequestTimeout = 30 * crl::time(1000);
const auto kTimeoutErrorMessage = u"TIMEOUT"_q;
const auto kInvalidWebFileDcError = u"WEBFILE_DC_INVALID"_q;
const auto kStreamingUrlEndpoint = u"getStreamingUrl"_q;
[[nodiscard]] MTPtoncenter_PerformApiRequest ToncenterRequest(
const Gram::HttpRequest &request) {
using Flag = MTPtoncenter_performApiRequest::Flag;
return MTPtoncenter_PerformApiRequest(
MTP_flags((request.post ? Flag::f_post : Flag())
| (request.query.isEmpty() ? Flag() : Flag::f_query)
| (request.payload.isEmpty() ? Flag() : Flag::f_payload)),
MTP_string(request.endpoint),
MTP_string(request.query),
MTP_string(QString::fromUtf8(request.payload)));
}
} // namespace
Api::Api(not_null<Main::Session*> session)
: _session(session)
, _api(&session->api().instance())
, _killSessionTimer([=] { checkIdleSession(); })
, _timeoutTimer([=] { checkTimeouts(); }) {
}
Api::~Api() = default;
bool Api::IsTimeoutError(const Gram::ApiError &error) {
return (error.message == kTimeoutErrorMessage);
}
mtpRequestId Api::request(
const Gram::HttpRequest &request,
Fn<void(const QByteArray &)> done,
Fn<void(const Gram::ApiError &)> fail) {
const auto dcId = shiftedDcId();
if (!dcId) {
if (fail) {
fail(Gram::ApiError{ .message = kInvalidWebFileDcError });
}
return 0;
}
++_pendingCount;
_killSessionTimer.cancel();
const auto id = _api.request(
ToncenterRequest(request)
).toDC(dcId).done([=](
const MTPtoncenter_ApiResponse &result,
mtpRequestId requestId) {
const auto bytes = result.data().vresponse().data().vdata().v;
const auto deliver = [=] {
if (!requestAnswered(requestId)) {
return;
}
if (const auto error = Gram::ParseApiError(bytes)) {
if (fail) {
fail(*error);
}
} else if (done) {
done(bytes);
}
};
deliver();
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
const auto deliver = [=] {
if (!requestAnswered(requestId)) {
return;
}
LOG(("Wallet Error: toncenter.%1 failed: %2"
).arg(request.endpoint, error.type()));
if (fail) {
fail(Gram::ApiError{
.code = error.code(),
.message = error.type(),
});
}
};
deliver();
}).send();
_sent.push_back({
.id = id,
.deadline = crl::now() + kRequestTimeout,
.endpoint = request.endpoint,
.fail = fail,
});
scheduleTimeoutCheck();
return id;
}
mtpRequestId Api::requestStreamingUrl(
Fn<void(const QString &url, TimeId expires)> done,
Fn<void(const Gram::ApiError &)> fail) {
const auto dcId = shiftedDcId();
if (!dcId) {
if (fail) {
fail(Gram::ApiError{ .message = kInvalidWebFileDcError });
}
return 0;
}
++_pendingCount;
_killSessionTimer.cancel();
const auto id = _api.request(
MTPtoncenter_GetStreamingUrl()
).toDC(dcId).done([=](
const MTPtoncenter_StreamingUrl &result,
mtpRequestId requestId) {
if (!requestAnswered(requestId)) {
return;
} else if (done) {
const auto &data = result.data();
done(qs(data.vurl()), data.vexpires().v);
}
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
if (!requestAnswered(requestId)) {
return;
}
LOG(("Wallet Error: toncenter.getStreamingUrl failed: %1"
).arg(error.type()));
if (fail) {
fail(Gram::ApiError{
.code = error.code(),
.message = error.type(),
});
}
}).send();
_sent.push_back({
.id = id,
.deadline = crl::now() + kRequestTimeout,
.endpoint = kStreamingUrlEndpoint,
.fail = fail,
});
scheduleTimeoutCheck();
return id;
}
void Api::cancelRequest(mtpRequestId requestId) {
_api.request(requestId).cancel();
if (requestAnswered(requestId)) {
scheduleTimeoutCheck();
}
}
bool Api::hasPendingRequests() const {
return _pendingCount > 0;
}
MTP::ShiftedDcId Api::shiftedDcId() const {
const auto dcId = _session->serverConfig().webFileDcId;
if (dcId <= 0 || dcId >= MTP::kDcShift) {
return 0;
}
return MTP::ShiftDcId(dcId, MTP::kToncenterDcShift);
}
bool Api::requestAnswered(mtpRequestId requestId) {
const auto i = ranges::find(_sent, requestId, &Sent::id);
if (i == end(_sent)) {
return false;
}
_sent.erase(i);
requestFinished();
return true;
}
void Api::requestFinished() {
if (--_pendingCount <= 0) {
_pendingCount = 0;
_killSessionTimer.callOnce(kKillSessionTimeout);
}
}
void Api::scheduleTimeoutCheck() {
if (_sent.empty()) {
return;
}
const auto now = crl::now();
const auto deadline = _sent.front().deadline;
_timeoutTimer.callOnce((deadline > now) ? (deadline - now) : crl::time(0));
}
void Api::checkTimeouts() {
const auto now = crl::now();
while (!_sent.empty() && (now >= _sent.front().deadline)) {
auto entry = std::move(_sent.front());
_sent.erase(_sent.begin());
_api.request(entry.id).cancel();
requestFinished();
LOG(("Wallet Error: toncenter %1 timed out after %2ms."
).arg(entry.endpoint).arg(kRequestTimeout));
if (entry.fail) {
entry.fail(Gram::ApiError{ .message = kTimeoutErrorMessage });
}
}
scheduleTimeoutCheck();
}
void Api::checkIdleSession() {
if (_pendingCount <= 0) {
const auto dcId = shiftedDcId();
if (dcId) {
_session->mtp().killSession(dcId);
}
}
}
} // namespace Wallet