Merge remote-tracking branch 'td/master'

This commit is contained in:
Andrea Cavalli 2021-11-22 23:28:02 +01:00
commit 8e13003896
32 changed files with 1454 additions and 1269 deletions

View File

@ -280,6 +280,7 @@ set(TDLIB_SOURCE
td/mtproto/Transport.cpp
td/mtproto/utils.cpp
td/telegram/Account.cpp
td/telegram/AnimationsManager.cpp
td/telegram/AudiosManager.cpp
td/telegram/AuthManager.cpp
@ -458,6 +459,7 @@ set(TDLIB_SOURCE
td/mtproto/utils.h
td/telegram/AccessRights.h
td/telegram/Account.h
td/telegram/AffectedHistory.h
td/telegram/AnimationsManager.h
td/telegram/AudiosManager.h

View File

@ -2,18 +2,185 @@
<html>
<head>
<title>TDLight build instructions</title>
<style>
.hide { display: none; }
div.main { max-width:1200px; margin: auto; font-size: x-large; }
select.large { font-size: large; }
</style>
<title>TDLight build instructions</title>
<style>
:root {
--background: #fafafa;
--color: black;
--color-primary: #0088ff;
--color-code-block: #ebf9ff;
--color-select-border: rgb(211, 211, 211);
--color-checkbox-background: rgb(211, 211, 211);
--color-checkbox-tick: #ffffff;
--color-copy-success-background: #c1ffc6;
--color-copy-success-border: rgb(0, 255, 0);
--color-copy-fail-background: #ffcbcb;
--color-copy-fail-border: rgb(255, 0, 0);
color: var(--color);
background: var(--background);
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0e0e0e;
--color: rgb(190, 190, 190);
--color-primary: #0088ff;
--color-code-block: #101315;
--color-select-border: rgb(54, 54, 54);
--color-checkbox-background: rgb(51, 51, 51);
--color-checkbox-tick: #ffffff;
--color-copy-success-background: #001f00;
--color-copy-success-border: rgb(0, 255, 0);
--color-copy-fail-background: #1f0000;
--color-copy-fail-border: rgb(255, 0, 0);
}
}
body {
font-family: 'Segoe UI', Arial, Helvetica, sans-serif;
}
.hide {
display: none;
}
div.main {
max-width: 1250px;
padding: 25px;
margin: auto;
font-size: 16px;
}
p {
margin: 0;
}
.main > div {
margin-bottom: 20px;
}
#buildCommands {
font-family: Consolas, monospace;
margin-left: 40px;
background: var(--color-code-block);
padding: 5px;
margin-bottom: 0;
display: block;
}
#buildCommands ul {
list-style: '$ ';
}
a {
color: var(--color-primary);
text-decoration-color: transparent;
transition: text-decoration-color 200ms;
}
a:hover {
text-decoration: underline;
}
select, button {
border: 1px solid var(--color-select-border);
background-color: var(--background);
color: var(--color);
padding: 5px;
margin-top: 5px;
transition: border 200ms, padding 200ms;
border-radius: 999em;
font-size: 16px;
cursor: pointer;
}
select:focus, button:focus {
outline: none;
border-color: var(--color-primary);
border-width: 2px;
padding: 4px;
}
label * {
vertical-align: middle;
}
input[type=checkbox] {
margin-right: 5px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-color: var(--color-checkbox-background);
height: 20px;
width: 20px;
border-radius: 3px;
position: relative;
transition: background-color 200ms;
}
input[type=checkbox]::after {
content: "";
transition: border-color 200ms;
position: absolute;
left: 6px;
top: 2px;
width: 5px;
height: 10px;
border: solid transparent;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
input[type=checkbox]:checked {
background-color: var(--color-primary);
}
input[type=checkbox]:checked::after {
border-color: var(--color-checkbox-tick);
}
input[type=radio] {
margin-right: 5px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-color: var(--color-checkbox-background);
height: 20px;
width: 20px;
border-radius: 100%;
position: relative;
transition: background-color 200ms;
}
input[type=radio]::after {
content: "";
transition: border-color 200ms;
position: absolute;
left: 10px;
top: 10px;
width: 0;
height: 0;
border-radius: 100%;
background-color: transparent;
transition: width 200ms, height 200ms, left 200ms, top 200ms, background-color 100ms;
}
input[type=radio]:checked::after {
background-color: var(--color-primary);
left: 2px;
top: 2px;
width: 16px;
height: 16px;
}
#copyBuildCommandsButton {
margin-left: 40px;
}
#copyBuildCommandsButton.success {
background: var(--color-copy-success-background);
border-color: var(--color-copy-success-border);
}
#copyBuildCommandsButton.fail {
background: var(--color-copy-fail-background);
border-color: var(--color-copy-fail-border);
}
</style>
</head>
<body onload="onLoad(true)" onpopstate="onLoad(false)">
<div class="main">
<div id="languageSelectDiv" style="text-align:center;">
<div id="languageSelectDiv">
<p>Choose a programming language, from which you want to use TDLight:</p>
<select id="languageSelect" onchange="onLanguageChanged(false)" autofocus class="large">
<option>Choose a programming language:</option>
@ -44,7 +211,7 @@ select.large { font-size: large; }
</select>
</div>
<div id="osSelectDiv" class="hide" style="text-align:center;">
<div id="osSelectDiv" class="hide">
<p>Choose an operating system, on which you want to use TDLight:</p>
<select id="osSelect" onchange="onOsChanged()" class="large">
<option>Choose an operating system:</option>
@ -52,7 +219,7 @@ select.large { font-size: large; }
<p></p>
</div>
<div id="linuxSelectDiv" class="hide" style="text-align:center;">
<div id="linuxSelectDiv" class="hide">
<p>Choose a Linux distro, on which you want to use TDLight:</p>
<select id="linuxSelect" onchange="onOsChanged()" class="large">
<option>Choose a Linux distro:</option>
@ -71,7 +238,7 @@ select.large { font-size: large; }
<p></p>
</div>
<div id="buildOptionsDiv" class="hide" style="text-align:center;">
<div id="buildOptionsDiv" class="hide">
<div id="buildLtoDiv" class="hide">
<label><input type="checkbox" id="buildLtoCheckbox" onchange="onOptionsChanged()"/>Enable Link Time Optimization (requires CMake >= 3.9.0). It can significantly reduce binary size and increase performance, but sometimes it can lead to build failures.</label>
</div>
@ -133,14 +300,17 @@ select.large { font-size: large; }
<p></p>
</div>
<div id="buildTextDiv" class="hide" style="text-align:center;">
<div id="buildTextDiv" class="hide">
<p id="buildText">Hidden text</p>
</div>
<div id="buildCommandsDiv" class="hide" style="text-align:left;">
<p id="buildCommandsHeader" style="text-align:center;">Here is complete instruction for TDLight binaries building:</p>
<div id="buildCommandsDiv" class="hide">
<p id="buildCommandsHeader">Here is complete instruction for TDLight binaries building:</p>
<p id="buildPre">Hidden text</p>
<code id="buildCommands">Empty commands</code>
<button id="copyBuildCommandsButton" onclick="copyBuildInstructions()">
<span id="copyBuildCommandsText">Copy</span>
</button>
</div>
</div>
@ -543,7 +713,7 @@ function onOptionsChanged() {
pre_text.push('Note that the following instruction is for NetBSD 8.0 and default SH shell.');
}
if (os_mac) {
pre_text.push('Note that the following instruction will build TDLib only for the current architecture (x64 or Apple silicon).');
pre_text.push('Note that the following instruction will build TDLight only for the current architecture (x64 or Apple silicon).');
pre_text.push('If you want to create a universal XCFramework, take a look at our <a href="https://github.com/tdlight-team/tdlight/tree/master/example/ios">example</a> instead.');
}
@ -573,7 +743,7 @@ function onOptionsChanged() {
document.getElementById('buildPre').style.display = 'block';
if (install_dir && install_dir !== '/usr/local') {
install_dir = '../tdlib';
install_dir = '../tdlight';
if (target === 'JNI' || target === 'C++/CX') {
install_dir = '../../' + install_dir;
}
@ -903,6 +1073,26 @@ function onOptionsChanged() {
commands.push((use_powershell ? 'dir ' : 'ls -l ') + install_dir);
}
document.getElementById('buildCommands').innerHTML = '<ul><li>' + commands.join('</li><li>') + '</li></ul>';
document.getElementById('copyBuildCommandsButton').style.display = commands.includes('exit') ? 'none' : 'block';
}
function copyBuildInstructions() {
var text = document.getElementById('buildCommands').innerText;
function resetButtonState (state) {
document.getElementById('copyBuildCommandsButton').classList.remove(state);
document.getElementById('copyBuildCommandsText').innerText = "Copy";
}
navigator.clipboard.writeText(text).then(result => {
document.getElementById('copyBuildCommandsButton').classList.add('success');
document.getElementById('copyBuildCommandsText').innerText = "Copied!";
setTimeout(() => resetButtonState('success'), 5000);
}, error => {
document.getElementById('copyBuildCommandsButton').classList.add('fail');
document.getElementById('copyBuildCommandsText').innerText = "Couldn't copy :(";
setTimeout(() => resetButtonState('fail'), 5000);
})
}
</script>

View File

@ -13,8 +13,7 @@ import sys
# load shared library
tdjson_path = find_library('tdjson') or 'tdjson.dll'
if tdjson_path is None:
print('can\'t find tdjson library')
quit()
sys.exit("Can't find 'tdjson' library")
tdjson = CDLL(tdjson_path)
# load TDLib functions from shared library
@ -43,8 +42,7 @@ _td_set_log_message_callback.argtypes = [c_int, log_message_callback_type]
# initialize TDLib log with desired parameters
def on_log_message_callback(verbosity_level, message):
if verbosity_level == 0:
print('TDLib fatal error: ', message)
sys.stdout.flush()
sys.exit('TDLib fatal error: %r' % message)
def td_execute(query):
query = json.dumps(query).encode('utf-8')

View File

@ -600,7 +600,7 @@ chatInviteLinks total_count:int32 invite_links:vector<chatInviteLink> = ChatInvi
//@revoked_invite_link_count Number of revoked invite links
chatInviteLinkCount user_id:int53 invite_link_count:int32 revoked_invite_link_count:int32 = ChatInviteLinkCount;
//@description Contains a list of chat invite link counts @invite_link_counts List of invite linkcounts
//@description Contains a list of chat invite link counts @invite_link_counts List of invite link counts
chatInviteLinkCounts invite_link_counts:vector<chatInviteLinkCount> = ChatInviteLinkCounts;
//@description Describes a chat member joined a chat by an invite link @user_id User identifier @joined_chat_date Point in time (Unix timestamp) when the user joined the chat @approver_user_id User identifier of the chat administrator, approved user join request

View File

@ -301,6 +301,13 @@ std::string TD_TL_writer_cpp::get_pretty_field_name(std::string field_name) cons
}
std::string TD_TL_writer_cpp::get_pretty_class_name(std::string class_name) const {
if (tl_name != "mtproto_api") {
for (std::size_t i = 0; i < class_name.size(); i++) {
if (class_name[i] == '_') {
class_name[i] = '.';
}
}
}
return class_name;
}

