diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 47ec6a2e2..97692dea0 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -863,31 +863,6 @@ class UpdateProfileQuery final : public Td::ResultHandler { } }; -class CheckUsernameQuery final : public Td::ResultHandler { - Promise promise_; - - public: - explicit CheckUsernameQuery(Promise &&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(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 promise_; @@ -1116,45 +1091,6 @@ class UpdateEmojiStatusQuery final : public Td::ResultHandler { } }; -class CheckChannelUsernameQuery final : public Td::ResultHandler { - Promise promise_; - ChannelId channel_id_; - string username_; - - public: - explicit CheckChannelUsernameQuery(Promise &&promise) : promise_(std::move(promise)) { - } - - void send(ChannelId channel_id, const string &username) { - channel_id_ = channel_id; - tl_object_ptr input_channel; - if (channel_id.is_valid()) { - input_channel = td_->contacts_manager_->get_input_channel(channel_id); - } else { - input_channel = make_tl_object(); - } - 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(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 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(G()->is_test_dc() ? 10304875 : 1535424647)); } -void ContactsManager::check_dialog_username(DialogId dialog_id, const string &username, - Promise &&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 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(std::move(request_promise))->send(username); - case DialogType::Channel: - return td_->create_handler(std::move(request_promise)) - ->send(dialog_id.get_channel_id(), username); - case DialogType::None: - return td_->create_handler(std::move(request_promise))->send(ChannelId(), username); - case DialogType::Chat: - case DialogType::SecretChat: - default: - UNREACHABLE(); - } -} - -td_api::object_ptr ContactsManager::get_check_chat_username_result_object( - CheckDialogUsernameResult result) { - switch (result) { - case CheckDialogUsernameResult::Ok: - return td_api::make_object(); - case CheckDialogUsernameResult::Invalid: - return td_api::make_object(); - case CheckDialogUsernameResult::Occupied: - return td_api::make_object(); - case CheckDialogUsernameResult::Purchasable: - return td_api::make_object(); - case CheckDialogUsernameResult::PublicDialogsTooMany: - return td_api::make_object(); - case CheckDialogUsernameResult::PublicGroupsUnavailable: - return td_api::make_object(); - 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; diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 7b5e8decb..304829d4e 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -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 &&promise); - - static td_api::object_ptr get_check_chat_username_result_object( - CheckDialogUsernameResult result); - void add_contact(Contact contact, bool share_phone_number, Promise &&promise); std::pair, vector> import_contacts(const vector &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 &&promise); void on_update_user_name(User *u, UserId user_id, string &&first_name, string &&last_name); diff --git a/td/telegram/DialogManager.cpp b/td/telegram/DialogManager.cpp index 42eecbe30..ed671c9c1 100644 --- a/td/telegram/DialogManager.cpp +++ b/td/telegram/DialogManager.cpp @@ -37,6 +37,70 @@ namespace td { +class CheckUsernameQuery final : public Td::ResultHandler { + Promise promise_; + + public: + explicit CheckUsernameQuery(Promise &&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(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 promise_; + ChannelId channel_id_; + string username_; + + public: + explicit CheckChannelUsernameQuery(Promise &&promise) : promise_(std::move(promise)) { + } + + void send(ChannelId channel_id, const string &username) { + channel_id_ = channel_id; + telegram_api::object_ptr input_channel; + if (channel_id.is_valid()) { + input_channel = td_->contacts_manager_->get_input_channel(channel_id); + } else { + input_channel = telegram_api::make_object(); + } + 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(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 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 &&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 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(std::move(request_promise))->send(username); + case DialogType::Channel: + return td_->create_handler(std::move(request_promise)) + ->send(dialog_id.get_channel_id(), username); + case DialogType::None: + return td_->create_handler(std::move(request_promise))->send(ChannelId(), username); + case DialogType::Chat: + case DialogType::SecretChat: + default: + UNREACHABLE(); + } +} + +td_api::object_ptr DialogManager::get_check_chat_username_result_object( + CheckDialogUsernameResult result) { + switch (result) { + case CheckDialogUsernameResult::Ok: + return td_api::make_object(); + case CheckDialogUsernameResult::Invalid: + return td_api::make_object(); + case CheckDialogUsernameResult::Occupied: + return td_api::make_object(); + case CheckDialogUsernameResult::Purchasable: + return td_api::make_object(); + case CheckDialogUsernameResult::PublicDialogsTooMany: + return td_api::make_object(); + case CheckDialogUsernameResult::PublicGroupsUnavailable: + return td_api::make_object(); + default: + UNREACHABLE(); + return nullptr; + } +} + void DialogManager::send_resolve_dialog_username_query(const string &username, Promise &&promise) { td_->create_handler(std::move(promise))->send(username); } diff --git a/td/telegram/DialogManager.h b/td/telegram/DialogManager.h index 38593d44c..d744b3139 100644 --- a/td/telegram/DialogManager.h +++ b/td/telegram/DialogManager.h @@ -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 &&promise); + + static td_api::object_ptr get_check_chat_username_result_object( + CheckDialogUsernameResult result); + void resolve_dialog(const string &username, ChannelId channel_id, Promise promise); DialogId get_resolved_dialog_by_username(const string &username) const; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index a955a57d6..263429a30 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -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 result) mutable { + [promise = std::move(promise)](Result 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) { diff --git a/td/telegram/misc.cpp b/td/telegram/misc.cpp index 59cdde271..6143b9f67 100644 --- a/td/telegram/misc.cpp +++ b/td/telegram/misc.cpp @@ -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 &numbers) { uint64 acc = 0; for (auto number : numbers) { diff --git a/td/telegram/misc.h b/td/telegram/misc.h index ba7f76067..35790e456 100644 --- a/td/telegram/misc.h +++ b/td/telegram/misc.h @@ -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 &numbers) TD_WARN_UNUSED_RESULT;