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.cpp897
1 files changed, 897 insertions, 0 deletions
diff --git a/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp b/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp
new file mode 100644
index 0000000000..64915de36b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/td/telegram/AuthManager.cpp
@@ -0,0 +1,897 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// 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/ConfigManager.h"
+#include "td/telegram/ConfigShared.h"
+#include "td/telegram/ContactsManager.h"
+#include "td/telegram/Global.h"
+#include "td/telegram/misc.h"
+#include "td/telegram/net/NetQueryDispatcher.h"
+#include "td/telegram/Td.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/logging.h"
+#include "td/utils/ScopeGuard.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");
+ if (auth_str == "ok") {
+ string is_bot_str = G()->td_db()->get_binlog_pmc()->get("auth_is_bot");
+ if (is_bot_str == "true") {
+ is_bot_ = true;
+ }
+ 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());
+ 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); }));
+ }
+ } else if (auth_str == "logout") {
+ update_state(State::LoggingOut);
+ } else {
+ if (!load_state()) {
+ update_state(State::WaitPhoneNumber);
+ }
+ }
+}
+
+void AuthManager::start_up() {
+ if (state_ == State::LoggingOut) {
+ start_net_query(NetQueryType::LogOut, G()->net_query_creator().create(create_storer(telegram_api::auth_logOut())));
+ }
+}
+void AuthManager::tear_down() {
+ parent_.reset();
+}
+
+bool AuthManager::is_bot() const {
+ return is_authorized() && is_bot_;
+}
+
+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::is_authorized() const {
+ return state_ == State::Ok;
+}
+
+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::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::LoggingOut:
+ return make_tl_object<td_api::authorizationStateLoggingOut>();
+ case State::Closing:
+ return make_tl_object<td_api::authorizationStateClosing>();
+ case State::None:
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+}
+
+void AuthManager::get_state(uint64 query_id) {
+ if (state_ == State::None) {
+ pending_get_authorization_state_requests_.push_back(query_id);
+ } else {
+ send_closure(G()->td(), &Td::send_result, query_id, get_authorization_state_object(state_));
+ }
+}
+
+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 (!send_code_helper_.phone_number().empty()) {
+ return on_query_error(
+ query_id, Status::Error(8, "Cannot set bot token after authentication beginning. 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);
+ }
+
+ on_new_query(query_id);
+ bot_token_ = 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));
+}
+
+void AuthManager::set_phone_number(uint64 query_id, string phone_number, bool allow_flash_call,
+ bool is_current_phone_number) {
+ if (state_ != State::WaitPhoneNumber) {
+ if ((state_ == State::WaitCode || state_ == State::WaitPassword) && net_query_id_ == 0) {
+ // ok
+ } else {
+ return on_query_error(query_id, Status::Error(8, "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"));
+ }
+ 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_code(phone_number, allow_flash_call, is_current_phone_number, api_id_, api_hash_);
+ if (r_send_code.is_error()) {
+ 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());
+ }
+ }
+
+ 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));
+}
+
+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"));
+ }
+
+ 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 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"));
+ }
+ 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"));
+ }
+
+ 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));
+ } 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));
+ }
+}
+
+void AuthManager::check_password(uint64 query_id, string password) {
+ if (state_ != State::WaitPassword) {
+ return on_query_error(query_id, Status::Error(8, "checkAuthenticationPassword unexpected"));
+ }
+ BufferSlice buf(32);
+ password = wait_password_state_.current_salt_ + password + wait_password_state_.current_salt_;
+ sha256(password, buf.as_slice());
+
+ 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));
+}
+
+void AuthManager::request_password_recovery(uint64 query_id) {
+ if (state_ != State::WaitPassword) {
+ return on_query_error(query_id, Status::Error(8, "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));
+}
+
+void AuthManager::recover_password(uint64 query_id, string code) {
+ if (state_ != State::WaitPassword) {
+ return on_query_error(query_id, Status::Error(8, "recoverAuthenticationPassword unexpected"));
+ }
+
+ on_new_query(query_id);
+ 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));
+}
+
+void AuthManager::logout(uint64 query_id) {
+ if (state_ == State::Closing) {
+ return on_query_error(query_id, Status::Error(8, "Already logged out"));
+ }
+ if (state_ == State::LoggingOut) {
+ return on_query_error(query_id, Status::Error(8, "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);
+ on_query_ok();
+ } else {
+ LOG(INFO) << "Logging out";
+ 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())));
+ }
+}
+
+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"));
+ }
+ 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))));
+}
+
+void AuthManager::on_closing() {
+ 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"));
+ }
+ net_query_id_ = 0;
+ net_query_type_ = NetQueryType::None;
+ query_id_ = query_id;
+ // TODO: cancel older net_query
+}
+
+void AuthManager::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 AuthManager::on_query_error(uint64 id, Status status) {
+ send_closure(G()->td(), &Td::send_error, id, std::move(status));
+}
+
+void AuthManager::on_query_ok() {
+ CHECK(query_id_ != 0);
+ auto id = query_id_;
+ net_query_id_ = 0;
+ net_query_type_ = NetQueryType::None;
+ query_id_ = 0;
+ send_ok(id);
+}
+
+void AuthManager::send_ok(uint64 query_id) {
+ send_closure(G()->td(), &Td::send_result, query_id, td_api::make_object<td_api::ok>());
+}
+
+void AuthManager::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 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()) {
+ 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));
+
+ update_state(State::WaitCode, true);
+ on_query_ok();
+}
+
+void AuthManager::on_get_password_result(NetQueryPtr &result) {
+ auto r_password = fetch_result<telegram_api::account_getPassword>(result->ok());
+ if (r_password.is_error()) {
+ return on_query_error(r_password.move_as_error());
+ }
+ auto password = r_password.move_as_ok();
+ 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();
+ } 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();
+}
+
+void AuthManager::on_request_password_recovery_result(NetQueryPtr &result) {
+ auto r_email_address_pattern = fetch_result<telegram_api::auth_requestPasswordRecovery>(result->ok());
+ if (r_email_address_pattern.is_error()) {
+ return on_query_error(r_email_address_pattern.move_as_error());
+ }
+ 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_;
+ update_state(State::WaitPassword, true);
+ on_query_ok();
+}
+
+void AuthManager::on_authentication_result(NetQueryPtr &result, bool expected_flag) {
+ auto r_sign_in = fetch_result<telegram_api::auth_signIn>(result->ok());
+ if (r_sign_in.is_error()) {
+ if (expected_flag && 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));
+}
+
+void AuthManager::on_log_out_result(NetQueryPtr &result) {
+ Status status;
+ 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!");
+ }
+ } else {
+ status = r_log_out.move_as_error();
+ }
+ } 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);
+ if (query_id_ != 0) {
+ on_query_ok();
+ }
+}
+
+void AuthManager::on_delete_account_result(NetQueryPtr &result) {
+ Status status;
+ if (result->is_ok()) {
+ 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");
+ }
+ } else {
+ status = r_delete_account.move_as_error();
+ }
+ } 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;
+ // TODO handle some errors
+ if (query_id_ != 0) {
+ on_query_error(std::move(status));
+ }
+ } else {
+ send_closure_later(G()->td(), &Td::destroy);
+ 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());
+ 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");
+ state_ = State::Ok;
+ td->contacts_manager_->on_get_user(std::move(auth->user_), true);
+ update_state(State::Ok, true);
+ if (!td->contacts_manager_->get_my_id("on_authorization").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);
+ return;
+ }
+ if ((auth->flags_ & telegram_api::auth_authorization::TMP_SESSIONS_MASK) != 0) {
+ G()->shared_config().set_option_integer("session_count", auth->tmp_sessions_);
+ }
+ td->updates_manager_->get_difference("on_authorization");
+ td->on_online_updated(true, true);
+ send_closure(G()->config_manager(), &ConfigManager::request_config);
+ if (query_id_ != 0) {
+ on_query_ok();
+ }
+}
+
+void AuthManager::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 (type == NetQueryType::SignIn && result->error().code() == 401 &&
+ result->error().message() == CSlice("SESSION_PASSWORD_NEEDED")) {
+ start_net_query(NetQueryType::GetPassword,
+ G()->net_query_creator().create(create_storer(telegram_api::account_getPassword()),
+ DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
+ return;
+ }
+ if (type != NetQueryType::LogOut) {
+ if (query_id_ != 0) {
+ if (state_ == State::WaitPhoneNumber) {
+ send_code_helper_ = SendCodeHelper();
+ }
+ on_query_error(std::move(result->error()));
+ }
+ return;
+ }
+ }
+ } else if (result->is_ok() && result->ok_tl_constructor() == telegram_api::auth_authorization::ID) {
+ type = NetQueryType::Authentication;
+ }
+ switch (type) {
+ case NetQueryType::None:
+ result->ignore();
+ break;
+ case NetQueryType::SignIn:
+ case NetQueryType::SignUp:
+ case NetQueryType::BotAuthentication:
+ case NetQueryType::CheckPassword:
+ case NetQueryType::RecoverPassword:
+ on_authentication_result(result, true);
+ break;
+ case NetQueryType::Authentication:
+ on_authentication_result(result, false);
+ break;
+ case NetQueryType::SendCode:
+ on_send_code_result(result);
+ break;
+ case NetQueryType::GetPassword:
+ on_get_password_result(result);
+ break;
+ case NetQueryType::RequestPasswordRecovery:
+ on_request_password_recovery_result(result);
+ break;
+ case NetQueryType::LogOut:
+ on_log_out_result(result);
+ break;
+ case NetQueryType::DeleteAccount:
+ on_delete_account_result(result);
+ break;
+ }
+}
+
+void AuthManager::update_state(State new_state, bool force, bool should_save_state) {
+ if (state_ == new_state && !force) {
+ return;
+ }
+ 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 (!pending_get_authorization_state_requests_.empty()) {
+ auto query_ids = std::move(pending_get_authorization_state_requests_);
+ for (auto query_id : query_ids) {
+ send_closure(G()->td(), &Td::send_result, query_id, get_authorization_state_object(state_));
+ }
+ }
+}
+
+bool AuthManager::load_state() {
+ auto data = G()->td_db()->get_binlog_pmc()->get("auth_state");
+ DbState db_state;
+ auto status = log_event_parse(db_state, data);
+ if (status.is_error()) {
+ LOG(INFO) << "Ignore auth_state: " << status;
+ return false;
+ }
+ if (db_state.api_id_ != api_id_ || db_state.api_hash_ != api_hash_) {
+ 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();
+ return false;
+ }
+
+ LOG(INFO) << "Load auth_state from db: " << tag("state", static_cast<int32>(db_state.state_));
+ if (db_state.state_ == State::WaitCode) {
+ send_code_helper_ = std::move(db_state.send_code_helper_);
+ } else if (db_state.state_ == State::WaitPassword) {
+ wait_password_state_ = std::move(db_state.wait_password_state_);
+ } else {
+ UNREACHABLE();
+ }
+ update_state(db_state.state_, false, false);
+ return true;
+}
+
+void AuthManager::save_state() {
+ if (state_ != State::WaitCode && state_ != State::WaitPassword) {
+ 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();
+ }
+ G()->td_db()->get_binlog_pmc()->set("auth_state", log_event_store(db_state).as_slice().str());
+}
+
+} // namespace td