summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp')
-rw-r--r--protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp1222
1 files changed, 757 insertions, 465 deletions
diff --git a/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp b/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp
index 64915de36b..78e5e05937 100644
--- a/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp
+++ b/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp
@@ -1,313 +1,44 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/AuthManager.h"
-#include "td/telegram/AuthManager.hpp"
-
-#include "td/telegram/td_api.h"
-#include "td/telegram/telegram_api.h"
+#include "td/telegram/AttachMenuManager.h"
+#include "td/telegram/AuthManager.hpp"
#include "td/telegram/ConfigManager.h"
-#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Global.h"
+#include "td/telegram/logevent/LogEvent.h"
+#include "td/telegram/MessagesManager.h"
#include "td/telegram/misc.h"
+#include "td/telegram/net/DcId.h"
#include "td/telegram/net/NetQueryDispatcher.h"
+#include "td/telegram/NewPasswordState.h"
+#include "td/telegram/NotificationManager.h"
+#include "td/telegram/OptionManager.h"
+#include "td/telegram/PasswordManager.h"
+#include "td/telegram/StateManager.h"
+#include "td/telegram/StickersManager.h"
#include "td/telegram/Td.h"
+#include "td/telegram/TdDb.h"
+#include "td/telegram/ThemeManager.h"
+#include "td/telegram/TopDialogManager.h"
#include "td/telegram/UpdatesManager.h"
-#include "td/telegram/logevent/LogEvent.h"
-
-#include "td/actor/PromiseFuture.h"
-
-#include "td/utils/buffer.h"
-#include "td/utils/crypto.h"
+#include "td/utils/base64.h"
+#include "td/utils/format.h"
#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/Promise.h"
#include "td/utils/ScopeGuard.h"
+#include "td/utils/Slice.h"
#include "td/utils/Time.h"
namespace td {
-// SendCodeHelper
-void SendCodeHelper::on_sent_code(telegram_api::object_ptr<telegram_api::auth_sentCode> sent_code) {
- phone_registered_ = (sent_code->flags_ & SENT_CODE_FLAG_IS_USER_REGISTERED) != 0;
- phone_code_hash_ = sent_code->phone_code_hash_;
- sent_code_info_ = get_authentication_code_info(std::move(sent_code->type_));
- next_code_info_ = get_authentication_code_info(std::move(sent_code->next_type_));
- next_code_timestamp_ = Timestamp::in((sent_code->flags_ & SENT_CODE_FLAG_HAS_TIMEOUT) != 0 ? sent_code->timeout_ : 0);
-}
-
-td_api::object_ptr<td_api::authorizationStateWaitCode> SendCodeHelper::get_authorization_state_wait_code() const {
- return make_tl_object<td_api::authorizationStateWaitCode>(phone_registered_, get_authentication_code_info_object());
-}
-
-td_api::object_ptr<td_api::authenticationCodeInfo> SendCodeHelper::get_authentication_code_info_object() const {
- return make_tl_object<td_api::authenticationCodeInfo>(
- phone_number_, get_authentication_code_type_object(sent_code_info_),
- get_authentication_code_type_object(next_code_info_),
- max(static_cast<int32>(next_code_timestamp_.in() + 1 - 1e-9), 0));
-}
-
-Result<telegram_api::auth_resendCode> SendCodeHelper::resend_code() {
- if (next_code_info_.type == AuthenticationCodeInfo::Type::None) {
- return Status::Error(8, "Authentication code can't be resend");
- }
- sent_code_info_ = next_code_info_;
- next_code_info_ = {};
- next_code_timestamp_ = {};
- return telegram_api::auth_resendCode(phone_number_, phone_code_hash_);
-}
-
-Result<telegram_api::auth_sendCode> SendCodeHelper::send_code(Slice phone_number, bool allow_flash_call,
- bool is_current_phone_number, int32 api_id,
- const string &api_hash) {
- if (!phone_number_.empty()) {
- return Status::Error(8, "Can't change phone");
- }
- phone_number_ = phone_number.str();
- int32 flags = 0;
- if (allow_flash_call) {
- flags |= AUTH_SEND_CODE_FLAG_ALLOW_FLASH_CALL;
- }
- return telegram_api::auth_sendCode(flags, false /*ignored*/, phone_number_, is_current_phone_number, api_id,
- api_hash);
-}
-
-Result<telegram_api::account_sendChangePhoneCode> SendCodeHelper::send_change_phone_code(Slice phone_number,
- bool allow_flash_call,
- bool is_current_phone_number) {
- phone_number_ = phone_number.str();
- int32 flags = 0;
- if (allow_flash_call) {
- flags |= AUTH_SEND_CODE_FLAG_ALLOW_FLASH_CALL;
- }
- return telegram_api::account_sendChangePhoneCode(flags, false /*ignored*/, phone_number_, is_current_phone_number);
-}
-
-SendCodeHelper::AuthenticationCodeInfo SendCodeHelper::get_authentication_code_info(
- tl_object_ptr<telegram_api::auth_CodeType> &&code_type_ptr) {
- if (code_type_ptr == nullptr) {
- return AuthenticationCodeInfo();
- }
-
- switch (code_type_ptr->get_id()) {
- case telegram_api::auth_codeTypeSms::ID:
- return {AuthenticationCodeInfo::Type::Sms, 0, ""};
- case telegram_api::auth_codeTypeCall::ID:
- return {AuthenticationCodeInfo::Type::Call, 0, ""};
- case telegram_api::auth_codeTypeFlashCall::ID:
- return {AuthenticationCodeInfo::Type::FlashCall, 0, ""};
- default:
- UNREACHABLE();
- return AuthenticationCodeInfo();
- }
-}
-
-SendCodeHelper::AuthenticationCodeInfo SendCodeHelper::get_authentication_code_info(
- tl_object_ptr<telegram_api::auth_SentCodeType> &&sent_code_type_ptr) {
- CHECK(sent_code_type_ptr != nullptr);
- switch (sent_code_type_ptr->get_id()) {
- case telegram_api::auth_sentCodeTypeApp::ID: {
- auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeApp>(sent_code_type_ptr);
- return AuthenticationCodeInfo{AuthenticationCodeInfo::Type::Message, code_type->length_, ""};
- }
- case telegram_api::auth_sentCodeTypeSms::ID: {
- auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeSms>(sent_code_type_ptr);
- return AuthenticationCodeInfo{AuthenticationCodeInfo::Type::Sms, code_type->length_, ""};
- }
- case telegram_api::auth_sentCodeTypeCall::ID: {
- auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeCall>(sent_code_type_ptr);
- return AuthenticationCodeInfo{AuthenticationCodeInfo::Type::Call, code_type->length_, ""};
- }
- case telegram_api::auth_sentCodeTypeFlashCall::ID: {
- auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeFlashCall>(sent_code_type_ptr);
- return AuthenticationCodeInfo{AuthenticationCodeInfo::Type::FlashCall, 0, code_type->pattern_};
- }
- default:
- UNREACHABLE();
- return AuthenticationCodeInfo();
- }
-}
-
-tl_object_ptr<td_api::AuthenticationCodeType> SendCodeHelper::get_authentication_code_type_object(
- const AuthenticationCodeInfo &authentication_code_info) {
- switch (authentication_code_info.type) {
- case AuthenticationCodeInfo::Type::None:
- return nullptr;
- case AuthenticationCodeInfo::Type::Message:
- return make_tl_object<td_api::authenticationCodeTypeTelegramMessage>(authentication_code_info.length);
- case AuthenticationCodeInfo::Type::Sms:
- return make_tl_object<td_api::authenticationCodeTypeSms>(authentication_code_info.length);
- case AuthenticationCodeInfo::Type::Call:
- return make_tl_object<td_api::authenticationCodeTypeCall>(authentication_code_info.length);
- case AuthenticationCodeInfo::Type::FlashCall:
- return make_tl_object<td_api::authenticationCodeTypeFlashCall>(authentication_code_info.pattern);
- default:
- UNREACHABLE();
- return nullptr;
- }
-}
-
-// ChangePhoneNumberManager
-void ChangePhoneNumberManager::get_state(uint64 query_id) {
- tl_object_ptr<td_api::Object> obj;
- switch (state_) {
- case State::Ok:
- obj = make_tl_object<td_api::ok>();
- break;
- case State::WaitCode:
- obj = send_code_helper_.get_authentication_code_info_object();
- break;
- }
- CHECK(obj);
- send_closure(G()->td(), &Td::send_result, query_id, std::move(obj));
-}
-
-ChangePhoneNumberManager::ChangePhoneNumberManager(ActorShared<> parent) : parent_(std::move(parent)) {
-}
-void ChangePhoneNumberManager::change_phone_number(uint64 query_id, string phone_number, bool allow_flash_call,
- bool is_current_phone_number) {
- if (phone_number.empty()) {
- return on_query_error(query_id, Status::Error(8, "Phone number can't be empty"));
- }
- auto r_send_code = send_code_helper_.send_change_phone_code(phone_number, allow_flash_call, is_current_phone_number);
- if (r_send_code.is_error()) {
- return on_query_error(query_id, r_send_code.move_as_error());
- }
-
- on_new_query(query_id);
-
- start_net_query(NetQueryType::SendCode, G()->net_query_creator().create(create_storer(r_send_code.move_as_ok())));
-}
-
-void ChangePhoneNumberManager::resend_authentication_code(uint64 query_id) {
- if (state_ != State::WaitCode) {
- return on_query_error(query_id, Status::Error(8, "resendAuthenticationCode unexpected"));
- }
-
- auto r_resend_code = send_code_helper_.resend_code();
- if (r_resend_code.is_error()) {
- return on_query_error(query_id, r_resend_code.move_as_error());
- }
-
- on_new_query(query_id);
-
- start_net_query(NetQueryType::SendCode,
- G()->net_query_creator().create(create_storer(r_resend_code.move_as_ok()), DcId::main(),
- NetQuery::Type::Common, NetQuery::AuthFlag::Off));
-}
-
-void ChangePhoneNumberManager::check_code(uint64 query_id, string code) {
- if (state_ != State::WaitCode) {
- return on_query_error(query_id, Status::Error(8, "checkAuthenticationCode unexpected"));
- }
-
- on_new_query(query_id);
- start_net_query(NetQueryType::ChangePhone,
- G()->net_query_creator().create(create_storer(telegram_api::account_changePhone(
- send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code))));
-}
-
-void ChangePhoneNumberManager::on_new_query(uint64 query_id) {
- if (query_id_ != 0) {
- on_query_error(Status::Error(9, "Another authorization query has started"));
- }
- net_query_id_ = 0;
- net_query_type_ = NetQueryType::None;
- query_id_ = query_id;
- // TODO: cancel older net_query
-}
-
-void ChangePhoneNumberManager::on_query_error(Status status) {
- CHECK(query_id_ != 0);
- auto id = query_id_;
- query_id_ = 0;
- net_query_id_ = 0;
- net_query_type_ = NetQueryType::None;
- on_query_error(id, std::move(status));
-}
-
-void ChangePhoneNumberManager::on_query_error(uint64 id, Status status) {
- send_closure(G()->td(), &Td::send_error, id, std::move(status));
-}
-
-void ChangePhoneNumberManager::on_query_ok() {
- CHECK(query_id_ != 0);
- auto id = query_id_;
- net_query_id_ = 0;
- net_query_type_ = NetQueryType::None;
- query_id_ = 0;
- get_state(id);
-}
-
-void ChangePhoneNumberManager::start_net_query(NetQueryType net_query_type, NetQueryPtr net_query) {
- // TODO: cancel old net_query?
- net_query_type_ = net_query_type;
- net_query_id_ = net_query->id();
- G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this));
-}
-
-void ChangePhoneNumberManager::on_change_phone_result(NetQueryPtr &result) {
- auto r_change_phone = fetch_result<telegram_api::account_changePhone>(result->ok());
- if (r_change_phone.is_error()) {
- return on_query_error(r_change_phone.move_as_error());
- }
- state_ = State::Ok;
- on_query_ok();
-}
-
-void ChangePhoneNumberManager::on_send_code_result(NetQueryPtr &result) {
- auto r_sent_code = fetch_result<telegram_api::account_sendChangePhoneCode>(result->ok());
- if (r_sent_code.is_error()) {
- return on_query_error(r_sent_code.move_as_error());
- }
- auto sent_code = r_sent_code.move_as_ok();
-
- LOG(INFO) << "Receive " << to_string(sent_code);
-
- send_code_helper_.on_sent_code(std::move(sent_code));
-
- state_ = State::WaitCode;
- on_query_ok();
-}
-
-void ChangePhoneNumberManager::on_result(NetQueryPtr result) {
- SCOPE_EXIT {
- result->clear();
- };
- NetQueryType type = NetQueryType::None;
- if (result->id() == net_query_id_) {
- net_query_id_ = 0;
- type = net_query_type_;
- net_query_type_ = NetQueryType::None;
- if (result->is_error()) {
- if (query_id_ != 0) {
- on_query_error(std::move(result->error()));
- }
- return;
- }
- }
- switch (type) {
- case NetQueryType::None:
- result->ignore();
- break;
- case NetQueryType::SendCode:
- on_send_code_result(result);
- break;
- case NetQueryType::ChangePhone:
- on_change_phone_result(result);
- break;
- }
-}
-
-void ChangePhoneNumberManager::tear_down() {
- parent_.reset();
-}
-
-// AuthManager
AuthManager::AuthManager(int32 api_id, const string &api_hash, ActorShared<> parent)
: parent_(std::move(parent)), api_id_(api_id), api_hash_(api_hash) {
string auth_str = G()->td_db()->get_binlog_pmc()->get("auth");
@@ -319,16 +50,21 @@ AuthManager::AuthManager(int32 api_id, const string &api_hash, ActorShared<> par
auto my_id = ContactsManager::load_my_id();
if (my_id.is_valid()) {
// just in case
- G()->shared_config().set_option_integer("my_id", my_id.get());
+ LOG(INFO) << "Logged in as " << my_id;
+ td_->option_manager_->set_option_integer("my_id", my_id.get());
update_state(State::Ok);
} else {
LOG(ERROR) << "Restore unknown my_id";
ContactsManager::send_get_me_query(
- G()->td().get_actor_unsafe(),
- PromiseCreator::lambda([this](Result<Unit> result) { update_state(State::Ok); }));
+ td_, PromiseCreator::lambda([this](Result<Unit> result) { update_state(State::Ok); }));
}
+ G()->net_query_dispatcher().check_authorization_is_ok();
} else if (auth_str == "logout") {
+ LOG(WARNING) << "Continue to log out";
update_state(State::LoggingOut);
+ } else if (auth_str == "destroy") {
+ LOG(WARNING) << "Continue to destroy auth keys";
+ update_state(State::DestroyingKeys);
} else {
if (!load_state()) {
update_state(State::WaitPhoneNumber);
@@ -338,7 +74,13 @@ AuthManager::AuthManager(int32 api_id, const string &api_hash, ActorShared<> par
void AuthManager::start_up() {
if (state_ == State::LoggingOut) {
- start_net_query(NetQueryType::LogOut, G()->net_query_creator().create(create_storer(telegram_api::auth_logOut())));
+ send_log_out_query();
+ } else if (state_ == State::DestroyingKeys) {
+ G()->net_query_dispatcher().destroy_auth_keys(PromiseCreator::lambda([](Result<Unit> result) {
+ if (result.is_ok()) {
+ send_closure_later(G()->td(), &Td::destroy);
+ }
+ }));
}
}
void AuthManager::tear_down() {
@@ -346,15 +88,15 @@ void AuthManager::tear_down() {
}
bool AuthManager::is_bot() const {
- return is_authorized() && is_bot_;
+ if (net_query_id_ != 0 && net_query_type_ == NetQueryType::BotAuthentication) {
+ return true;
+ }
+ return is_bot_ && was_authorized();
}
-void AuthManager::set_is_bot(bool is_bot) {
- if (!is_bot_ && is_bot && api_id_ == 23818) {
- LOG(ERROR) << "Fix is_bot to " << is_bot;
- G()->td_db()->get_binlog_pmc()->set("auth_is_bot", "true");
- is_bot_ = true;
- }
+bool AuthManager::was_authorized() const {
+ return state_ == State::Ok || state_ == State::LoggingOut || state_ == State::DestroyingKeys ||
+ state_ == State::Closing;
}
bool AuthManager::is_authorized() const {
@@ -363,16 +105,29 @@ bool AuthManager::is_authorized() const {
tl_object_ptr<td_api::AuthorizationState> AuthManager::get_authorization_state_object(State authorization_state) const {
switch (authorization_state) {
- case State::Ok:
- return make_tl_object<td_api::authorizationStateReady>();
- case State::WaitCode:
- return send_code_helper_.get_authorization_state_wait_code();
case State::WaitPhoneNumber:
return make_tl_object<td_api::authorizationStateWaitPhoneNumber>();
+ case State::WaitEmailAddress:
+ return make_tl_object<td_api::authorizationStateWaitEmailAddress>(allow_apple_id_, allow_google_id_);
+ case State::WaitEmailCode:
+ return make_tl_object<td_api::authorizationStateWaitEmailCode>(
+ allow_apple_id_, allow_google_id_, email_code_info_.get_email_address_authentication_code_info_object(),
+ next_phone_number_login_date_);
+ case State::WaitCode:
+ return send_code_helper_.get_authorization_state_wait_code();
+ case State::WaitQrCodeConfirmation:
+ return make_tl_object<td_api::authorizationStateWaitOtherDeviceConfirmation>("tg://login?token=" +
+ base64url_encode(login_token_));
case State::WaitPassword:
return make_tl_object<td_api::authorizationStateWaitPassword>(
wait_password_state_.hint_, wait_password_state_.has_recovery_, wait_password_state_.email_address_pattern_);
+ case State::WaitRegistration:
+ return make_tl_object<td_api::authorizationStateWaitRegistration>(
+ terms_of_service_.get_terms_of_service_object());
+ case State::Ok:
+ return make_tl_object<td_api::authorizationStateReady>();
case State::LoggingOut:
+ case State::DestroyingKeys:
return make_tl_object<td_api::authorizationStateLoggingOut>();
case State::Closing:
return make_tl_object<td_api::authorizationStateClosing>();
@@ -383,6 +138,14 @@ tl_object_ptr<td_api::AuthorizationState> AuthManager::get_authorization_state_o
}
}
+tl_object_ptr<td_api::AuthorizationState> AuthManager::get_current_authorization_state_object() const {
+ if (state_ == State::None) {
+ return nullptr;
+ } else {
+ return get_authorization_state_object(state_);
+ }
+}
+
void AuthManager::get_state(uint64 query_id) {
if (state_ == State::None) {
pending_get_authorization_state_requests_.push_back(query_id);
@@ -392,76 +155,165 @@ void AuthManager::get_state(uint64 query_id) {
}
void AuthManager::check_bot_token(uint64 query_id, string bot_token) {
- if (state_ != State::WaitPhoneNumber && state_ != State::Ok) {
- // TODO do not allow State::Ok
- return on_query_error(query_id, Status::Error(8, "checkAuthenticationBotToken unexpected"));
+ if (state_ == State::WaitPhoneNumber && net_query_id_ == 0) {
+ // can ignore previous checks
+ was_check_bot_token_ = false; // TODO can we remove was_check_bot_token_?
+ }
+ if (state_ != State::WaitPhoneNumber) {
+ return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationBotToken unexpected"));
}
- if (!send_code_helper_.phone_number().empty()) {
+ if (!send_code_helper_.phone_number().empty() || was_qr_code_request_) {
return on_query_error(
- query_id, Status::Error(8, "Cannot set bot token after authentication beginning. You need to log out first"));
+ query_id, Status::Error(400, "Cannot set bot token after authentication began. You need to log out first"));
}
if (was_check_bot_token_ && bot_token_ != bot_token) {
- return on_query_error(query_id, Status::Error(8, "Cannot change bot token. You need to log out first"));
- }
- if (state_ == State::Ok) {
- if (!is_bot_) {
- // fix old bots
- const int32 AUTH_IS_BOT_FIXED_DATE = 1500940800;
- if (G()->shared_config().get_option_integer("authorization_date") < AUTH_IS_BOT_FIXED_DATE) {
- G()->td_db()->get_binlog_pmc()->set("auth_is_bot", "true");
- is_bot_ = true;
- }
- }
- return send_ok(query_id);
+ return on_query_error(query_id, Status::Error(400, "Cannot change bot token. You need to log out first"));
}
on_new_query(query_id);
- bot_token_ = bot_token;
+ bot_token_ = std::move(bot_token);
was_check_bot_token_ = true;
start_net_query(NetQueryType::BotAuthentication,
- G()->net_query_creator().create(
- create_storer(telegram_api::auth_importBotAuthorization(0, api_id_, api_hash_, bot_token_)),
- DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ G()->net_query_creator().create_unauth(
+ telegram_api::auth_importBotAuthorization(0, api_id_, api_hash_, bot_token_)));
+}
+
+void AuthManager::request_qr_code_authentication(uint64 query_id, vector<UserId> other_user_ids) {
+ if (state_ != State::WaitPhoneNumber) {
+ if ((state_ == State::WaitEmailAddress || state_ == State::WaitEmailCode || state_ == State::WaitCode ||
+ state_ == State::WaitPassword || state_ == State::WaitRegistration) &&
+ net_query_id_ == 0) {
+ // ok
+ } else {
+ return on_query_error(query_id, Status::Error(400, "Call to requestQrCodeAuthentication unexpected"));
+ }
+ }
+ if (was_check_bot_token_) {
+ return on_query_error(
+ query_id,
+ Status::Error(400,
+ "Cannot request QR code authentication after bot token was entered. You need to log out first"));
+ }
+ for (auto &other_user_id : other_user_ids) {
+ if (!other_user_id.is_valid()) {
+ return on_query_error(query_id, Status::Error(400, "Invalid user_id among other user_ids"));
+ }
+ }
+
+ other_user_ids_ = std::move(other_user_ids);
+ send_code_helper_ = SendCodeHelper();
+ terms_of_service_ = TermsOfService();
+ was_qr_code_request_ = true;
+
+ on_new_query(query_id);
+
+ send_export_login_token_query();
+}
+
+void AuthManager::send_export_login_token_query() {
+ poll_export_login_code_timeout_.cancel_timeout();
+ start_net_query(NetQueryType::RequestQrCode,
+ G()->net_query_creator().create_unauth(telegram_api::auth_exportLoginToken(
+ api_id_, api_hash_, UserId::get_input_user_ids(other_user_ids_))));
+}
+
+void AuthManager::set_login_token_expires_at(double login_token_expires_at) {
+ login_token_expires_at_ = login_token_expires_at;
+ poll_export_login_code_timeout_.cancel_timeout();
+ poll_export_login_code_timeout_.set_callback(std::move(on_update_login_token_static));
+ poll_export_login_code_timeout_.set_callback_data(static_cast<void *>(td_));
+ poll_export_login_code_timeout_.set_timeout_at(login_token_expires_at_);
+}
+
+void AuthManager::on_update_login_token_static(void *td) {
+ if (G()->close_flag()) {
+ return;
+ }
+ static_cast<Td *>(td)->auth_manager_->on_update_login_token();
}
-void AuthManager::set_phone_number(uint64 query_id, string phone_number, bool allow_flash_call,
- bool is_current_phone_number) {
+void AuthManager::on_update_login_token() {
+ if (G()->close_flag()) {
+ return;
+ }
+ if (state_ != State::WaitQrCodeConfirmation) {
+ return;
+ }
+
+ send_export_login_token_query();
+}
+
+void AuthManager::set_phone_number(uint64 query_id, string phone_number,
+ td_api::object_ptr<td_api::phoneNumberAuthenticationSettings> settings) {
if (state_ != State::WaitPhoneNumber) {
- if ((state_ == State::WaitCode || state_ == State::WaitPassword) && net_query_id_ == 0) {
+ if ((state_ == State::WaitEmailAddress || state_ == State::WaitEmailCode || state_ == State::WaitCode ||
+ state_ == State::WaitPassword || state_ == State::WaitRegistration) &&
+ net_query_id_ == 0) {
// ok
} else {
- return on_query_error(query_id, Status::Error(8, "setAuthenticationPhoneNumber unexpected"));
+ return on_query_error(query_id, Status::Error(400, "Call to setAuthenticationPhoneNumber unexpected"));
}
}
if (was_check_bot_token_) {
return on_query_error(
- query_id, Status::Error(8, "Cannot set phone number after bot token was entered. You need to log out first"));
+ query_id, Status::Error(400, "Cannot set phone number after bot token was entered. You need to log out first"));
}
if (phone_number.empty()) {
- return on_query_error(query_id, Status::Error(8, "Phone number can't be empty"));
+ return on_query_error(query_id, Status::Error(400, "Phone number must be non-empty"));
}
- auto r_send_code =
- send_code_helper_.send_code(phone_number, allow_flash_call, is_current_phone_number, api_id_, api_hash_);
- if (r_send_code.is_error()) {
+ other_user_ids_.clear();
+ was_qr_code_request_ = false;
+
+ allow_apple_id_ = false;
+ allow_google_id_ = false;
+ email_address_ = {};
+ email_code_info_ = {};
+ next_phone_number_login_date_ = 0;
+ code_ = string();
+ email_code_ = {};
+
+ if (send_code_helper_.phone_number() != phone_number) {
send_code_helper_ = SendCodeHelper();
- r_send_code =
- send_code_helper_.send_code(phone_number, allow_flash_call, is_current_phone_number, api_id_, api_hash_);
- if (r_send_code.is_error()) {
- return on_query_error(query_id, r_send_code.move_as_error());
+ terms_of_service_ = TermsOfService();
+ }
+
+ on_new_query(query_id);
+
+ start_net_query(NetQueryType::SendCode, G()->net_query_creator().create_unauth(send_code_helper_.send_code(
+ std::move(phone_number), settings, api_id_, api_hash_)));
+}
+
+void AuthManager::set_email_address(uint64 query_id, string email_address) {
+ if (state_ != State::WaitEmailAddress) {
+ if (state_ == State::WaitEmailCode && net_query_id_ == 0) {
+ // ok
+ } else {
+ return on_query_error(query_id, Status::Error(400, "Call to setAuthenticationEmailAddress unexpected"));
}
}
+ if (email_address.empty()) {
+ return on_query_error(query_id, Status::Error(400, "Email address must be non-empty"));
+ }
+
+ email_address_ = std::move(email_address);
on_new_query(query_id);
- start_net_query(NetQueryType::SendCode,
- G()->net_query_creator().create(create_storer(r_send_code.move_as_ok()), DcId::main(),
- NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ start_net_query(NetQueryType::SendEmailCode,
+ G()->net_query_creator().create_unauth(send_code_helper_.send_verify_email_code(email_address_)));
}
void AuthManager::resend_authentication_code(uint64 query_id) {
- if (state_ != State::WaitCode || was_check_bot_token_) {
- return on_query_error(query_id, Status::Error(8, "resendAuthenticationCode unexpected"));
+ if (state_ != State::WaitCode) {
+ if (state_ == State::WaitEmailCode) {
+ on_new_query(query_id);
+ start_net_query(NetQueryType::SendEmailCode,
+ G()->net_query_creator().create_unauth(send_code_helper_.send_verify_email_code(email_address_)));
+ return;
+ }
+
+ return on_query_error(query_id, Status::Error(400, "Call to resendAuthenticationCode unexpected"));
}
auto r_resend_code = send_code_helper_.resend_code();
@@ -471,114 +323,198 @@ void AuthManager::resend_authentication_code(uint64 query_id) {
on_new_query(query_id);
- start_net_query(NetQueryType::SendCode,
- G()->net_query_creator().create(create_storer(r_resend_code.move_as_ok()), DcId::main(),
- NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ start_net_query(NetQueryType::SendCode, G()->net_query_creator().create_unauth(r_resend_code.move_as_ok()));
}
-void AuthManager::check_code(uint64 query_id, string code, string first_name, string last_name) {
- if (state_ != State::WaitCode) {
- return on_query_error(query_id, Status::Error(8, "checkAuthenticationCode unexpected"));
+void AuthManager::send_auth_sign_in_query() {
+ bool is_email = !email_code_.is_empty();
+ int32 flags =
+ is_email ? telegram_api::auth_signIn::EMAIL_VERIFICATION_MASK : telegram_api::auth_signIn::PHONE_CODE_MASK;
+ start_net_query(NetQueryType::SignIn,
+ G()->net_query_creator().create_unauth(telegram_api::auth_signIn(
+ flags, send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code_,
+ is_email ? email_code_.get_input_email_verification() : nullptr)));
+}
+
+void AuthManager::check_email_code(uint64 query_id, EmailVerification &&code) {
+ if (code.is_empty()) {
+ return on_query_error(query_id, Status::Error(400, "Code must be non-empty"));
}
- first_name = clean_name(first_name, MAX_NAME_LENGTH);
- if (!send_code_helper_.phone_registered() && first_name.empty()) {
- return on_query_error(query_id, Status::Error(8, "First name can't be empty"));
+ if (state_ != State::WaitEmailCode && !(state_ == State::WaitEmailAddress && code.is_email_code())) {
+ return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationEmailCode unexpected"));
}
+ code_ = string();
+ email_code_ = std::move(code);
+
on_new_query(query_id);
- if (send_code_helper_.phone_registered()) {
- start_net_query(NetQueryType::SignIn,
- G()->net_query_creator().create(
- create_storer(telegram_api::auth_signIn(send_code_helper_.phone_number().str(),
- send_code_helper_.phone_code_hash().str(), code)),
- DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ if (email_address_.empty()) {
+ send_auth_sign_in_query();
} else {
- last_name = clean_name(last_name, MAX_NAME_LENGTH);
start_net_query(
- NetQueryType::SignUp,
- G()->net_query_creator().create(create_storer(telegram_api::auth_signUp(
- send_code_helper_.phone_number().str(),
- send_code_helper_.phone_code_hash().str(), code, first_name, last_name)),
- DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ NetQueryType::VerifyEmailAddress,
+ G()->net_query_creator().create_unauth(telegram_api::account_verifyEmail(
+ send_code_helper_.get_email_verify_purpose_login_setup(), email_code_.get_input_email_verification())));
}
}
+void AuthManager::check_code(uint64 query_id, string code) {
+ if (state_ != State::WaitCode) {
+ return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationCode unexpected"));
+ }
+
+ code_ = std::move(code);
+ email_code_ = {};
+
+ on_new_query(query_id);
+ send_auth_sign_in_query();
+}
+
+void AuthManager::register_user(uint64 query_id, string first_name, string last_name) {
+ if (state_ != State::WaitRegistration) {
+ return on_query_error(query_id, Status::Error(400, "Call to registerUser unexpected"));
+ }
+
+ on_new_query(query_id);
+ first_name = clean_name(first_name, MAX_NAME_LENGTH);
+ if (first_name.empty()) {
+ return on_query_error(Status::Error(400, "First name must be non-empty"));
+ }
+
+ last_name = clean_name(last_name, MAX_NAME_LENGTH);
+ start_net_query(NetQueryType::SignUp, G()->net_query_creator().create_unauth(telegram_api::auth_signUp(
+ send_code_helper_.phone_number().str(),
+ send_code_helper_.phone_code_hash().str(), first_name, last_name)));
+}
+
void AuthManager::check_password(uint64 query_id, string password) {
if (state_ != State::WaitPassword) {
- return on_query_error(query_id, Status::Error(8, "checkAuthenticationPassword unexpected"));
+ return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationPassword unexpected"));
}
- BufferSlice buf(32);
- password = wait_password_state_.current_salt_ + password + wait_password_state_.current_salt_;
- sha256(password, buf.as_slice());
+ LOG(INFO) << "Have SRP ID " << wait_password_state_.srp_id_;
on_new_query(query_id);
- start_net_query(NetQueryType::CheckPassword,
- G()->net_query_creator().create(create_storer(telegram_api::auth_checkPassword(std::move(buf))),
- DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ password_ = std::move(password);
+ recovery_code_.clear();
+ new_password_.clear();
+ new_hint_.clear();
+ start_net_query(NetQueryType::GetPassword,
+ G()->net_query_creator().create_unauth(telegram_api::account_getPassword()));
}
void AuthManager::request_password_recovery(uint64 query_id) {
if (state_ != State::WaitPassword) {
- return on_query_error(query_id, Status::Error(8, "requestAuthenticationPasswordRecovery unexpected"));
+ return on_query_error(query_id, Status::Error(400, "Call to requestAuthenticationPasswordRecovery unexpected"));
}
on_new_query(query_id);
start_net_query(NetQueryType::RequestPasswordRecovery,
- G()->net_query_creator().create(create_storer(telegram_api::auth_requestPasswordRecovery()),
- DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ G()->net_query_creator().create_unauth(telegram_api::auth_requestPasswordRecovery()));
+}
+
+void AuthManager::check_password_recovery_code(uint64 query_id, string code) {
+ if (state_ != State::WaitPassword) {
+ return on_query_error(query_id, Status::Error(400, "Call to checkAuthenticationPasswordRecoveryCode unexpected"));
+ }
+
+ on_new_query(query_id);
+ start_net_query(NetQueryType::CheckPasswordRecoveryCode,
+ G()->net_query_creator().create_unauth(telegram_api::auth_checkRecoveryPassword(code)));
}
-void AuthManager::recover_password(uint64 query_id, string code) {
+void AuthManager::recover_password(uint64 query_id, string code, string new_password, string new_hint) {
if (state_ != State::WaitPassword) {
- return on_query_error(query_id, Status::Error(8, "recoverAuthenticationPassword unexpected"));
+ return on_query_error(query_id, Status::Error(400, "Call to recoverAuthenticationPassword unexpected"));
}
on_new_query(query_id);
+ if (!new_password.empty()) {
+ password_.clear();
+ recovery_code_ = std::move(code);
+ new_password_ = std::move(new_password);
+ new_hint_ = std::move(new_hint);
+ start_net_query(NetQueryType::GetPassword,
+ G()->net_query_creator().create_unauth(telegram_api::account_getPassword()));
+ return;
+ }
start_net_query(NetQueryType::RecoverPassword,
- G()->net_query_creator().create(create_storer(telegram_api::auth_recoverPassword(code)), DcId::main(),
- NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ G()->net_query_creator().create_unauth(telegram_api::auth_recoverPassword(0, code, nullptr)));
}
-void AuthManager::logout(uint64 query_id) {
+void AuthManager::log_out(uint64 query_id) {
if (state_ == State::Closing) {
- return on_query_error(query_id, Status::Error(8, "Already logged out"));
+ return on_query_error(query_id, Status::Error(400, "Already logged out"));
}
- if (state_ == State::LoggingOut) {
- return on_query_error(query_id, Status::Error(8, "Already logging out"));
+ if (state_ == State::LoggingOut || state_ == State::DestroyingKeys) {
+ return on_query_error(query_id, Status::Error(400, "Already logging out"));
}
on_new_query(query_id);
if (state_ != State::Ok) {
- update_state(State::LoggingOut);
// TODO: could skip full logout if still no authorization
// TODO: send auth.cancelCode if state_ == State::WaitCode
- send_closure_later(G()->td(), &Td::destroy);
+ LOG(WARNING) << "Destroying auth keys by user request";
+ destroy_auth_keys();
on_query_ok();
} else {
- LOG(INFO) << "Logging out";
+ LOG(WARNING) << "Logging out by user request";
G()->td_db()->get_binlog_pmc()->set("auth", "logout");
update_state(State::LoggingOut);
- start_net_query(NetQueryType::LogOut, G()->net_query_creator().create(create_storer(telegram_api::auth_logOut())));
+ send_log_out_query();
}
}
-void AuthManager::delete_account(uint64 query_id, const string &reason) {
- if (state_ != State::Ok) {
- return on_query_error(query_id, Status::Error(8, "Need to log in first"));
+void AuthManager::send_log_out_query() {
+ // we can lose authorization while logging out, but still may need to resend the request,
+ // so we pretend that it doesn't require authorization
+ auto query = G()->net_query_creator().create_unauth(telegram_api::auth_logOut());
+ query->set_priority(1);
+ start_net_query(NetQueryType::LogOut, std::move(query));
+}
+
+void AuthManager::delete_account(uint64 query_id, string reason, string password) {
+ if (state_ != State::Ok && state_ != State::WaitPassword) {
+ return on_query_error(query_id, Status::Error(400, "Need to log in first"));
}
+ if (password.empty() || state_ != State::Ok) {
+ on_new_query(query_id);
+ LOG(INFO) << "Deleting account";
+ start_net_query(NetQueryType::DeleteAccount,
+ G()->net_query_creator().create_unauth(telegram_api::account_deleteAccount(0, reason, nullptr)));
+ } else {
+ send_closure(G()->password_manager(), &PasswordManager::get_input_check_password_srp, password,
+ PromiseCreator::lambda(
+ [actor_id = actor_id(this), query_id, reason = std::move(reason)](
+ Result<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> r_input_password) mutable {
+ send_closure(actor_id, &AuthManager::do_delete_account, query_id, std::move(reason),
+ std::move(r_input_password));
+ }));
+ }
+}
+
+void AuthManager::do_delete_account(uint64 query_id, string reason,
+ Result<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> r_input_password) {
+ if (r_input_password.is_error()) {
+ return on_query_error(query_id, r_input_password.move_as_error());
+ }
+
on_new_query(query_id);
- LOG(INFO) << "Deleting account";
- update_state(State::LoggingOut);
- start_net_query(NetQueryType::DeleteAccount,
- G()->net_query_creator().create(create_storer(telegram_api::account_deleteAccount(reason))));
+ LOG(INFO) << "Deleting account with password";
+ int32 flags = telegram_api::account_deleteAccount::PASSWORD_MASK;
+ start_net_query(NetQueryType::DeleteAccount, G()->net_query_creator().create(telegram_api::account_deleteAccount(
+ flags, reason, r_input_password.move_as_ok())));
}
-void AuthManager::on_closing() {
- update_state(State::Closing);
+void AuthManager::on_closing(bool destroy_flag) {
+ if (destroy_flag) {
+ update_state(State::LoggingOut);
+ } else {
+ update_state(State::Closing);
+ }
}
void AuthManager::on_new_query(uint64 query_id) {
if (query_id_ != 0) {
- on_query_error(Status::Error(9, "Another authorization query has started"));
+ on_query_error(Status::Error(400, "Another authorization query has started"));
}
net_query_id_ = 0;
net_query_type_ = NetQueryType::None;
@@ -595,8 +531,8 @@ void AuthManager::on_query_error(Status status) {
on_query_error(id, std::move(status));
}
-void AuthManager::on_query_error(uint64 id, Status status) {
- send_closure(G()->td(), &Td::send_error, id, std::move(status));
+void AuthManager::on_query_error(uint64 query_id, Status status) {
+ send_closure(G()->td(), &Td::send_error, query_id, std::move(status));
}
void AuthManager::on_query_ok() {
@@ -619,6 +555,33 @@ void AuthManager::start_net_query(NetQueryType net_query_type, NetQueryPtr net_q
G()->net_query_dispatcher().dispatch_with_callback(std::move(net_query), actor_shared(this));
}
+void AuthManager::on_sent_code(telegram_api::object_ptr<telegram_api::auth_sentCode> &&sent_code) {
+ auto code_type_id = sent_code->type_->get_id();
+ if (code_type_id == telegram_api::auth_sentCodeTypeSetUpEmailRequired::ID) {
+ auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeSetUpEmailRequired>(std::move(sent_code->type_));
+ send_code_helper_.on_phone_code_hash(std::move(sent_code->phone_code_hash_));
+ allow_apple_id_ = code_type->apple_signin_allowed_;
+ allow_google_id_ = code_type->google_signin_allowed_;
+ update_state(State::WaitEmailAddress, true);
+ } else if (code_type_id == telegram_api::auth_sentCodeTypeEmailCode::ID) {
+ auto code_type = move_tl_object_as<telegram_api::auth_sentCodeTypeEmailCode>(std::move(sent_code->type_));
+ send_code_helper_.on_phone_code_hash(std::move(sent_code->phone_code_hash_));
+ allow_apple_id_ = code_type->apple_signin_allowed_;
+ allow_google_id_ = code_type->google_signin_allowed_;
+ email_address_.clear();
+ email_code_info_ = SentEmailCode(std::move(code_type->email_pattern_), code_type->length_);
+ next_phone_number_login_date_ = td::max(static_cast<int32>(0), code_type->next_phone_login_date_);
+ if (email_code_info_.is_empty()) {
+ email_code_info_ = SentEmailCode("<unknown>", code_type->length_);
+ CHECK(!email_code_info_.is_empty());
+ }
+ update_state(State::WaitEmailCode, true);
+ } else {
+ send_code_helper_.on_sent_code(std::move(sent_code));
+ update_state(State::WaitCode, true);
+ }
+}
+
void AuthManager::on_send_code_result(NetQueryPtr &result) {
auto r_sent_code = fetch_result<telegram_api::auth_sendCode>(result->ok());
if (r_sent_code.is_error()) {
@@ -627,33 +590,209 @@ void AuthManager::on_send_code_result(NetQueryPtr &result) {
auto sent_code = r_sent_code.move_as_ok();
LOG(INFO) << "Receive " << to_string(sent_code);
+ on_sent_code(std::move(sent_code));
+ on_query_ok();
+}
- send_code_helper_.on_sent_code(std::move(sent_code));
+void AuthManager::on_send_email_code_result(NetQueryPtr &result) {
+ auto r_sent_code = fetch_result<telegram_api::account_sendVerifyEmailCode>(result->ok());
+ if (r_sent_code.is_error()) {
+ return on_query_error(r_sent_code.move_as_error());
+ }
+ auto sent_code = r_sent_code.move_as_ok();
+
+ LOG(INFO) << "Receive " << to_string(sent_code);
- update_state(State::WaitCode, true);
+ email_code_info_ = SentEmailCode(std::move(sent_code));
+ if (email_code_info_.is_empty()) {
+ return on_query_error(Status::Error(500, "Receive invalid response"));
+ }
+ next_phone_number_login_date_ = 0;
+
+ update_state(State::WaitEmailCode, true);
on_query_ok();
}
+void AuthManager::on_verify_email_address_result(NetQueryPtr &result) {
+ auto r_email_verified = fetch_result<telegram_api::account_verifyEmail>(result->ok());
+ if (r_email_verified.is_error()) {
+ return on_query_error(r_email_verified.move_as_error());
+ }
+ auto email_verified = r_email_verified.move_as_ok();
+
+ LOG(INFO) << "Receive " << to_string(email_verified);
+ if (email_verified->get_id() != telegram_api::account_emailVerifiedLogin::ID) {
+ return on_query_error(Status::Error(500, "Receive invalid response"));
+ }
+
+ auto verified_login = telegram_api::move_object_as<telegram_api::account_emailVerifiedLogin>(email_verified);
+ on_sent_code(std::move(verified_login->sent_code_));
+ on_query_ok();
+}
+
+void AuthManager::on_request_qr_code_result(NetQueryPtr &result, bool is_import) {
+ Status status;
+ if (result->is_ok()) {
+ auto r_login_token = fetch_result<telegram_api::auth_exportLoginToken>(result->ok());
+ if (r_login_token.is_ok()) {
+ auto login_token = r_login_token.move_as_ok();
+
+ if (is_import) {
+ CHECK(DcId::is_valid(imported_dc_id_));
+ G()->net_query_dispatcher().set_main_dc_id(imported_dc_id_);
+ imported_dc_id_ = -1;
+ }
+
+ on_get_login_token(std::move(login_token));
+ return;
+ }
+
+ status = r_login_token.move_as_error();
+ } else {
+ status = std::move(result->error());
+ }
+ CHECK(status.is_error());
+
+ LOG(INFO) << "Receive " << status << " for login token " << (is_import ? "import" : "export");
+ if (is_import) {
+ imported_dc_id_ = -1;
+ }
+ if (query_id_ != 0) {
+ on_query_error(std::move(status));
+ } else {
+ login_code_retry_delay_ = clamp(2 * login_code_retry_delay_, 1, 60);
+ set_login_token_expires_at(Time::now() + login_code_retry_delay_);
+ }
+}
+
+void AuthManager::on_get_login_token(tl_object_ptr<telegram_api::auth_LoginToken> login_token) {
+ LOG(INFO) << "Receive " << to_string(login_token);
+
+ login_code_retry_delay_ = 0;
+
+ CHECK(login_token != nullptr);
+ switch (login_token->get_id()) {
+ case telegram_api::auth_loginToken::ID: {
+ auto token = move_tl_object_as<telegram_api::auth_loginToken>(login_token);
+ login_token_ = token->token_.as_slice().str();
+ set_login_token_expires_at(Time::now() + td::max(token->expires_ - G()->server_time(), 1.0));
+ update_state(State::WaitQrCodeConfirmation, true);
+ if (query_id_ != 0) {
+ on_query_ok();
+ }
+ break;
+ }
+ case telegram_api::auth_loginTokenMigrateTo::ID: {
+ auto token = move_tl_object_as<telegram_api::auth_loginTokenMigrateTo>(login_token);
+ if (!DcId::is_valid(token->dc_id_)) {
+ LOG(ERROR) << "Receive wrong DC " << token->dc_id_;
+ return;
+ }
+ if (query_id_ != 0) {
+ on_query_ok();
+ }
+
+ imported_dc_id_ = token->dc_id_;
+ start_net_query(NetQueryType::ImportQrCode, G()->net_query_creator().create_unauth(
+ telegram_api::auth_importLoginToken(std::move(token->token_)),
+ DcId::internal(token->dc_id_)));
+ break;
+ }
+ case telegram_api::auth_loginTokenSuccess::ID: {
+ auto token = move_tl_object_as<telegram_api::auth_loginTokenSuccess>(login_token);
+ on_get_authorization(std::move(token->authorization_));
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
void AuthManager::on_get_password_result(NetQueryPtr &result) {
- auto r_password = fetch_result<telegram_api::account_getPassword>(result->ok());
- if (r_password.is_error()) {
+ Result<telegram_api::object_ptr<telegram_api::account_password>> r_password;
+ if (result->is_error()) {
+ r_password = std::move(result->error());
+ } else {
+ r_password = fetch_result<telegram_api::account_getPassword>(result->ok());
+ }
+ if (r_password.is_error() && query_id_ != 0) {
return on_query_error(r_password.move_as_error());
}
- auto password = r_password.move_as_ok();
+ auto password = r_password.is_ok() ? r_password.move_as_ok() : nullptr;
+ LOG(INFO) << "Receive password info: " << to_string(password);
+
wait_password_state_ = WaitPasswordState();
- if (password->get_id() == telegram_api::account_noPassword::ID) {
- auto no_password = move_tl_object_as<telegram_api::account_noPassword>(password);
- wait_password_state_.new_salt_ = no_password->new_salt_.as_slice().str();
+ Result<NewPasswordState> r_new_password_state;
+ if (password != nullptr && password->current_algo_ != nullptr) {
+ switch (password->current_algo_->get_id()) {
+ case telegram_api::passwordKdfAlgoUnknown::ID:
+ return on_query_error(Status::Error(400, "Application update is needed to log in"));
+ case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: {
+ auto algo = move_tl_object_as<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow>(
+ password->current_algo_);
+ wait_password_state_.current_client_salt_ = algo->salt1_.as_slice().str();
+ wait_password_state_.current_server_salt_ = algo->salt2_.as_slice().str();
+ wait_password_state_.srp_g_ = algo->g_;
+ wait_password_state_.srp_p_ = algo->p_.as_slice().str();
+ wait_password_state_.srp_B_ = password->srp_B_.as_slice().str();
+ wait_password_state_.srp_id_ = password->srp_id_;
+ wait_password_state_.hint_ = std::move(password->hint_);
+ wait_password_state_.has_recovery_ = password->has_recovery_;
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ r_new_password_state =
+ get_new_password_state(std::move(password->new_algo_), std::move(password->new_secure_algo_));
+ } else if (was_qr_code_request_) {
+ imported_dc_id_ = -1;
+ login_code_retry_delay_ = clamp(2 * login_code_retry_delay_, 1, 60);
+ set_login_token_expires_at(Time::now() + login_code_retry_delay_);
+ return;
} else {
- CHECK(password->get_id() == telegram_api::account_password::ID);
- auto password_info = move_tl_object_as<telegram_api::account_password>(password);
- wait_password_state_.current_salt_ = password_info->current_salt_.as_slice().str();
- wait_password_state_.new_salt_ = password_info->new_salt_.as_slice().str();
- wait_password_state_.hint_ = password_info->hint_;
- wait_password_state_.has_recovery_ = password_info->has_recovery_;
- }
- update_state(State::WaitPassword);
- on_query_ok();
+ send_auth_sign_in_query();
+ return;
+ }
+
+ if (imported_dc_id_ != -1) {
+ G()->net_query_dispatcher().set_main_dc_id(imported_dc_id_);
+ imported_dc_id_ = -1;
+ }
+
+ if (state_ == State::WaitPassword) {
+ if (!new_password_.empty()) {
+ if (r_new_password_state.is_error()) {
+ return on_query_error(r_new_password_state.move_as_error());
+ }
+
+ auto r_new_settings = PasswordManager::get_password_input_settings(std::move(new_password_), std::move(new_hint_),
+ r_new_password_state.ok());
+ if (r_new_settings.is_error()) {
+ return on_query_error(r_new_settings.move_as_error());
+ }
+
+ int32 flags = telegram_api::auth_recoverPassword::NEW_SETTINGS_MASK;
+ start_net_query(NetQueryType::RecoverPassword,
+ G()->net_query_creator().create_unauth(
+ telegram_api::auth_recoverPassword(flags, recovery_code_, r_new_settings.move_as_ok())));
+ return;
+ }
+ LOG(INFO) << "Have SRP ID " << wait_password_state_.srp_id_;
+ auto hash = PasswordManager::get_input_check_password(password_, wait_password_state_.current_client_salt_,
+ wait_password_state_.current_server_salt_,
+ wait_password_state_.srp_g_, wait_password_state_.srp_p_,
+ wait_password_state_.srp_B_, wait_password_state_.srp_id_);
+
+ start_net_query(NetQueryType::CheckPassword,
+ G()->net_query_creator().create_unauth(telegram_api::auth_checkPassword(std::move(hash))));
+ } else {
+ update_state(State::WaitPassword);
+ if (query_id_ != 0) {
+ on_query_ok();
+ }
+ }
}
void AuthManager::on_request_password_recovery_result(NetQueryPtr &result) {
@@ -663,22 +802,31 @@ void AuthManager::on_request_password_recovery_result(NetQueryPtr &result) {
}
auto email_address_pattern = r_email_address_pattern.move_as_ok();
CHECK(email_address_pattern->get_id() == telegram_api::auth_passwordRecovery::ID);
- wait_password_state_.email_address_pattern_ = email_address_pattern->email_pattern_;
+ wait_password_state_.email_address_pattern_ = std::move(email_address_pattern->email_pattern_);
update_state(State::WaitPassword, true);
on_query_ok();
}
-void AuthManager::on_authentication_result(NetQueryPtr &result, bool expected_flag) {
+void AuthManager::on_check_password_recovery_code_result(NetQueryPtr &result) {
+ auto r_success = fetch_result<telegram_api::auth_checkRecoveryPassword>(result->ok());
+ if (r_success.is_error()) {
+ return on_query_error(r_success.move_as_error());
+ }
+ if (!r_success.ok()) {
+ return on_query_error(Status::Error(400, "Invalid recovery code"));
+ }
+ on_query_ok();
+}
+
+void AuthManager::on_authentication_result(NetQueryPtr &result, bool is_from_current_query) {
auto r_sign_in = fetch_result<telegram_api::auth_signIn>(result->ok());
if (r_sign_in.is_error()) {
- if (expected_flag && query_id_ != 0) {
+ if (is_from_current_query && query_id_ != 0) {
return on_query_error(r_sign_in.move_as_error());
}
return;
}
- auto sign_in = r_sign_in.move_as_ok();
- CHECK(sign_in->get_id() == telegram_api::auth_authorization::ID);
- on_authorization(std::move(sign_in));
+ on_get_authorization(r_sign_in.move_as_ok());
}
void AuthManager::on_log_out_result(NetQueryPtr &result) {
@@ -686,8 +834,10 @@ void AuthManager::on_log_out_result(NetQueryPtr &result) {
if (result->is_ok()) {
auto r_log_out = fetch_result<telegram_api::auth_logOut>(result->ok());
if (r_log_out.is_ok()) {
- if (!r_log_out.ok()) {
- status = Status::Error(500, "auth.logOut returned false!");
+ auto logged_out = r_log_out.move_as_ok();
+ if (!logged_out->future_auth_token_.empty()) {
+ td_->option_manager_->set_option_string("authentication_token",
+ base64url_encode(logged_out->future_auth_token_.as_slice()));
}
} else {
status = r_log_out.move_as_error();
@@ -695,13 +845,43 @@ void AuthManager::on_log_out_result(NetQueryPtr &result) {
} else {
status = std::move(result->error());
}
- LOG_IF(ERROR, status.is_error()) << "auth.logOut failed: " << status;
- // state_ will stay logout, so no queries will work.
- send_closure_later(G()->td(), &Td::destroy);
+ LOG_IF(ERROR, status.is_error() && status.code() != 401) << "Receive error for auth.logOut: " << status;
+ // state_ will stay LoggingOut, so no queries will work.
+ destroy_auth_keys();
if (query_id_ != 0) {
on_query_ok();
}
}
+void AuthManager::on_authorization_lost(string source) {
+ if (state_ == State::LoggingOut && net_query_type_ == NetQueryType::LogOut) {
+ LOG(INFO) << "Ignore authorization loss because of " << source << ", while logging out";
+ return;
+ }
+ if (state_ == State::Closing || state_ == State::DestroyingKeys) {
+ LOG(INFO) << "Ignore duplicate authorization loss because of " << source;
+ return;
+ }
+ LOG(WARNING) << "Lost authorization because of " << source;
+ destroy_auth_keys();
+}
+
+void AuthManager::destroy_auth_keys() {
+ if (state_ == State::Closing || state_ == State::DestroyingKeys) {
+ return;
+ }
+ update_state(State::DestroyingKeys);
+ auto promise = PromiseCreator::lambda([](Result<Unit> result) {
+ if (result.is_ok()) {
+ G()->net_query_dispatcher().destroy_auth_keys(PromiseCreator::lambda([](Result<Unit> result) {
+ if (result.is_ok()) {
+ send_closure_later(G()->td(), &Td::destroy);
+ }
+ }));
+ }
+ });
+ G()->td_db()->get_binlog_pmc()->set("auth", "destroy");
+ G()->td_db()->get_binlog_pmc()->force_sync(std::move(promise));
+}
void AuthManager::on_delete_account_result(NetQueryPtr &result) {
Status status;
@@ -709,7 +889,7 @@ void AuthManager::on_delete_account_result(NetQueryPtr &result) {
auto r_delete_account = fetch_result<telegram_api::account_deleteAccount>(result->ok());
if (r_delete_account.is_ok()) {
if (!r_delete_account.ok()) {
- status = Status::Error(500, "Receive false as result of the request");
+ // status = Status::Error(500, "Receive false as result of the request");
}
} else {
status = r_delete_account.move_as_error();
@@ -717,45 +897,84 @@ void AuthManager::on_delete_account_result(NetQueryPtr &result) {
} else {
status = std::move(result->error());
}
- if (status.is_error() && status.error().message() != "USER_DEACTIVATED") {
- update_state(State::Ok);
- LOG(WARNING) << "account.deleteAccount failed: " << status;
+ if (status.is_error() && status.message() != "USER_DEACTIVATED") {
+ LOG(WARNING) << "Request account.deleteAccount failed: " << status;
// TODO handle some errors
if (query_id_ != 0) {
on_query_error(std::move(status));
}
} else {
- send_closure_later(G()->td(), &Td::destroy);
+ destroy_auth_keys();
if (query_id_ != 0) {
on_query_ok();
}
}
}
-void AuthManager::on_authorization(tl_object_ptr<telegram_api::auth_authorization> auth) {
- G()->shared_config().set_option_integer("authorization_date", G()->unix_time());
+void AuthManager::on_get_authorization(tl_object_ptr<telegram_api::auth_Authorization> auth_ptr) {
+ if (state_ == State::Ok) {
+ LOG(WARNING) << "Ignore duplicated auth.Authorization";
+ if (query_id_ != 0) {
+ on_query_ok();
+ }
+ return;
+ }
+ CHECK(auth_ptr != nullptr);
+ if (auth_ptr->get_id() == telegram_api::auth_authorizationSignUpRequired::ID) {
+ auto sign_up_required = telegram_api::move_object_as<telegram_api::auth_authorizationSignUpRequired>(auth_ptr);
+ terms_of_service_ = TermsOfService(std::move(sign_up_required->terms_of_service_));
+ update_state(State::WaitRegistration);
+ if (query_id_ != 0) {
+ on_query_ok();
+ }
+ return;
+ }
+ auto auth = telegram_api::move_object_as<telegram_api::auth_authorization>(auth_ptr);
+
+ td_->option_manager_->set_option_integer("authorization_date", G()->unix_time());
if (was_check_bot_token_) {
is_bot_ = true;
G()->td_db()->get_binlog_pmc()->set("auth_is_bot", "true");
}
G()->td_db()->get_binlog_pmc()->set("auth", "ok");
+ code_.clear();
+ password_.clear();
+ recovery_code_.clear();
+ new_password_.clear();
+ new_hint_.clear();
state_ = State::Ok;
- td->contacts_manager_->on_get_user(std::move(auth->user_), true);
+ td_->contacts_manager_->on_get_user(std::move(auth->user_), "on_get_authorization", true);
update_state(State::Ok, true);
- if (!td->contacts_manager_->get_my_id("on_authorization").is_valid()) {
+ if (!td_->contacts_manager_->get_my_id().is_valid()) {
LOG(ERROR) << "Server doesn't send proper authorization";
if (query_id_ != 0) {
on_query_error(Status::Error(500, "Server doesn't send proper authorization"));
}
- logout(0);
+ log_out(0);
return;
}
if ((auth->flags_ & telegram_api::auth_authorization::TMP_SESSIONS_MASK) != 0) {
- G()->shared_config().set_option_integer("session_count", auth->tmp_sessions_);
+ td_->option_manager_->set_option_integer("session_count", auth->tmp_sessions_);
+ }
+ if (auth->setup_password_required_ && auth->otherwise_relogin_days_ > 0) {
+ td_->option_manager_->set_option_integer("otherwise_relogin_days", auth->otherwise_relogin_days_);
+ }
+ td_->attach_menu_manager_->init();
+ td_->messages_manager_->on_authorization_success();
+ td_->notification_manager_->init();
+ td_->stickers_manager_->init();
+ td_->theme_manager_->init();
+ td_->top_dialog_manager_->init();
+ td_->updates_manager_->get_difference("on_get_authorization");
+ td_->on_online_updated(false, true);
+ if (!is_bot()) {
+ td_->schedule_get_terms_of_service(0);
+ td_->schedule_get_promo_data(0);
+ G()->td_db()->get_binlog_pmc()->set("fetched_marks_as_unread", "1");
+ } else {
+ td_->set_is_bot_online(true);
}
- td->updates_manager_->get_difference("on_authorization");
- td->on_online_updated(true, true);
- send_closure(G()->config_manager(), &ConfigManager::request_config);
+ send_closure(G()->config_manager(), &ConfigManager::request_config, false);
if (query_id_ != 0) {
on_query_ok();
}
@@ -766,26 +985,48 @@ void AuthManager::on_result(NetQueryPtr result) {
result->clear();
};
NetQueryType type = NetQueryType::None;
+ LOG(INFO) << "Receive result of query " << result->id() << ", expecting " << net_query_id_ << " with type "
+ << static_cast<int32>(net_query_type_);
if (result->id() == net_query_id_) {
net_query_id_ = 0;
type = net_query_type_;
net_query_type_ = NetQueryType::None;
if (result->is_error()) {
- if (type == NetQueryType::SignIn && result->error().code() == 401 &&
- result->error().message() == CSlice("SESSION_PASSWORD_NEEDED")) {
+ if ((type == NetQueryType::SendCode || type == NetQueryType::SendEmailCode ||
+ type == NetQueryType::VerifyEmailAddress || type == NetQueryType::SignIn ||
+ type == NetQueryType::RequestQrCode || type == NetQueryType::ImportQrCode) &&
+ result->error().code() == 401 && result->error().message() == CSlice("SESSION_PASSWORD_NEEDED")) {
+ auto dc_id = DcId::main();
+ if (type == NetQueryType::ImportQrCode) {
+ CHECK(DcId::is_valid(imported_dc_id_));
+ dc_id = DcId::internal(imported_dc_id_);
+ }
start_net_query(NetQueryType::GetPassword,
- G()->net_query_creator().create(create_storer(telegram_api::account_getPassword()),
- DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ G()->net_query_creator().create_unauth(telegram_api::account_getPassword(), dc_id));
return;
}
- if (type != NetQueryType::LogOut) {
+ if (result->error().message() == CSlice("PHONE_NUMBER_BANNED")) {
+ LOG(PLAIN)
+ << "Your phone number was banned 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.";
+ }
+ if (type != NetQueryType::LogOut && type != NetQueryType::DeleteAccount) {
if (query_id_ != 0) {
if (state_ == State::WaitPhoneNumber) {
+ other_user_ids_.clear();
send_code_helper_ = SendCodeHelper();
+ terms_of_service_ = TermsOfService();
+ was_qr_code_request_ = false;
+ was_check_bot_token_ = false;
}
on_query_error(std::move(result->error()));
+ return;
+ }
+ if (type != NetQueryType::RequestQrCode && type != NetQueryType::ImportQrCode &&
+ type != NetQueryType::GetPassword) {
+ LOG(INFO) << "Ignore error for net query of type " << static_cast<int32>(net_query_type_);
+ return;
}
- return;
}
}
} else if (result->is_ok() && result->ok_tl_constructor() == telegram_api::auth_authorization::ID) {
@@ -808,12 +1049,27 @@ void AuthManager::on_result(NetQueryPtr result) {
case NetQueryType::SendCode:
on_send_code_result(result);
break;
+ case NetQueryType::SendEmailCode:
+ on_send_email_code_result(result);
+ break;
+ case NetQueryType::VerifyEmailAddress:
+ on_verify_email_address_result(result);
+ break;
+ case NetQueryType::RequestQrCode:
+ on_request_qr_code_result(result, false);
+ break;
+ case NetQueryType::ImportQrCode:
+ on_request_qr_code_result(result, true);
+ break;
case NetQueryType::GetPassword:
on_get_password_result(result);
break;
case NetQueryType::RequestPasswordRecovery:
on_request_password_recovery_result(result);
break;
+ case NetQueryType::CheckPasswordRecoveryCode:
+ on_check_password_recovery_code_result(result);
+ break;
case NetQueryType::LogOut:
on_log_out_result(result);
break;
@@ -827,12 +1083,19 @@ void AuthManager::update_state(State new_state, bool force, bool should_save_sta
if (state_ == new_state && !force) {
return;
}
+ bool skip_update = (state_ == State::LoggingOut || state_ == State::DestroyingKeys) &&
+ (new_state == State::LoggingOut || new_state == State::DestroyingKeys);
state_ = new_state;
if (should_save_state) {
save_state();
}
- send_closure(G()->td(), &Td::send_update,
- make_tl_object<td_api::updateAuthorizationState>(get_authorization_state_object(state_)));
+ if (new_state == State::LoggingOut || new_state == State::DestroyingKeys) {
+ send_closure(G()->state_manager(), &StateManager::on_logging_out, true);
+ }
+ if (!skip_update) {
+ send_closure(G()->td(), &Td::send_update,
+ make_tl_object<td_api::updateAuthorizationState>(get_authorization_state_object(state_)));
+ }
if (!pending_get_authorization_state_requests_.empty()) {
auto query_ids = std::move(pending_get_authorization_state_requests_);
@@ -844,6 +1107,10 @@ void AuthManager::update_state(State new_state, bool force, bool should_save_sta
bool AuthManager::load_state() {
auto data = G()->td_db()->get_binlog_pmc()->get("auth_state");
+ if (data.empty()) {
+ LOG(INFO) << "Have no saved auth_state. Waiting for phone number";
+ return false;
+ }
DbState db_state;
auto status = log_event_parse(db_state, data);
if (status.is_error()) {
@@ -854,20 +1121,34 @@ bool AuthManager::load_state() {
LOG(INFO) << "Ignore auth_state: api_id or api_hash changed";
return false;
}
- if (!db_state.state_timestamp_.is_in_past()) {
- LOG(INFO) << "Ignore auth_state: timestamp in future";
- return false;
- }
- if (Timestamp::at(db_state.state_timestamp_.at() + 5 * 60).is_in_past()) {
- LOG(INFO) << "Ignore auth_state: expired " << db_state.state_timestamp_.in();
+ if (db_state.expires_at_ <= Time::now()) {
+ LOG(INFO) << "Ignore auth_state: expired";
return false;
}
- LOG(INFO) << "Load auth_state from db: " << tag("state", static_cast<int32>(db_state.state_));
- if (db_state.state_ == State::WaitCode) {
+ LOG(INFO) << "Load auth_state from database: " << tag("state", static_cast<int32>(db_state.state_));
+ if (db_state.state_ == State::WaitEmailAddress) {
+ allow_apple_id_ = db_state.allow_apple_id_;
+ allow_google_id_ = db_state.allow_google_id_;
send_code_helper_ = std::move(db_state.send_code_helper_);
+ } else if (db_state.state_ == State::WaitEmailCode) {
+ allow_apple_id_ = db_state.allow_apple_id_;
+ allow_google_id_ = db_state.allow_google_id_;
+ email_address_ = std::move(db_state.email_address_);
+ email_code_info_ = std::move(db_state.email_code_info_);
+ next_phone_number_login_date_ = db_state.next_phone_number_login_date_;
+ send_code_helper_ = std::move(db_state.send_code_helper_);
+ } else if (db_state.state_ == State::WaitCode) {
+ send_code_helper_ = std::move(db_state.send_code_helper_);
+ } else if (db_state.state_ == State::WaitQrCodeConfirmation) {
+ other_user_ids_ = std::move(db_state.other_user_ids_);
+ login_token_ = std::move(db_state.login_token_);
+ set_login_token_expires_at(db_state.login_token_expires_at_);
} else if (db_state.state_ == State::WaitPassword) {
wait_password_state_ = std::move(db_state.wait_password_state_);
+ } else if (db_state.state_ == State::WaitRegistration) {
+ send_code_helper_ = std::move(db_state.send_code_helper_);
+ terms_of_service_ = std::move(db_state.terms_of_service_);
} else {
UNREACHABLE();
}
@@ -876,21 +1157,32 @@ bool AuthManager::load_state() {
}
void AuthManager::save_state() {
- if (state_ != State::WaitCode && state_ != State::WaitPassword) {
+ if (state_ != State::WaitEmailAddress && state_ != State::WaitEmailCode && state_ != State::WaitCode &&
+ state_ != State::WaitQrCodeConfirmation && state_ != State::WaitPassword && state_ != State::WaitRegistration) {
if (state_ != State::Closing) {
G()->td_db()->get_binlog_pmc()->erase("auth_state");
}
return;
}
- DbState db_state;
- if (state_ == State::WaitCode) {
- db_state = DbState::wait_code(api_id_, api_hash_, send_code_helper_);
- } else if (state_ == State::WaitPassword) {
- db_state = DbState::wait_password(api_id_, api_hash_, wait_password_state_);
- } else {
- UNREACHABLE();
- }
+ DbState db_state = [&] {
+ if (state_ == State::WaitEmailAddress) {
+ return DbState::wait_email_address(api_id_, api_hash_, allow_apple_id_, allow_google_id_, send_code_helper_);
+ } else if (state_ == State::WaitEmailCode) {
+ return DbState::wait_email_code(api_id_, api_hash_, allow_apple_id_, allow_google_id_, email_address_,
+ email_code_info_, next_phone_number_login_date_, send_code_helper_);
+ } else if (state_ == State::WaitCode) {
+ return DbState::wait_code(api_id_, api_hash_, send_code_helper_);
+ } else if (state_ == State::WaitQrCodeConfirmation) {
+ return DbState::wait_qr_code_confirmation(api_id_, api_hash_, other_user_ids_, login_token_,
+ login_token_expires_at_);
+ } else if (state_ == State::WaitPassword) {
+ return DbState::wait_password(api_id_, api_hash_, wait_password_state_);
+ } else {
+ CHECK(state_ == State::WaitRegistration);
+ return DbState::wait_registration(api_id_, api_hash_, send_code_helper_, terms_of_service_);
+ }
+ }();
G()->td_db()->get_binlog_pmc()->set("auth_state", log_event_store(db_state).as_slice().str());
}