371
td/telegram/Account.cpp Normal file
View File

@ -0,0 +1,371 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/Account.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/DeviceTokenManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserId.h"
#include "td/actor/actor.h"
#include "td/utils/algorithm.h"
#include "td/utils/base64.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <algorithm>
namespace td {
static td_api::object_ptr<td_api::session> convert_authorization_object(
tl_object_ptr<telegram_api::authorization> &&authorization) {
CHECK(authorization != nullptr);
return td_api::make_object<td_api::session>(
authorization->hash_, authorization->current_, authorization->password_pending_, authorization->api_id_,
authorization->app_name_, authorization->app_version_, authorization->official_app_, authorization->device_model_,
authorization->platform_, authorization->system_version_, authorization->date_created_,
authorization->date_active_, authorization->ip_, authorization->country_, authorization->region_);
}
class SetAccountTtlQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SetAccountTtlQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(int32 account_ttl) {
send_query(G()->net_query_creator().create(
telegram_api::account_setAccountTTL(make_tl_object<telegram_api::accountDaysTTL>(account_ttl))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_setAccountTTL>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
if (!result) {
return on_error(Status::Error(500, "Internal Server Error: failed to set account TTL"));
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetAccountTtlQuery final : public Td::ResultHandler {
Promise<int32> promise_;
public:
explicit GetAccountTtlQuery(Promise<int32> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_getAccountTTL()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getAccountTTL>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetAccountTtlQuery: " << to_string(ptr);
promise_.set_value(std::move(ptr->days_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class AcceptLoginTokenQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::session>> promise_;
public:
explicit AcceptLoginTokenQuery(Promise<td_api::object_ptr<td_api::session>> &&promise)
: promise_(std::move(promise)) {
}
void send(const string &login_token) {
send_query(G()->net_query_creator().create(telegram_api::auth_acceptLoginToken(BufferSlice(login_token))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::auth_acceptLoginToken>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
LOG(DEBUG) << "Receive result for AcceptLoginTokenQuery: " << to_string(result_ptr.ok());
promise_.set_value(convert_authorization_object(result_ptr.move_as_ok()));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetAuthorizationsQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::sessions>> promise_;
public:
explicit GetAuthorizationsQuery(Promise<td_api::object_ptr<td_api::sessions>> &&promise)
: promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_getAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetAuthorizationsQuery: " << to_string(ptr);
auto results =
td_api::make_object<td_api::sessions>(transform(std::move(ptr->authorizations_), convert_authorization_object));
std::sort(results->sessions_.begin(), results->sessions_.end(),
[](const td_api::object_ptr<td_api::session> &lhs, const td_api::object_ptr<td_api::session> &rhs) {
if (lhs->is_current_ != rhs->is_current_) {
return lhs->is_current_;
}
if (lhs->is_password_pending_ != rhs->is_password_pending_) {
return lhs->is_password_pending_;
}
return lhs->last_active_date_ > rhs->last_active_date_;
});
promise_.set_value(std::move(results));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetAuthorizationQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetAuthorizationQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(int64 authorization_id) {
send_query(G()->net_query_creator().create(telegram_api::account_resetAuthorization(authorization_id)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_resetAuthorization>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to terminate session";
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetAuthorizationsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetAuthorizationsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::auth_resetAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::auth_resetAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to terminate all sessions";
send_closure(td_->device_token_manager_, &DeviceTokenManager::reregister_device);
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetWebAuthorizationsQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::connectedWebsites>> promise_;
public:
explicit GetWebAuthorizationsQuery(Promise<td_api::object_ptr<td_api::connectedWebsites>> &&promise)
: promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_getWebAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getWebAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetWebAuthorizationsQuery: " << to_string(ptr);
td_->contacts_manager_->on_get_users(std::move(ptr->users_), "GetWebAuthorizationsQuery");
auto results = td_api::make_object<td_api::connectedWebsites>();
results->websites_.reserve(ptr->authorizations_.size());
for (auto &authorization : ptr->authorizations_) {
CHECK(authorization != nullptr);
UserId bot_user_id(authorization->bot_id_);
if (!bot_user_id.is_valid()) {
LOG(ERROR) << "Receive invalid bot " << bot_user_id;
bot_user_id = UserId();
}
results->websites_.push_back(td_api::make_object<td_api::connectedWebsite>(
authorization->hash_, authorization->domain_,
td_->contacts_manager_->get_user_id_object(bot_user_id, "GetWebAuthorizationsQuery"), authorization->browser_,
authorization->platform_, authorization->date_created_, authorization->date_active_, authorization->ip_,
authorization->region_));
}
promise_.set_value(std::move(results));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetWebAuthorizationQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetWebAuthorizationQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(int64 hash) {
send_query(G()->net_query_creator().create(telegram_api::account_resetWebAuthorization(hash)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_resetWebAuthorization>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to disconnect website";
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetWebAuthorizationsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetWebAuthorizationsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_resetWebAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_resetWebAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to disconnect all websites";
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
void set_account_ttl(Td *td, int32 account_ttl, Promise<Unit> &&promise) {
td->create_handler<SetAccountTtlQuery>(std::move(promise))->send(account_ttl);
}
void get_account_ttl(Td *td, Promise<int32> &&promise) {
td->create_handler<GetAccountTtlQuery>(std::move(promise))->send();
}
void confirm_qr_code_authentication(Td *td, const string &link,
Promise<td_api::object_ptr<td_api::session>> &&promise) {
Slice prefix("tg://login?token=");
if (!begins_with(to_lower(link), prefix)) {
return promise.set_error(Status::Error(400, "AUTH_TOKEN_INVALID"));
}
auto r_token = base64url_decode(Slice(link).substr(prefix.size()));
if (r_token.is_error()) {
return promise.set_error(Status::Error(400, "AUTH_TOKEN_INVALID"));
}
td->create_handler<AcceptLoginTokenQuery>(std::move(promise))->send(r_token.ok());
}
void get_active_sessions(Td *td, Promise<td_api::object_ptr<td_api::sessions>> &&promise) {
td->create_handler<GetAuthorizationsQuery>(std::move(promise))->send();
}
void terminate_session(Td *td, int64 session_id, Promise<Unit> &&promise) {
td->create_handler<ResetAuthorizationQuery>(std::move(promise))->send(session_id);
}
void terminate_all_other_sessions(Td *td, Promise<Unit> &&promise) {
td->create_handler<ResetAuthorizationsQuery>(std::move(promise))->send();
}
void get_connected_websites(Td *td, Promise<td_api::object_ptr<td_api::connectedWebsites>> &&promise) {
td->create_handler<GetWebAuthorizationsQuery>(std::move(promise))->send();
}
void disconnect_website(Td *td, int64 website_id, Promise<Unit> &&promise) {
td->create_handler<ResetWebAuthorizationQuery>(std::move(promise))->send(website_id);
}
void disconnect_all_websites(Td *td, Promise<Unit> &&promise) {
td->create_handler<ResetWebAuthorizationsQuery>(std::move(promise))->send();
}
} // namespace td

37
td/telegram/Account.h Normal file
View File

@ -0,0 +1,37 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
namespace td {
class Td;
void set_account_ttl(Td *td, int32 account_ttl, Promise<Unit> &&promise);
void get_account_ttl(Td *td, Promise<int32> &&promise);
void confirm_qr_code_authentication(Td *td, const string &link, Promise<td_api::object_ptr<td_api::session>> &&promise);
void get_active_sessions(Td *td, Promise<td_api::object_ptr<td_api::sessions>> &&promise);
void terminate_session(Td *td, int64 session_id, Promise<Unit> &&promise);
void terminate_all_other_sessions(Td *td, Promise<Unit> &&promise);
void get_connected_websites(Td *td, Promise<td_api::object_ptr<td_api::connectedWebsites>> &&promise);
void disconnect_website(Td *td, int64 website_id, Promise<Unit> &&promise);
void disconnect_all_websites(Td *td, Promise<Unit> &&promise);
} // namespace td

View File

@ -55,7 +55,6 @@
#include "td/actor/SleepActor.h"
#include "td/utils/algorithm.h"
#include "td/utils/base64.h"
#include "td/utils/buffer.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
@ -107,290 +106,6 @@ class DismissSuggestionQuery final : public Td::ResultHandler {
}
};
class SetAccountTtlQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit SetAccountTtlQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(int32 account_ttl) {
send_query(G()->net_query_creator().create(
telegram_api::account_setAccountTTL(make_tl_object<telegram_api::accountDaysTTL>(account_ttl))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_setAccountTTL>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
if (!result) {
return on_error(Status::Error(500, "Internal Server Error: failed to set account TTL"));
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetAccountTtlQuery final : public Td::ResultHandler {
Promise<int32> promise_;
public:
explicit GetAccountTtlQuery(Promise<int32> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_getAccountTTL()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getAccountTTL>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetAccountTtlQuery: " << to_string(ptr);
promise_.set_value(std::move(ptr->days_));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class AcceptLoginTokenQuery final : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::session>> promise_;
public:
explicit AcceptLoginTokenQuery(Promise<td_api::object_ptr<td_api::session>> &&promise)
: promise_(std::move(promise)) {
}
void send(const string &login_token) {
send_query(G()->net_query_creator().create(telegram_api::auth_acceptLoginToken(BufferSlice(login_token))));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::auth_acceptLoginToken>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
LOG(DEBUG) << "Receive result for AcceptLoginTokenQuery: " << to_string(result_ptr.ok());
promise_.set_value(ContactsManager::convert_authorization_object(result_ptr.move_as_ok()));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetAuthorizationsQuery final : public Td::ResultHandler {
Promise<tl_object_ptr<td_api::sessions>> promise_;
public:
explicit GetAuthorizationsQuery(Promise<tl_object_ptr<td_api::sessions>> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_getAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetAuthorizationsQuery: " << to_string(ptr);
auto results = make_tl_object<td_api::sessions>(
transform(std::move(ptr->authorizations_), ContactsManager::convert_authorization_object));
std::sort(results->sessions_.begin(), results->sessions_.end(),
[](const td_api::object_ptr<td_api::session> &lhs, const td_api::object_ptr<td_api::session> &rhs) {
if (lhs->is_current_ != rhs->is_current_) {
return lhs->is_current_;
}
if (lhs->is_password_pending_ != rhs->is_password_pending_) {
return lhs->is_password_pending_;
}
return lhs->last_active_date_ > rhs->last_active_date_;
});
promise_.set_value(std::move(results));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetAuthorizationQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetAuthorizationQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(int64 authorization_id) {
send_query(G()->net_query_creator().create(telegram_api::account_resetAuthorization(authorization_id)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_resetAuthorization>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to terminate session";
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetAuthorizationsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetAuthorizationsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::auth_resetAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::auth_resetAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to terminate all sessions";
send_closure(td_->device_token_manager_, &DeviceTokenManager::reregister_device);
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetWebAuthorizationsQuery final : public Td::ResultHandler {
Promise<tl_object_ptr<td_api::connectedWebsites>> promise_;
public:
explicit GetWebAuthorizationsQuery(Promise<tl_object_ptr<td_api::connectedWebsites>> &&promise)
: promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_getWebAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_getWebAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
LOG(INFO) << "Receive result for GetWebAuthorizationsQuery: " << to_string(ptr);
td_->contacts_manager_->on_get_users(std::move(ptr->users_), "GetWebAuthorizationsQuery");
auto results = make_tl_object<td_api::connectedWebsites>();
results->websites_.reserve(ptr->authorizations_.size());
for (auto &authorization : ptr->authorizations_) {
CHECK(authorization != nullptr);
UserId bot_user_id(authorization->bot_id_);
if (!bot_user_id.is_valid()) {
LOG(ERROR) << "Receive invalid bot " << bot_user_id;
bot_user_id = UserId();
}
results->websites_.push_back(make_tl_object<td_api::connectedWebsite>(
authorization->hash_, authorization->domain_,
td_->contacts_manager_->get_user_id_object(bot_user_id, "GetWebAuthorizationsQuery"), authorization->browser_,
authorization->platform_, authorization->date_created_, authorization->date_active_, authorization->ip_,
authorization->region_));
}
promise_.set_value(std::move(results));
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetWebAuthorizationQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetWebAuthorizationQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(int64 hash) {
send_query(G()->net_query_creator().create(telegram_api::account_resetWebAuthorization(hash)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_resetWebAuthorization>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to disconnect website";
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ResetWebAuthorizationsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ResetWebAuthorizationsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send() {
send_query(G()->net_query_creator().create(telegram_api::account_resetWebAuthorizations()));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_resetWebAuthorizations>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
bool result = result_ptr.move_as_ok();
LOG_IF(WARNING, !result) << "Failed to disconnect all websites";
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class GetContactsQuery final : public Td::ResultHandler {
public:
void send(int64 hash) {
@ -5224,61 +4939,6 @@ td_api::object_ptr<td_api::CheckChatUsernameResult> ContactsManager::get_check_c
}
}
void ContactsManager::set_account_ttl(int32 account_ttl, Promise<Unit> &&promise) const {
td_->create_handler<SetAccountTtlQuery>(std::move(promise))->send(account_ttl);
}
void ContactsManager::get_account_ttl(Promise<int32> &&promise) const {
td_->create_handler<GetAccountTtlQuery>(std::move(promise))->send();
}
td_api::object_ptr<td_api::session> ContactsManager::convert_authorization_object(
tl_object_ptr<telegram_api::authorization> &&authorization) {
CHECK(authorization != nullptr);
return td_api::make_object<td_api::session>(
authorization->hash_, authorization->current_, authorization->password_pending_, authorization->api_id_,
authorization->app_name_, authorization->app_version_, authorization->official_app_, authorization->device_model_,
authorization->platform_, authorization->system_version_, authorization->date_created_,
authorization->date_active_, authorization->ip_, authorization->country_, authorization->region_);
}
void ContactsManager::confirm_qr_code_authentication(const string &link,
Promise<td_api::object_ptr<td_api::session>> &&promise) {
Slice prefix("tg://login?token=");
if (!begins_with(to_lower(link), prefix)) {
return promise.set_error(Status::Error(400, "AUTH_TOKEN_INVALID"));
}
auto r_token = base64url_decode(Slice(link).substr(prefix.size()));
if (r_token.is_error()) {
return promise.set_error(Status::Error(400, "AUTH_TOKEN_INVALID"));
}
td_->create_handler<AcceptLoginTokenQuery>(std::move(promise))->send(r_token.ok());
}
void ContactsManager::get_active_sessions(Promise<tl_object_ptr<td_api::sessions>> &&promise) const {
td_->create_handler<GetAuthorizationsQuery>(std::move(promise))->send();
}
void ContactsManager::terminate_session(int64 session_id, Promise<Unit> &&promise) const {
td_->create_handler<ResetAuthorizationQuery>(std::move(promise))->send(session_id);
}
void ContactsManager::terminate_all_other_sessions(Promise<Unit> &&promise) const {
td_->create_handler<ResetAuthorizationsQuery>(std::move(promise))->send();
}
void ContactsManager::get_connected_websites(Promise<tl_object_ptr<td_api::connectedWebsites>> &&promise) const {
td_->create_handler<GetWebAuthorizationsQuery>(std::move(promise))->send();
}
void ContactsManager::disconnect_website(int64 website_id, Promise<Unit> &&promise) const {
td_->create_handler<ResetWebAuthorizationQuery>(std::move(promise))->send(website_id);
}
void ContactsManager::disconnect_all_websites(Promise<Unit> &&promise) const {
td_->create_handler<ResetWebAuthorizationsQuery>(std::move(promise))->send();
}
bool ContactsManager::is_valid_username(const string &username) {
if (username.size() < 5 || username.size() > 32) {
return false;

View File

@ -275,22 +275,6 @@ class ContactsManager final : public Actor {
static td_api::object_ptr<td_api::CheckChatUsernameResult> get_check_chat_username_result_object(
CheckDialogUsernameResult result);
void set_account_ttl(int32 account_ttl, Promise<Unit> &&promise) const;
void get_account_ttl(Promise<int32> &&promise) const;
static td_api::object_ptr<td_api::session> convert_authorization_object(
tl_object_ptr<telegram_api::authorization> &&authorization);
void confirm_qr_code_authentication(const string &link, Promise<td_api::object_ptr<td_api::session>> &&promise);
void get_active_sessions(Promise<tl_object_ptr<td_api::sessions>> &&promise) const;
void terminate_session(int64 session_id, Promise<Unit> &&promise) const;
void terminate_all_other_sessions(Promise<Unit> &&promise) const;
void get_connected_websites(Promise<tl_object_ptr<td_api::connectedWebsites>> &&promise) const;
void disconnect_website(int64 website_id, Promise<Unit> &&promise) const;
void disconnect_all_websites(Promise<Unit> &&promise) const;
void add_contact(Contact contact, bool share_phone_number, Promise<Unit> &&promise);
std::pair<vector<UserId>, vector<int32>> import_contacts(const vector<Contact> &contacts, int64 &random_id,

View File

@ -94,7 +94,9 @@ void parse(Document &document, ParserT &parser) {
break;
case Document::Type::Unknown:
default:
UNREACHABLE();
LOG(ERROR) << "Have invalid Document type " << static_cast<int32>(document.type);
document = Document();
return;
}
if (!document.file_id.is_valid()) {
LOG(ERROR) << "Parse invalid document.file_id";

View File

@ -2849,6 +2849,15 @@ int32 get_message_content_live_location_period(const MessageContent *content) {
}
}
bool get_message_content_poll_is_anonymous(const Td *td, const MessageContent *content) {
switch (content->get_type()) {
case MessageContentType::Poll:
return td->poll_manager_->get_poll_is_anonymous(static_cast<const MessagePoll *>(content)->poll_id);
default:
return false;
}
}
bool get_message_content_poll_is_closed(const Td *td, const MessageContent *content) {
switch (content->get_type()) {
case MessageContentType::Poll:

View File

@ -147,6 +147,8 @@ UserId get_message_content_deleted_user_id(const MessageContent *content);
int32 get_message_content_live_location_period(const MessageContent *content);
bool get_message_content_poll_is_anonymous(const Td *td, const MessageContent *content);
bool get_message_content_poll_is_closed(const Td *td, const MessageContent *content);
bool has_message_content_web_page(const MessageContent *content);

View File

@ -7054,7 +7054,7 @@ void MessagesManager::on_update_delete_scheduled_messages(DialogId dialog_id,
void MessagesManager::on_user_dialog_action(DialogId dialog_id, MessageId top_thread_message_id,
DialogId typing_dialog_id, DialogAction action, int32 date,
MessageContentType message_content_type) {
if (td_->auth_manager_->is_bot() || !typing_dialog_id.is_valid() || is_broadcast_channel(dialog_id)) {
if (td_->auth_manager_->is_bot() || !typing_dialog_id.is_valid()) {
return;
}
if (top_thread_message_id != MessageId() && !top_thread_message_id.is_valid()) {
@ -7076,6 +7076,10 @@ void MessagesManager::on_user_dialog_action(DialogId dialog_id, MessageId top_th
return;
}
if (is_broadcast_channel(dialog_id)) {
return;
}
if (typing_dialog_id.get_type() != DialogType::User) {
LOG(ERROR) << "Ignore " << action << " of " << typing_dialog_id << " in " << dialog_id;
return;
@ -17371,6 +17375,7 @@ Status MessagesManager::can_get_message_viewers(DialogId dialog_id, const Messag
if (td_->auth_manager_->is_bot()) {
return Status::Error(400, "User is bot");
}
CHECK(m != nullptr);
if (!m->is_outgoing) {
return Status::Error(400, "Can't get viewers of incoming messages");
}
@ -17407,7 +17412,7 @@ Status MessagesManager::can_get_message_viewers(DialogId dialog_id, const Messag
if (participant_count == 0) {
return Status::Error(400, "Chat is empty or have unknown number of members");
}
if (participant_count > G()->shared_config().get_option_integer("chat_read_mark_size_threshold", 50)) {
if (participant_count > G()->shared_config().get_option_integer("chat_read_mark_size_threshold", 100)) {
return Status::Error(400, "Chat is too big");
}
@ -17422,6 +17427,10 @@ Status MessagesManager::can_get_message_viewers(DialogId dialog_id, const Messag
}
CHECK(m->message_id.is_server());
if (m->content->get_type() == MessageContentType::Poll &&
get_message_content_poll_is_anonymous(td_, m->content.get())) {
}
return Status::OK();
}

View File

@ -6,7 +6,6 @@
//
#pragma once
#include "td/telegram/AuthManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/Td.h"
#include "td/telegram/td_api.h"
@ -68,14 +67,12 @@ class RequestActor : public Actor {
if (future_.is_error()) {
auto error = future_.move_as_error();
if (error == Status::Error<FutureActor<T>::HANGUP_ERROR_CODE>()) {
// dropping query due to lost authorization or lost promise
// dropping query due to closing or lost promise
if (G()->close_flag()) {
do_send_error(Global::request_aborted_error());
} else if (!td_->auth_manager_->is_authorized()) {
} else {
LOG(ERROR) << "Promise was lost";
do_send_error(Status::Error(500, "Query can't be answered due to a bug in TDLib"));
} else {
do_send_error(Status::Error(401, "Unauthorized"));
}
return stop();
}

View File

@ -6,6 +6,7 @@
//
#include "td/telegram/Td.h"
#include "td/telegram/Account.h"
#include "td/telegram/AnimationsManager.h"
#include "td/telegram/AudiosManager.h"
#include "td/telegram/AuthManager.h"
@ -4565,7 +4566,7 @@ void Td::on_request(uint64 id, td_api::checkAuthenticationBotToken &request) {
void Td::on_request(uint64 id, td_api::confirmQrCodeAuthentication &request) {
CLEAN_INPUT_STRING(request.link_);
CREATE_REQUEST_PROMISE();
contacts_manager_->confirm_qr_code_authentication(request.link_, std::move(promise));
confirm_qr_code_authentication(this, request.link_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getCurrentState &request) {
@ -4762,7 +4763,7 @@ void Td::on_request(uint64 id, const td_api::getAccountTtl &request) {
promise.set_value(td_api::make_object<td_api::accountTtl>(result.ok()));
}
});
contacts_manager_->get_account_ttl(std::move(query_promise));
get_account_ttl(this, std::move(query_promise));
}
void Td::on_request(uint64 id, const td_api::setAccountTtl &request) {
@ -4771,7 +4772,7 @@ void Td::on_request(uint64 id, const td_api::setAccountTtl &request) {
return send_error_raw(id, 400, "New account TTL must be non-empty");
}
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->set_account_ttl(request.ttl_->days_, std::move(promise));
set_account_ttl(this, request.ttl_->days_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::deleteAccount &request) {
@ -4801,37 +4802,37 @@ void Td::on_request(uint64 id, td_api::resendChangePhoneNumberCode &request) {
void Td::on_request(uint64 id, const td_api::getActiveSessions &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
contacts_manager_->get_active_sessions(std::move(promise));
get_active_sessions(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::terminateSession &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->terminate_session(request.session_id_, std::move(promise));
terminate_session(this, request.session_id_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::terminateAllOtherSessions &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->terminate_all_other_sessions(std::move(promise));
terminate_all_other_sessions(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getConnectedWebsites &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
contacts_manager_->get_connected_websites(std::move(promise));
get_connected_websites(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::disconnectWebsite &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->disconnect_website(request.website_id_, std::move(promise));
disconnect_website(this, request.website_id_, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::disconnectAllWebsites &request) {
CHECK_IS_USER();
CREATE_OK_REQUEST_PROMISE();
contacts_manager_->disconnect_all_websites(std::move(promise));
disconnect_all_websites(this, std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getMe &request) {

View File

@ -315,7 +315,7 @@ Status TdDb::init_sqlite(int32 scheduler_id, const TdParameters &parameters, con
// Get 'PRAGMA user_version'
TRY_RESULT(user_version, db.user_version());
LOG(WARNING) << "Got PRAGMA user_version = " << user_version;
LOG(INFO) << "Got PRAGMA user_version = " << user_version;
// init DialogDb
bool dialog_db_was_created = false;

View File

@ -31,6 +31,7 @@
#include "td/utils/misc.h"
#include "td/utils/NullLog.h"
#include "td/utils/OptionParser.h"
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/port/signals.h"
@ -4596,6 +4597,7 @@ static void on_log_message(int verbosity_level, const char *message) {
void main(int argc, char **argv) {
ExitGuard exit_guard;
detail::ThreadIdGuard thread_id_guard;
ignore_signal(SignalType::HangUp).ensure();
ignore_signal(SignalType::Pipe).ensure();
set_signal_handler(SignalType::Error, fail_signal).ensure();

View File

@ -143,10 +143,9 @@ bool Session::PriorityQueue::empty() const {
Session::Session(unique_ptr<Callback> callback, std::shared_ptr<AuthDataShared> shared_auth_data, int32 raw_dc_id,
int32 dc_id, bool is_main, bool use_pfs, bool is_cdn, bool need_destroy,
const mtproto::AuthKey &tmp_auth_key, const vector<mtproto::ServerSalt> &server_salts)
: raw_dc_id_(raw_dc_id), dc_id_(dc_id), is_main_(is_main), is_cdn_(is_cdn) {
VLOG(dc) << "Start connection " << tag("need_destroy", need_destroy);
need_destroy_ = need_destroy;
if (need_destroy) {
: raw_dc_id_(raw_dc_id), dc_id_(dc_id), is_main_(is_main), is_cdn_(is_cdn), need_destroy_(need_destroy) {
VLOG(dc) << "Start connection " << tag("need_destroy", need_destroy_);
if (need_destroy_) {
use_pfs = false;
CHECK(!is_cdn);
}
@ -173,8 +172,8 @@ Session::Session(unique_ptr<Callback> callback, std::shared_ptr<AuthDataShared>
callback_ = std::shared_ptr<Callback>(callback.release());
main_connection_.connection_id = 0;
long_poll_connection_.connection_id = 1;
main_connection_.connection_id_ = 0;
long_poll_connection_.connection_id_ = 1;
if (is_cdn) {
auth_data_.set_header(G()->mtproto_header().get_anonymous_header().str());
@ -244,11 +243,11 @@ void Session::connection_online_update(bool force) {
}
connection_online_flag_ = new_connection_online_flag;
VLOG(dc) << "Set connection_online " << connection_online_flag_;
if (main_connection_.connection) {
main_connection_.connection->set_online(connection_online_flag_, is_main_);
if (main_connection_.connection_) {
main_connection_.connection_->set_online(connection_online_flag_, is_main_);
}
if (long_poll_connection_.connection) {
long_poll_connection_.connection->set_online(connection_online_flag_, is_main_);
if (long_poll_connection_.connection_) {
long_poll_connection_.connection_->set_online(connection_online_flag_, is_main_);
}
}
@ -423,8 +422,8 @@ void Session::raw_event(const Event::Raw &event) {
return_query(std::move(query));
LOG(DEBUG) << "Drop answer " << tag("message_id", format::as_hex(message_id));
if (main_connection_.state == ConnectionInfo::State::Ready) {
main_connection_.connection->cancel_answer(message_id);
if (main_connection_.state_ == ConnectionInfo::State::Ready) {
main_connection_.connection_->cancel_answer(message_id);
} else {
to_cancel_.push_back(message_id);
}
@ -443,11 +442,11 @@ Status Session::on_pong() {
constexpr int MAX_QUERY_TIMEOUT = 60;
constexpr int MIN_CONNECTION_ACTIVE = 60;
if (current_info_ == &main_connection_ &&
Timestamp::at(current_info_->created_at + MIN_CONNECTION_ACTIVE).is_in_past()) {
Timestamp::at(current_info_->created_at_ + MIN_CONNECTION_ACTIVE).is_in_past()) {
Status status;
if (!unknown_queries_.empty()) {
status = Status::Error(PSLICE() << "No state info for " << unknown_queries_.size() << " queries for "
<< format::as_time(Time::now_cached() - current_info_->created_at));
<< format::as_time(Time::now_cached() - current_info_->created_at_));
}
if (!sent_queries_list_.empty()) {
for (auto it = sent_queries_list_.prev; it != &sent_queries_list_; it = it->prev) {
@ -494,16 +493,16 @@ void Session::on_closed(Status status) {
if (!close_flag_ && is_main_) {
connection_token_.reset();
}
auto raw_connection = current_info_->connection->move_as_raw_connection();
auto raw_connection = current_info_->connection_->move_as_raw_connection();
Scheduler::unsubscribe_before_close(raw_connection->get_poll_info().get_pollable_fd_ref());
raw_connection->close();
if (status.is_error()) {
LOG(WARNING) << "Session with " << sent_queries_.size() << " pending requests was closed: " << status << " "
<< current_info_->connection->get_name();
<< current_info_->connection_->get_name();
} else {
LOG(INFO) << "Session with " << sent_queries_.size() << " pending requests was closed: " << status << " "
<< current_info_->connection->get_name();
<< current_info_->connection_->get_name();
}
if (status.is_error() && status.code() == -404) {
@ -536,7 +535,7 @@ void Session::on_closed(Status status) {
// resend all queries without ack
for (auto it = sent_queries_.begin(); it != sent_queries_.end();) {
if (!it->second.ack && it->second.connection_id == current_info_->connection_id) {
if (!it->second.ack && it->second.connection_id == current_info_->connection_id_) {
// container vector leak otherwise
cleanup_container(it->first, &it->second);
@ -550,7 +549,7 @@ void Session::on_closed(Status status) {
query->set_message_id(0);
query->cancel_slot_.clear_event();
query->set_error(Status::Error(500, PSLICE() << "Session failed: " << status.message()),
current_info_->connection->get_name().str());
current_info_->connection_->get_name().str());
return_query(std::move(query));
it = sent_queries_.erase(it);
} else {
@ -562,8 +561,8 @@ void Session::on_closed(Status status) {
}
}
current_info_->connection.reset();
current_info_->state = ConnectionInfo::State::Empty;
current_info_->connection_.reset();
current_info_->state_ = ConnectionInfo::State::Empty;
}
void Session::on_session_created(uint64 unique_id, uint64 first_id) {
@ -790,21 +789,38 @@ void Session::on_message_result_error(uint64 id, int error_code, string message)
// UNAUTHORIZED
if (error_code == 401 && message != "SESSION_PASSWORD_NEEDED") {
if (auth_data_.use_pfs() && message == CSlice("AUTH_KEY_PERM_EMPTY")) {
LOG(INFO) << "Receive AUTH_KEY_PERM_EMPTY in session " << auth_data_.get_session_id() << " for auth key "
if (auth_data_.use_pfs() && (message == CSlice("AUTH_KEY_PERM_EMPTY") || !is_main_)) {
LOG(INFO) << "Receive 401, " << message << " in session " << auth_data_.get_session_id() << " for auth key "
<< auth_data_.get_tmp_auth_key().id();
// temporary key can be dropped any time
auth_data_.drop_tmp_auth_key();
on_tmp_auth_key_updated();
error_code = 500;
} else {
if (message == "USER_DEACTIVATED_BAN") {
LOG(PLAIN) << "Your account was suspended for suspicious activity. If you think that this is a mistake, please "
"write to recover@telegram.org your phone number and other details to recover the account.";
bool can_drop_main_auth_key_without_logging_out = is_cdn_;
if (!is_main_) {
CHECK(!auth_data_.use_pfs());
if (G()->net_query_dispatcher().get_main_dc_id().get_raw_id() != raw_dc_id_) {
can_drop_main_auth_key_without_logging_out = true;
}
}
if (can_drop_main_auth_key_without_logging_out) {
LOG(INFO) << "Receive 401, " << message << " in session " << auth_data_.get_session_id() << " for auth key "
<< auth_data_.get_auth_key().id();
auth_data_.drop_main_auth_key();
on_auth_key_updated();
error_code = 500;
} else {
if (message == "USER_DEACTIVATED_BAN") {
LOG(PLAIN)
<< "Your account was suspended for suspicious activity. If you think that this is a mistake, please "
"write to recover@telegram.org your phone number and other details to recover the account.";
}
auth_data_.set_auth_flag(false);
G()->shared_config().set_option_string("auth", message);
shared_auth_data_->set_auth_key(auth_data_.get_main_auth_key());
on_session_failed(Status::OK());
}
auth_data_.set_auth_flag(false);
G()->shared_config().set_option_string("auth", message);
shared_auth_data_->set_auth_key(auth_data_.get_main_auth_key());
on_session_failed(Status::OK());
}
}
@ -830,7 +846,7 @@ void Session::on_message_result_error(uint64 id, int error_code, string message)
cleanup_container(id, query_ptr);
mark_as_known(id, query_ptr);
query_ptr->query->set_error(Status::Error(error_code, message), current_info_->connection->get_name().str());
query_ptr->query->set_error(Status::Error(error_code, message), current_info_->connection_->get_name().str());
query_ptr->query->set_message_id(0);
query_ptr->query->cancel_slot_.clear_event();
return_query(std::move(query_ptr->query));
@ -922,7 +938,7 @@ void Session::on_message_info(uint64 id, int32 state, uint64 answer_id, int32 an
<< tag("answer_size", answer_size) << it->second.query;
it->second.query->debug("Session: resend answer");
}
current_info_->connection->resend_answer(answer_id);
current_info_->connection_->resend_answer(answer_id);
}
}
@ -954,7 +970,7 @@ void Session::add_query(NetQueryPtr &&net_query) {
void Session::connection_send_query(ConnectionInfo *info, NetQueryPtr &&net_query, uint64 message_id) {
net_query->debug("Session: trying to send to mtproto::connection");
CHECK(info->state == ConnectionInfo::State::Ready);
CHECK(info->state_ == ConnectionInfo::State::Ready);
current_info_ = info;
if (net_query->update_is_ready()) {
@ -975,17 +991,23 @@ void Session::connection_send_query(ConnectionInfo *info, NetQueryPtr &&net_quer
}
}
// net_query->debug("Session: send to mtproto::connection");
auto r_message_id =
info->connection->send_query(net_query->query().clone(), net_query->gzip_flag() == NetQuery::GzipFlag::On,
message_id, invoke_after_id, static_cast<bool>(net_query->quick_ack_promise_));
bool immediately_fail_query = false;
if (!immediately_fail_query) {
auto r_message_id =
info->connection_->send_query(net_query->query().clone(), net_query->gzip_flag() == NetQuery::GzipFlag::On,
message_id, invoke_after_id, static_cast<bool>(net_query->quick_ack_promise_));
net_query->on_net_write(net_query->query().size());
net_query->on_net_write(net_query->query().size());
if (r_message_id.is_error()) {
LOG(FATAL) << "Failed to send query: " << r_message_id.error();
if (r_message_id.is_error()) {
LOG(FATAL) << "Failed to send query: " << r_message_id.error();
}
message_id = r_message_id.ok();
} else {
if (message_id == 0) {
message_id = auth_data_.next_message_id(Time::now_cached());
}
}
message_id = r_message_id.ok();
VLOG(net_query) << "Send query to connection " << net_query << " [msg_id:" << format::as_hex(message_id) << "]"
<< tag("invoke_after", format::as_hex(invoke_after_id));
net_query->set_message_id(message_id);
@ -1001,24 +1023,27 @@ void Session::connection_send_query(ConnectionInfo *info, NetQueryPtr &&net_quer
net_query->cancel_slot_.set_event(EventCreator::raw(actor_id(), message_id));
}
auto status = sent_queries_.emplace(
message_id, Query{message_id, std::move(net_query), main_connection_.connection_id, Time::now_cached()});
message_id, Query{message_id, std::move(net_query), main_connection_.connection_id_, Time::now_cached()});
sent_queries_list_.put(status.first->second.get_list_node());
if (!status.second) {
LOG(FATAL) << "Duplicate message_id [message_id = " << message_id << "]";
}
if (immediately_fail_query) {
on_message_result_error(message_id, 401, "TEST_ERROR");
}
}
void Session::connection_open(ConnectionInfo *info, bool ask_info) {
CHECK(info->state == ConnectionInfo::State::Empty);
CHECK(info->state_ == ConnectionInfo::State::Empty);
if (!network_flag_) {
return;
}
if (!auth_data_.has_auth_key(Time::now_cached())) {
return;
}
info->ask_info = ask_info;
info->ask_info_ = ask_info;
info->state = ConnectionInfo::State::Connecting;
info->state_ = ConnectionInfo::State::Connecting;
info->cancellation_token_source_ = CancellationTokenSource{};
// NB: rely on constant location of info
auto promise = PromiseCreator::cancellable_lambda(
@ -1039,7 +1064,7 @@ void Session::connection_open(ConnectionInfo *info, bool ask_info) {
callback_->request_raw_connection(std::move(auth_data), std::move(promise));
}
info->wakeup_at = Time::now_cached() + 1000;
info->wakeup_at_ = Time::now_cached() + 1000;
}
void Session::connection_add(unique_ptr<mtproto::RawConnection> raw_connection) {
@ -1049,10 +1074,10 @@ void Session::connection_add(unique_ptr<mtproto::RawConnection> raw_connection)
}
void Session::connection_check_mode(ConnectionInfo *info) {
if (close_flag_ || info->state != ConnectionInfo::State::Ready) {
if (close_flag_ || info->state_ != ConnectionInfo::State::Ready) {
return;
}
if (info->mode != mode_) {
if (info->mode_ != mode_) {
LOG(WARNING) << "Close connection because of outdated mode_";
connection_close(info);
}
@ -1060,14 +1085,14 @@ void Session::connection_check_mode(ConnectionInfo *info) {
void Session::connection_open_finish(ConnectionInfo *info,
Result<unique_ptr<mtproto::RawConnection>> r_raw_connection) {
if (close_flag_ || info->state != ConnectionInfo::State::Connecting) {
if (close_flag_ || info->state_ != ConnectionInfo::State::Connecting) {
VLOG(dc) << "Ignore raw connection while closing";
return;
}
current_info_ = info;
if (r_raw_connection.is_error()) {
LOG(WARNING) << "Failed to open socket: " << r_raw_connection.error();
info->state = ConnectionInfo::State::Empty;
info->state_ = ConnectionInfo::State::Empty;
yield();
return;
}
@ -1076,7 +1101,7 @@ void Session::connection_open_finish(ConnectionInfo *info,
VLOG(dc) << "Receive raw connection " << raw_connection.get();
if (raw_connection->extra().extra != network_generation_) {
LOG(WARNING) << "Got RawConnection with old network_generation";
info->state = ConnectionInfo::State::Empty;
info->state_ = ConnectionInfo::State::Empty;
yield();
return;
}
@ -1086,10 +1111,10 @@ void Session::connection_open_finish(ConnectionInfo *info,
if (mode_ != expected_mode) {
VLOG(dc) << "Change mode " << mode_ << "--->" << expected_mode;
mode_ = expected_mode;
if (info->connection_id == 1 && mode_ != Mode::Http) {
if (info->connection_id_ == 1 && mode_ != Mode::Http) {
LOG(WARNING) << "Got tcp connection for long poll connection";
connection_add(std::move(raw_connection));
info->state = ConnectionInfo::State::Empty;
info->state_ = ConnectionInfo::State::Empty;
yield();
return;
}
@ -1101,7 +1126,7 @@ void Session::connection_open_finish(ConnectionInfo *info,
mode = mtproto::SessionConnection::Mode::Tcp;
mode_name = Slice("Tcp");
} else {
if (info->connection_id == 0) {
if (info->connection_id_ == 0) {
mode = mtproto::SessionConnection::Mode::Http;
mode_name = Slice("Http");
} else {
@ -1111,28 +1136,28 @@ void Session::connection_open_finish(ConnectionInfo *info,
}
auto name = PSTRING() << get_name() << "::Connect::" << mode_name << "::" << raw_connection->extra().debug_str;
LOG(INFO) << "Finished to open connection " << name;
info->connection = make_unique<mtproto::SessionConnection>(mode, std::move(raw_connection), &auth_data_);
info->connection_ = make_unique<mtproto::SessionConnection>(mode, std::move(raw_connection), &auth_data_);
if (can_destroy_auth_key()) {
info->connection->destroy_key();
info->connection_->destroy_key();
}
info->connection->set_online(connection_online_flag_, is_main_);
info->connection->set_name(name);
Scheduler::subscribe(info->connection->get_poll_info().extract_pollable_fd(this));
info->mode = mode_;
info->state = ConnectionInfo::State::Ready;
info->created_at = Time::now_cached();
info->wakeup_at = Time::now_cached() + 10;
info->connection_->set_online(connection_online_flag_, is_main_);
info->connection_->set_name(name);
Scheduler::subscribe(info->connection_->get_poll_info().extract_pollable_fd(this));
info->mode_ = mode_;
info->state_ = ConnectionInfo::State::Ready;
info->created_at_ = Time::now_cached();
info->wakeup_at_ = Time::now_cached() + 10;
if (unknown_queries_.size() > MAX_INFLIGHT_QUERIES) {
LOG(ERROR) << "With current limits `Too much queries with unknown state` error must be impossible";
on_session_failed(Status::Error("Too much queries with unknown state"));
return;
}
if (info->ask_info) {
if (info->ask_info_) {
for (auto &id : unknown_queries_) {
info->connection->get_state_info(id);
info->connection_->get_state_info(id);
}
for (auto &id : to_cancel_) {
info->connection->cancel_answer(id);
info->connection_->cancel_answer(id);
}
to_cancel_.clear();
}
@ -1140,18 +1165,18 @@ void Session::connection_open_finish(ConnectionInfo *info,
}
void Session::connection_flush(ConnectionInfo *info) {
CHECK(info->state == ConnectionInfo::State::Ready);
CHECK(info->state_ == ConnectionInfo::State::Ready);
current_info_ = info;
info->wakeup_at = info->connection->flush(static_cast<mtproto::SessionConnection::Callback *>(this));
info->wakeup_at_ = info->connection_->flush(static_cast<mtproto::SessionConnection::Callback *>(this));
}
void Session::connection_close(ConnectionInfo *info) {
current_info_ = info;
if (info->state != ConnectionInfo::State::Ready) {
if (info->state_ != ConnectionInfo::State::Ready) {
return;
}
info->connection->force_close(static_cast<mtproto::SessionConnection::Callback *>(this));
CHECK(info->state == ConnectionInfo::State::Empty);
info->connection_->force_close(static_cast<mtproto::SessionConnection::Callback *>(this));
CHECK(info->state_ == ConnectionInfo::State::Empty);
}
bool Session::need_send_check_main_key() const {
@ -1166,7 +1191,7 @@ bool Session::connection_send_check_main_key(ConnectionInfo *info) {
if (key_id == being_checked_main_auth_key_id_) {
return false;
}
CHECK(info->state != ConnectionInfo::State::Empty);
CHECK(info->state_ != ConnectionInfo::State::Empty);
LOG(INFO) << "Check main key";
being_checked_main_auth_key_id_ = key_id;
last_check_query_id_ = UniqueId::next(UniqueId::BindKey);
@ -1190,7 +1215,7 @@ bool Session::need_send_query() const {
}
bool Session::connection_send_bind_key(ConnectionInfo *info) {
CHECK(info->state != ConnectionInfo::State::Empty);
CHECK(info->state_ != ConnectionInfo::State::Empty);
uint64 key_id = auth_data_.get_tmp_auth_key().id();
if (key_id == being_binded_tmp_auth_key_id_) {
return false;
@ -1203,7 +1228,7 @@ bool Session::connection_send_bind_key(ConnectionInfo *info) {
auto expires_at = static_cast<int32>(auth_data_.get_server_time(auth_data_.get_tmp_auth_key().expires_at()));
int64 message_id;
BufferSlice encrypted;
std::tie(message_id, encrypted) = info->connection->encrypted_bind(perm_auth_key_id, nonce, expires_at);
std::tie(message_id, encrypted) = info->connection_->encrypted_bind(perm_auth_key_id, nonce, expires_at);
LOG(INFO) << "Bind key: " << tag("tmp", key_id) << tag("perm", static_cast<uint64>(perm_auth_key_id));
NetQueryPtr query = G()->net_query_creator().create(
@ -1342,8 +1367,8 @@ void Session::loop() {
connection_online_update();
double wakeup_at = 0;
main_connection_.wakeup_at = 0;
long_poll_connection_.wakeup_at = 0;
main_connection_.wakeup_at_ = 0;
long_poll_connection_.wakeup_at_ = 0;
// NB: order is crucial. First long_poll_connection, then main_connection
// Otherwise queries could be sent with big delay
@ -1351,20 +1376,20 @@ void Session::loop() {
connection_check_mode(&main_connection_);
connection_check_mode(&long_poll_connection_);
if (mode_ == Mode::Http) {
if (long_poll_connection_.state == ConnectionInfo::State::Ready) {
if (long_poll_connection_.state_ == ConnectionInfo::State::Ready) {
connection_flush(&long_poll_connection_);
}
if (!close_flag_ && long_poll_connection_.state == ConnectionInfo::State::Empty) {
if (!close_flag_ && long_poll_connection_.state_ == ConnectionInfo::State::Empty) {
connection_open(&long_poll_connection_);
}
relax_timeout_at(&wakeup_at, long_poll_connection_.wakeup_at);
relax_timeout_at(&wakeup_at, long_poll_connection_.wakeup_at_);
}
if (main_connection_.state == ConnectionInfo::State::Ready) {
if (main_connection_.state_ == ConnectionInfo::State::Ready) {
// do not send queries before we have key and e.t.c
// do not send queries before tmp_key is bound
bool need_flush = true;
while (main_connection_.state == ConnectionInfo::State::Ready) {
while (main_connection_.state_ == ConnectionInfo::State::Ready) {
if (auth_data_.is_ready(Time::now_cached())) {
if (need_send_query()) {
while (!pending_queries_.empty() && sent_queries_.size() < MAX_INFLIGHT_QUERIES) {
@ -1391,11 +1416,11 @@ void Session::loop() {
}
}
}
if (!close_flag_ && main_connection_.state == ConnectionInfo::State::Empty) {
if (!close_flag_ && main_connection_.state_ == ConnectionInfo::State::Empty) {
connection_open(&main_connection_, true /*send ask_info*/);
}
relax_timeout_at(&wakeup_at, main_connection_.wakeup_at);
relax_timeout_at(&wakeup_at, main_connection_.wakeup_at_);
double wakeup_in = 0;
if (wakeup_at != 0) {

View File

@ -102,17 +102,17 @@ class Session final
// Just re-ask answer_id each time we get information about it.
// Though mtproto::Connection must ensure delivery of such query.
int32 raw_dc_id_;
int32 dc_id_;
int32 raw_dc_id_; // numerical datacenter ID, i.e. 2
int32 dc_id_; // unique datacenter ID, i.e. -10002
enum class Mode : int8 { Tcp, Http } mode_ = Mode::Tcp;
bool is_main_;
bool is_main_; // true only for the primary Session(s) to the main DC
bool is_cdn_;
bool need_destroy_;
bool was_on_network_ = false;
bool network_flag_ = false;
uint32 network_generation_ = 0;
bool online_flag_ = false;
bool connection_online_flag_ = false;
uint32 network_generation_ = 0;
uint64 being_binded_tmp_auth_key_id_ = 0;
uint64 being_checked_main_auth_key_id_ = 0;
uint64 last_bind_query_id_ = 0;
@ -140,14 +140,14 @@ class Session final
ListNode sent_queries_list_;
struct ConnectionInfo {
int8 connection_id = 0;
Mode mode = Mode::Tcp;
enum class State : int8 { Empty, Connecting, Ready } state = State::Empty;
int8 connection_id_ = 0;
Mode mode_ = Mode::Tcp;
enum class State : int8 { Empty, Connecting, Ready } state_ = State::Empty;
CancellationTokenSource cancellation_token_source_;
unique_ptr<mtproto::SessionConnection> connection;
bool ask_info;
double wakeup_at = 0;
double created_at = 0;
unique_ptr<mtproto::SessionConnection> connection_;
bool ask_info_ = false;
double wakeup_at_ = 0;
double created_at_ = 0;
};
ConnectionInfo *current_info_;

View File

@ -91,7 +91,6 @@ class TimeoutManager final : public td::Actor {
td::int32 TimeoutManager::count;
TEST(MultiTimeout, Destroy) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
td::ConcurrentScheduler sched;
int threads_n = 0;
sched.init(threads_n);

View File

@ -244,7 +244,7 @@ optional<int32> SqliteDb::get_cipher_version() const {
Result<SqliteDb> SqliteDb::change_key(CSlice path, bool allow_creation, const DbKey &new_db_key,
const DbKey &old_db_key) {
PerfWarningTimer perf("change key", 0.001);
PerfWarningTimer perf("change key", 0.05);
// fast path
{

View File

@ -8,12 +8,73 @@
#include "td/utils/port/thread_local.h"
namespace td {
#include <array>
#include <cstdlib>
StackAllocator::Impl &StackAllocator::impl() {
static TD_THREAD_LOCAL StackAllocator::Impl *impl; // static zero-initialized
init_thread_local<Impl>(impl);
return *impl;
namespace td {
namespace {
class ArrayAllocator final : public StackAllocator::AllocatorImpl {
static const size_t MEM_SIZE = 1024 * 1024;
std::array<char, MEM_SIZE> mem;
size_t pos{0};
MutableSlice allocate(size_t size) final {
if (size > MEM_SIZE) {
std::abort(); // too much memory requested
}
char *res = mem.data() + pos;
pos += (size + 7) & -8;
if (pos > MEM_SIZE) {
std::abort(); // memory is over
}
return {res, size};
}
void free_ptr(char *ptr, size_t size) final {
size = (size + 7) & -8;
if (size > pos || ptr != mem.data() + (pos - size)) {
std::abort(); // shouldn't happen
}
pos -= size;
}
public:
~ArrayAllocator() final {
if (pos != 0) {
std::abort(); // shouldn't happen
}
}
};
class NewAllocator final : public StackAllocator::AllocatorImpl {
MutableSlice allocate(size_t size) final {
return {new char[size], size};
}
void free_ptr(char *ptr, size_t size) final {
delete[] ptr;
}
public:
~NewAllocator() final = default;
};
} // namespace
StackAllocator::Ptr::~Ptr() {
if (!slice_.empty()) {
allocator_->free_ptr(slice_.data(), slice_.size());
}
}
StackAllocator::AllocatorImpl *StackAllocator::impl() {
if (get_thread_id() != 0) {
static TD_THREAD_LOCAL ArrayAllocator *array_allocator; // static zero-initialized
init_thread_local<ArrayAllocator>(array_allocator);
return array_allocator;
} else {
static NewAllocator new_allocator;
return &new_allocator;
}
}
} // namespace td

View File

@ -7,76 +7,54 @@
#pragma once
#include "td/utils/common.h"
#include "td/utils/MovableValue.h"
#include "td/utils/Slice.h"
#include <array>
#include <cstdlib>
#include <memory>
namespace td {
class StackAllocator {
class Deleter {
public:
class AllocatorImpl {
public:
void operator()(char *ptr) {
free_ptr(ptr);
}
AllocatorImpl() = default;
AllocatorImpl(const AllocatorImpl &) = delete;
AllocatorImpl &operator=(const AllocatorImpl &) = delete;
AllocatorImpl(AllocatorImpl &&) = delete;
AllocatorImpl &operator=(AllocatorImpl &&) = delete;
virtual ~AllocatorImpl() = default;
virtual MutableSlice allocate(size_t size) = 0;
virtual void free_ptr(char *ptr, size_t size) = 0;
};
// TODO: alloc memory with mmap and unload unused pages
// memory still can be corrupted, but it is better than explicit free function
// TODO: use pointer that can't be even copied
using PtrImpl = std::unique_ptr<char, Deleter>;
private:
class Ptr {
public:
Ptr(char *ptr, size_t size) : ptr_(ptr), size_(size) {
Ptr(AllocatorImpl *allocator, size_t size) : allocator_(allocator), slice_(allocator_->allocate(size)) {
}
Ptr(const Ptr &other) = delete;
Ptr &operator=(const Ptr &other) = delete;
Ptr(Ptr &&other) noexcept : allocator_(other.allocator_), slice_(other.slice_) {
other.allocator_ = nullptr;
other.slice_ = MutableSlice();
}
Ptr &operator=(Ptr &&other) = delete;
~Ptr();
MutableSlice as_slice() const {
return MutableSlice(ptr_.get(), size_.get());
return slice_;
}
private:
PtrImpl ptr_;
MovableValue<size_t> size_;
AllocatorImpl *allocator_;
MutableSlice slice_;
};
static void free_ptr(char *ptr) {
impl().free_ptr(ptr);
}
struct Impl {
static const size_t MEM_SIZE = 1024 * 1024;
std::array<char, MEM_SIZE> mem;
size_t pos{0};
char *alloc(size_t size) {
if (size == 0) {
size = 1;
}
char *res = mem.data() + pos;
size = (size + 7) & -8;
pos += size;
if (pos > MEM_SIZE) {
std::abort(); // memory is over
}
return res;
}
void free_ptr(char *ptr) {
size_t new_pos = ptr - mem.data();
if (new_pos >= pos) {
std::abort(); // shouldn't happen
}
pos = new_pos;
}
};
static Impl &impl();
static AllocatorImpl *impl();
public:
static Ptr alloc(size_t size) {
return Ptr(impl().alloc(size), size);
return Ptr(impl(), size);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,7 @@
#include "td/utils/StringBuilder.h"
#include "td/utils/tests.h"
#include "td/utils/Time.h"
#include "td/utils/tl_helpers.h"
#include "td/utils/translit.h"
#include "td/utils/uint128.h"
#include "td/utils/unicode.h"
@ -68,11 +69,9 @@ struct CheckExitGuard {
static CheckExitGuard check_exit_guard_true{true};
static td::ExitGuard exit_guard;
static CheckExitGuard check_exit_guard_false{false};
#if TD_LINUX || TD_DARWIN
TEST(Misc, update_atime_saves_mtime) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
td::string name = "test_file";
td::unlink(name).ignore();
auto r_file = td::FileFd::open(name, td::FileFd::Read | td::FileFd::Flags::Create | td::FileFd::Flags::Truncate);
@ -102,7 +101,6 @@ TEST(Misc, update_atime_saves_mtime) {
}
TEST(Misc, update_atime_change_atime) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
td::string name = "test_file";
td::unlink(name).ignore();
auto r_file = td::FileFd::open(name, td::FileFd::Read | td::FileFd::Flags::Create | td::FileFd::Flags::Truncate);
@ -1232,4 +1230,17 @@ TEST(Misc, is_emoji) {
ASSERT_TRUE(!td::is_emoji(" "));
ASSERT_TRUE(!td::is_emoji(""));
ASSERT_TRUE(!td::is_emoji("1234567890123456789012345678901234567890123456789012345678901234567890"));
ASSERT_TRUE(td::is_emoji("❤️"));
ASSERT_TRUE(td::is_emoji(""));
}
TEST(Misc, serialize) {
td::int32 x = 1;
ASSERT_EQ(td::base64_encode(td::serialize(x)), td::base64_encode(td::string("\x01\x00\x00\x00", 4)));
td::int64 y = -2;
ASSERT_EQ(td::base64_encode(td::serialize(y)), td::base64_encode(td::string("\xfe\xff\xff\xff\xff\xff\xff\xff", 8)));
}
TEST(Misc, check_reset_guard) {
CheckExitGuard check_exit_guard{false};
}

View File

@ -11,6 +11,7 @@
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/logging.h"
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
@ -274,6 +275,7 @@ static HandshakeTest pregenerated_test() {
}
int main() {
td::detail::ThreadIdGuard thread_id_guard;
auto test = gen_test();
run_test(test);
run_test(pregenerated_test());

View File

@ -340,7 +340,6 @@ class BaselineKV {
};
TEST(DB, key_value) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
std::vector<std::string> keys;
std::vector<std::string> values;
@ -517,7 +516,6 @@ TEST(DB, persistent_key_value) {
using KeyValue = BinlogKeyValue<ConcurrentBinlog>;
// using KeyValue = PersistentKeyValue;
// using KeyValue = SqliteKV;
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
std::vector<std::string> keys;
std::vector<std::string> values;
CSlice path = "test_pmc";

View File

@ -140,7 +140,6 @@ TEST(Http, reader) {
return;
#endif
clear_thread_locals();
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
auto start_mem = BufferAllocator::get_buffer_mem();
auto start_size = BufferAllocator::get_buffer_slice_size();
{

View File

@ -6,8 +6,11 @@
//
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/ExitGuard.h"
#include "td/utils/logging.h"
#include "td/utils/OptionParser.h"
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/port/stacktrace.h"
#include "td/utils/Slice.h"
#include "td/utils/tests.h"
#include "td/telegram/Log.h"
@ -17,21 +20,34 @@
#endif
int main(int argc, char **argv) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL));
td::ExitGuard exit_guard;
td::detail::ThreadIdGuard thread_id_guard;
td::Stacktrace::init();
td::init_openssl_threads();
td::TestsRunner &runner = td::TestsRunner::get_default();
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
int default_verbosity_level = 1;
td::OptionParser options;
options.add_option('f', "filter", "Run only specified tests",
options.add_option('f', "filter", "run only specified tests",
[&](td::Slice filter) { runner.add_substr_filter(filter.str()); });
options.add_option('s', "stress", "Run tests infinitely", [&] { runner.set_stress_flag(true); });
options.add_option('s', "stress", "run tests infinitely", [&] { runner.set_stress_flag(true); });
options.add_checked_option('v', "verbosity", "log verbosity level",
td::OptionParser::parse_integer(default_verbosity_level));
options.add_check([&] {
if (default_verbosity_level < 0) {
return td::Status::Error("Wrong verbosity level specified");
}
return td::Status::OK();
});
auto r_non_options = options.run(argc, argv, 0);
if (r_non_options.is_error()) {
LOG(PLAIN) << argv[0] << ": " << r_non_options.error().message();
LOG(PLAIN) << options;
return 1;
}
SET_VERBOSITY_LEVEL(default_verbosity_level);
#if TD_EMSCRIPTEN
emscripten_set_main_loop(

View File

@ -49,7 +49,6 @@
using namespace td;
TEST(Mtproto, GetHostByNameActor) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
ConcurrentScheduler sched;
int threads_n = 1;
sched.init(threads_n);
@ -667,7 +666,6 @@ TEST(Mtproto, Grease) {
}
TEST(Mtproto, TlsTransport) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
ConcurrentScheduler sched;
int threads_n = 1;
sched.init(threads_n);

View File

@ -1001,7 +1001,6 @@ void FakeSecretChatContext::on_read_message(int64, Promise<> promise) {
TEST(Secret, go) {
return;
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
ConcurrentScheduler sched;
int threads_n = 0;
sched.init(threads_n);

View File

@ -828,7 +828,6 @@ class Tdclient_login final : public td::Test {
using Test::Test;
bool step() final {
if (!is_inited_) {
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(DEBUG) + 2);
sched_.init(4);
sched_.create_actor_unsafe<LoginTestActor>(0, "LoginTestActor", &result_).release();
sched_.start();
@ -936,7 +935,7 @@ TEST(Client, Multi) {
TEST(Client, Manager) {
td::vector<td::thread> threads;
td::ClientManager client;
#if !TD_EVENTFD_UNSUPPORTED // Client must be used from a single thread if there is no EventFd
#if !TD_EVENTFD_UNSUPPORTED // Client must be used from a single thread if there is no EventFd
int threads_n = 4;
#else
int threads_n = 1;
@ -973,7 +972,7 @@ TEST(Client, Manager) {
}
}
#if !TD_EVENTFD_UNSUPPORTED // Client must be used from a single thread if there is no EventFd
#if !TD_EVENTFD_UNSUPPORTED // Client must be used from a single thread if there is no EventFd
TEST(Client, Close) {
std::atomic<bool> stop_send{false};
std::atomic<bool> can_stop_receive{false};