Move check_dialog_username to DialogManager.

This commit is contained in:
levlam 2024-01-08 19:14:41 +03:00
parent 466c841b4e
commit 9a90089879
7 changed files with 213 additions and 208 deletions

View File

@ -863,31 +863,6 @@ class UpdateProfileQuery final : public Td::ResultHandler {
}
};
class CheckUsernameQuery final : public Td::ResultHandler {
Promise<bool> promise_;
public:
explicit CheckUsernameQuery(Promise<bool> &&promise) : promise_(std::move(promise)) {
}
void send(const string &username) {
send_query(G()->net_query_creator().create(telegram_api::account_checkUsername(username), {{"me"}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_checkUsername>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class UpdateUsernameQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
@ -1116,45 +1091,6 @@ class UpdateEmojiStatusQuery final : public Td::ResultHandler {
}
};
class CheckChannelUsernameQuery final : public Td::ResultHandler {
Promise<bool> promise_;
ChannelId channel_id_;
string username_;
public:
explicit CheckChannelUsernameQuery(Promise<bool> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, const string &username) {
channel_id_ = channel_id;
tl_object_ptr<telegram_api::InputChannel> input_channel;
if (channel_id.is_valid()) {
input_channel = td_->contacts_manager_->get_input_channel(channel_id);
} else {
input_channel = make_tl_object<telegram_api::inputChannelEmpty>();
}
CHECK(input_channel != nullptr);
send_query(
G()->net_query_creator().create(telegram_api::channels_checkUsername(std::move(input_channel), username)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_checkUsername>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(Status status) final {
if (channel_id_.is_valid()) {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "CheckChannelUsernameQuery");
}
promise_.set_error(std::move(status));
}
};
class UpdateChannelUsernameQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
ChannelId channel_id_;
@ -5269,6 +5205,14 @@ string ContactsManager::get_channel_first_username(ChannelId channel_id) const {
return c->usernames.get_first_username();
}
string ContactsManager::get_channel_editable_username(ChannelId channel_id) const {
auto c = get_channel(channel_id);
if (c == nullptr) {
return string();
}
return c->usernames.get_editable_username();
}
UserId ContactsManager::get_secret_chat_user_id(SecretChatId secret_chat_id) const {
auto c = get_secret_chat(secret_chat_id);
if (c == nullptr) {
@ -5450,131 +5394,6 @@ ChatId ContactsManager::get_unsupported_chat_id() {
return ChatId(static_cast<int64>(G()->is_test_dc() ? 10304875 : 1535424647));
}
void ContactsManager::check_dialog_username(DialogId dialog_id, const string &username,
Promise<CheckDialogUsernameResult> &&promise) {
if (dialog_id != DialogId() && !dialog_id.is_valid()) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
switch (dialog_id.get_type()) {
case DialogType::User: {
if (dialog_id.get_user_id() != get_my_id()) {
return promise.set_error(Status::Error(400, "Can't check username for private chat with other user"));
}
break;
}
case DialogType::Channel: {
auto c = get_channel(dialog_id.get_channel_id());
if (c == nullptr) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
if (!get_channel_status(c).is_creator()) {
return promise.set_error(Status::Error(400, "Not enough rights to change username"));
}
if (username == c->usernames.get_editable_username()) {
return promise.set_value(CheckDialogUsernameResult::Ok);
}
break;
}
case DialogType::None:
break;
case DialogType::Chat:
case DialogType::SecretChat:
if (username.empty()) {
return promise.set_value(CheckDialogUsernameResult::Ok);
}
return promise.set_error(Status::Error(400, "Chat can't have username"));
default:
UNREACHABLE();
return;
}
if (username.empty()) {
return promise.set_value(CheckDialogUsernameResult::Ok);
}
if (!is_allowed_username(username) && username.size() != 4) {
return promise.set_value(CheckDialogUsernameResult::Invalid);
}
auto request_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<bool> result) mutable {
if (result.is_error()) {
auto error = result.move_as_error();
if (error.message() == "CHANNEL_PUBLIC_GROUP_NA") {
return promise.set_value(CheckDialogUsernameResult::PublicGroupsUnavailable);
}
if (error.message() == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH") {
return promise.set_value(CheckDialogUsernameResult::PublicDialogsTooMany);
}
if (error.message() == "USERNAME_INVALID") {
return promise.set_value(CheckDialogUsernameResult::Invalid);
}
if (error.message() == "USERNAME_PURCHASE_AVAILABLE") {
if (begins_with(G()->get_option_string("my_phone_number"), "1")) {
return promise.set_value(CheckDialogUsernameResult::Invalid);
}
return promise.set_value(CheckDialogUsernameResult::Purchasable);
}
return promise.set_error(std::move(error));
}
promise.set_value(result.ok() ? CheckDialogUsernameResult::Ok : CheckDialogUsernameResult::Occupied);
});
switch (dialog_id.get_type()) {
case DialogType::User:
return td_->create_handler<CheckUsernameQuery>(std::move(request_promise))->send(username);
case DialogType::Channel:
return td_->create_handler<CheckChannelUsernameQuery>(std::move(request_promise))
->send(dialog_id.get_channel_id(), username);
case DialogType::None:
return td_->create_handler<CheckChannelUsernameQuery>(std::move(request_promise))->send(ChannelId(), username);
case DialogType::Chat:
case DialogType::SecretChat:
default:
UNREACHABLE();
}
}
td_api::object_ptr<td_api::CheckChatUsernameResult> ContactsManager::get_check_chat_username_result_object(
CheckDialogUsernameResult result) {
switch (result) {
case CheckDialogUsernameResult::Ok:
return td_api::make_object<td_api::checkChatUsernameResultOk>();
case CheckDialogUsernameResult::Invalid:
return td_api::make_object<td_api::checkChatUsernameResultUsernameInvalid>();
case CheckDialogUsernameResult::Occupied:
return td_api::make_object<td_api::checkChatUsernameResultUsernameOccupied>();
case CheckDialogUsernameResult::Purchasable:
return td_api::make_object<td_api::checkChatUsernameResultUsernamePurchasable>();
case CheckDialogUsernameResult::PublicDialogsTooMany:
return td_api::make_object<td_api::checkChatUsernameResultPublicChatsTooMany>();
case CheckDialogUsernameResult::PublicGroupsUnavailable:
return td_api::make_object<td_api::checkChatUsernameResultPublicGroupsUnavailable>();
default:
UNREACHABLE();
return nullptr;
}
}
bool ContactsManager::is_allowed_username(const string &username) {
if (!is_valid_username(username)) {
return false;
}
if (username.size() < 5) {
return false;
}
auto username_lowered = to_lower(username);
if (username_lowered.find("admin") == 0 || username_lowered.find("telegram") == 0 ||
username_lowered.find("support") == 0 || username_lowered.find("security") == 0 ||
username_lowered.find("settings") == 0 || username_lowered.find("contacts") == 0 ||
username_lowered.find("service") == 0 || username_lowered.find("telegraph") == 0) {
return false;
}
return true;
}
int32 ContactsManager::get_user_was_online(const User *u, UserId user_id, int32 unix_time) const {
if (u == nullptr || u->is_deleted) {
return 0;

View File

@ -171,6 +171,7 @@ class ContactsManager final : public Actor {
string get_user_first_username(UserId user_id) const;
string get_channel_first_username(ChannelId channel_id) const;
string get_channel_editable_username(ChannelId channel_id) const;
int32 get_secret_chat_date(SecretChatId secret_chat_id) const;
int32 get_secret_chat_ttl(SecretChatId secret_chat_id) const;
@ -353,20 +354,6 @@ class ContactsManager final : public Actor {
void invalidate_user_full(UserId user_id);
enum class CheckDialogUsernameResult : uint8 {
Ok,
Invalid,
Occupied,
Purchasable,
PublicDialogsTooMany,
PublicGroupsUnavailable
};
void check_dialog_username(DialogId dialog_id, const string &username, Promise<CheckDialogUsernameResult> &&promise);
static td_api::object_ptr<td_api::CheckChatUsernameResult> get_check_chat_username_result_object(
CheckDialogUsernameResult result);
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,
@ -1387,8 +1374,6 @@ class ContactsManager final : public Actor {
void set_my_id(UserId my_id);
static bool is_allowed_username(const string &username);
void on_set_emoji_status(EmojiStatus emoji_status, Promise<Unit> &&promise);
void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name);

View File

@ -37,6 +37,70 @@
namespace td {
class CheckUsernameQuery final : public Td::ResultHandler {
Promise<bool> promise_;
public:
explicit CheckUsernameQuery(Promise<bool> &&promise) : promise_(std::move(promise)) {
}
void send(const string &username) {
send_query(G()->net_query_creator().create(telegram_api::account_checkUsername(username), {{"me"}}));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_checkUsername>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class CheckChannelUsernameQuery final : public Td::ResultHandler {
Promise<bool> promise_;
ChannelId channel_id_;
string username_;
public:
explicit CheckChannelUsernameQuery(Promise<bool> &&promise) : promise_(std::move(promise)) {
}
void send(ChannelId channel_id, const string &username) {
channel_id_ = channel_id;
telegram_api::object_ptr<telegram_api::InputChannel> input_channel;
if (channel_id.is_valid()) {
input_channel = td_->contacts_manager_->get_input_channel(channel_id);
} else {
input_channel = telegram_api::make_object<telegram_api::inputChannelEmpty>();
}
CHECK(input_channel != nullptr);
send_query(
G()->net_query_creator().create(telegram_api::channels_checkUsername(std::move(input_channel), username)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::channels_checkUsername>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(result_ptr.move_as_ok());
}
void on_error(Status status) final {
if (channel_id_.is_valid()) {
td_->contacts_manager_->on_get_channel_error(channel_id_, status, "CheckChannelUsernameQuery");
}
promise_.set_error(std::move(status));
}
};
class ResolveUsernameQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
string username_;
@ -1675,6 +1739,110 @@ void DialogManager::on_dialog_usernames_received(DialogId dialog_id, const Usern
}
}
void DialogManager::check_dialog_username(DialogId dialog_id, const string &username,
Promise<CheckDialogUsernameResult> &&promise) {
if (dialog_id != DialogId() && !have_dialog_force(dialog_id, "check_dialog_username")) {
return promise.set_error(Status::Error(400, "Chat not found"));
}
switch (dialog_id.get_type()) {
case DialogType::User: {
if (dialog_id != get_my_dialog_id()) {
return promise.set_error(Status::Error(400, "Can't check username for private chat with other user"));
}
break;
}
case DialogType::Channel: {
auto channel_id = dialog_id.get_channel_id();
if (!td_->contacts_manager_->get_channel_status(channel_id).is_creator()) {
return promise.set_error(Status::Error(400, "Not enough rights to change username"));
}
if (username == td_->contacts_manager_->get_channel_editable_username(channel_id)) {
return promise.set_value(CheckDialogUsernameResult::Ok);
}
break;
}
case DialogType::None:
break;
case DialogType::Chat:
case DialogType::SecretChat:
if (!username.empty()) {
return promise.set_error(Status::Error(400, "The chat can't have a username"));
}
break;
default:
UNREACHABLE();
return;
}
if (username.empty()) {
return promise.set_value(CheckDialogUsernameResult::Ok);
}
if (!is_allowed_username(username) && username.size() != 4) {
return promise.set_value(CheckDialogUsernameResult::Invalid);
}
auto request_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<bool> result) mutable {
if (result.is_error()) {
auto error = result.move_as_error();
if (error.message() == "CHANNEL_PUBLIC_GROUP_NA") {
return promise.set_value(CheckDialogUsernameResult::PublicGroupsUnavailable);
}
if (error.message() == "CHANNELS_ADMIN_PUBLIC_TOO_MUCH") {
return promise.set_value(CheckDialogUsernameResult::PublicDialogsTooMany);
}
if (error.message() == "USERNAME_INVALID") {
return promise.set_value(CheckDialogUsernameResult::Invalid);
}
if (error.message() == "USERNAME_PURCHASE_AVAILABLE") {
if (begins_with(G()->get_option_string("my_phone_number"), "1")) {
return promise.set_value(CheckDialogUsernameResult::Invalid);
}
return promise.set_value(CheckDialogUsernameResult::Purchasable);
}
return promise.set_error(std::move(error));
}
promise.set_value(result.ok() ? CheckDialogUsernameResult::Ok : CheckDialogUsernameResult::Occupied);
});
switch (dialog_id.get_type()) {
case DialogType::User:
return td_->create_handler<CheckUsernameQuery>(std::move(request_promise))->send(username);
case DialogType::Channel:
return td_->create_handler<CheckChannelUsernameQuery>(std::move(request_promise))
->send(dialog_id.get_channel_id(), username);
case DialogType::None:
return td_->create_handler<CheckChannelUsernameQuery>(std::move(request_promise))->send(ChannelId(), username);
case DialogType::Chat:
case DialogType::SecretChat:
default:
UNREACHABLE();
}
}
td_api::object_ptr<td_api::CheckChatUsernameResult> DialogManager::get_check_chat_username_result_object(
CheckDialogUsernameResult result) {
switch (result) {
case CheckDialogUsernameResult::Ok:
return td_api::make_object<td_api::checkChatUsernameResultOk>();
case CheckDialogUsernameResult::Invalid:
return td_api::make_object<td_api::checkChatUsernameResultUsernameInvalid>();
case CheckDialogUsernameResult::Occupied:
return td_api::make_object<td_api::checkChatUsernameResultUsernameOccupied>();
case CheckDialogUsernameResult::Purchasable:
return td_api::make_object<td_api::checkChatUsernameResultUsernamePurchasable>();
case CheckDialogUsernameResult::PublicDialogsTooMany:
return td_api::make_object<td_api::checkChatUsernameResultPublicChatsTooMany>();
case CheckDialogUsernameResult::PublicGroupsUnavailable:
return td_api::make_object<td_api::checkChatUsernameResultPublicGroupsUnavailable>();
default:
UNREACHABLE();
return nullptr;
}
}
void DialogManager::send_resolve_dialog_username_query(const string &username, Promise<Unit> &&promise) {
td_->create_handler<ResolveUsernameQuery>(std::move(promise))->send(username);
}

View File

@ -172,6 +172,19 @@ class DialogManager final : public Actor {
void on_dialog_usernames_received(DialogId dialog_id, const Usernames &usernames, bool from_database);
enum class CheckDialogUsernameResult : uint8 {
Ok,
Invalid,
Occupied,
Purchasable,
PublicDialogsTooMany,
PublicGroupsUnavailable
};
void check_dialog_username(DialogId dialog_id, const string &username, Promise<CheckDialogUsernameResult> &&promise);
static td_api::object_ptr<td_api::CheckChatUsernameResult> get_check_chat_username_result_object(
CheckDialogUsernameResult result);
void resolve_dialog(const string &username, ChannelId channel_id, Promise<DialogId> promise);
DialogId get_resolved_dialog_by_username(const string &username) const;

View File

@ -5240,14 +5240,14 @@ void Td::on_request(uint64 id, td_api::checkChatUsername &request) {
CLEAN_INPUT_STRING(request.username_);
CREATE_REQUEST_PROMISE();
auto query_promise = PromiseCreator::lambda(
[promise = std::move(promise)](Result<ContactsManager::CheckDialogUsernameResult> result) mutable {
[promise = std::move(promise)](Result<DialogManager::CheckDialogUsernameResult> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(ContactsManager::get_check_chat_username_result_object(result.ok()));
promise.set_value(DialogManager::get_check_chat_username_result_object(result.ok()));
}
});
contacts_manager_->check_dialog_username(DialogId(request.chat_id_), request.username_, std::move(query_promise));
dialog_manager_->check_dialog_username(DialogId(request.chat_id_), request.username_, std::move(query_promise));
}
void Td::on_request(uint64 id, const td_api::getCreatedPublicChats &request) {

View File

@ -268,6 +268,23 @@ bool is_valid_username(Slice username) {
return true;
}
bool is_allowed_username(Slice username) {
if (!is_valid_username(username)) {
return false;
}
if (username.size() < 5) {
return false;
}
auto username_lowered = to_lower(username);
if (username_lowered.find("admin") == 0 || username_lowered.find("telegram") == 0 ||
username_lowered.find("support") == 0 || username_lowered.find("security") == 0 ||
username_lowered.find("settings") == 0 || username_lowered.find("contacts") == 0 ||
username_lowered.find("service") == 0 || username_lowered.find("telegraph") == 0) {
return false;
}
return true;
}
int64 get_vector_hash(const vector<uint64> &numbers) {
uint64 acc = 0;
for (auto number : numbers) {

View File

@ -36,6 +36,9 @@ bool is_empty_string(const string &str) TD_WARN_UNUSED_RESULT;
// checks whether a string could be a valid username
bool is_valid_username(Slice username);
// checks whether a string can be set as a username
bool is_allowed_username(Slice username);
// calculates hash of list of uint64
int64 get_vector_hash(const vector<uint64> &numbers) TD_WARN_UNUSED_RESULT;