diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 081b63a7..78e0fa54 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -109,8 +109,10 @@ authorizationStateClosing = AuthorizationState; authorizationStateClosed = AuthorizationState; -//@description Represents the current state of 2-step verification @has_password True, if a 2-step verification password is set @password_hint Hint for the password; can be empty @has_recovery_email_address True, if a recovery email is set @has_passport_data True, if some Telegram Passport elements were saved @unconfirmed_recovery_email_address_pattern Pattern of the email address to which the confirmation email was sent -passwordState has_password:Bool password_hint:string has_recovery_email_address:Bool has_passport_data:Bool unconfirmed_recovery_email_address_pattern:string = PasswordState; +//@description Represents the current state of 2-step verification @has_password True, if a 2-step verification password is set @password_hint Hint for the password; can be empty +//@has_recovery_email_address True, if a recovery email is set @has_passport_data True, if some Telegram Passport elements were saved +//@recovery_email_address_code_info Information about the recovery email address to which the confirmation email was sent; may be null +passwordState has_password:Bool password_hint:string has_recovery_email_address:Bool has_passport_data:Bool recovery_email_address_code_info:emailAddressAuthenticationCodeInfo = PasswordState; //@description Contains information about the current recovery email address @recovery_email_address Recovery email address recoveryEmailAddress recovery_email_address:string = RecoveryEmailAddress; @@ -2585,17 +2587,20 @@ setDatabaseEncryptionKey new_encryption_key:bytes = Ok; //@description Returns the current state of 2-step verification getPasswordState = PasswordState; -//@description Changes the password for the user. If a new recovery email address is specified, then the error EMAIL_UNCONFIRMED is returned and the password change will not be applied until the new recovery email address has been confirmed. The application should periodically call getPasswordState to check whether the new email address has been confirmed +//@description Changes the password for the user. If a new recovery email address is specified, then the change will not be applied until the new recovery email address is confirmed //@old_password Previous password of the user @new_password New password of the user; may be empty to remove the password @new_hint New password hint; may be empty @set_recovery_email_address Pass true if the recovery email address should be changed @new_recovery_email_address New recovery email address; may be empty setPassword old_password:string new_password:string new_hint:string set_recovery_email_address:Bool new_recovery_email_address:string = PasswordState; -//@description Returns a recovery email address that was previously set up. This method can be used to verify a password provided by the user @password The password for the current user +//@description Returns a 2-step verification recovery email address that was previously set up. This method can be used to verify a password provided by the user @password The password for the current user getRecoveryEmailAddress password:string = RecoveryEmailAddress; -//@description Changes the recovery email address of the user. If a new recovery email address is specified, then the error EMAIL_UNCONFIRMED is returned and the email address will not be changed until the new email has been confirmed. The application should periodically call getPasswordState to check whether the email address has been confirmed. +//@description Changes the 2-step verification recovery email address of the user. If a new recovery email address is specified, then the change will not be applied until the new recovery email address is confirmed //-If new_recovery_email_address is the same as the email address that is currently set up, this call succeeds immediately and aborts all other requests waiting for an email confirmation @password Password of the current user @new_recovery_email_address New recovery email address setRecoveryEmailAddress password:string new_recovery_email_address:string = PasswordState; +//@description Checks the 2-step verification recovery email address code @code Verification code +checkRecoveryEmailAddressCode code:string = Ok; + //@description Requests to send a password recovery code to an email address that was previously set up requestPasswordRecovery = EmailAddressAuthenticationCodeInfo; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index a4145075..ac689d67 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/PasswordManager.cpp b/td/telegram/PasswordManager.cpp index 9b65e6bd..51b260e4 100644 --- a/td/telegram/PasswordManager.cpp +++ b/td/telegram/PasswordManager.cpp @@ -181,7 +181,8 @@ void PasswordManager::get_secure_secret(string password, Promise promise) { +void PasswordManager::do_get_secure_secret(bool allow_recursive, string password, + Promise promise) { if (secret_) { return promise.set_value(secret_.value().clone()); } @@ -189,7 +190,7 @@ void PasswordManager::do_get_secure_secret(bool recursive, string password, Prom return promise.set_error(Status::Error(400, "PASSWORD_HASH_INVALID")); } get_full_state( - password, PromiseCreator::lambda([password, recursive, promise = std::move(promise), + password, PromiseCreator::lambda([password, allow_recursive, promise = std::move(promise), actor_id = actor_id(this)](Result r_state) mutable { if (r_state.is_error()) { return promise.set_error(r_state.move_as_error()); @@ -202,7 +203,7 @@ void PasswordManager::do_get_secure_secret(bool recursive, string password, Prom send_closure(actor_id, &PasswordManager::cache_secret, state.private_state.secret.value().clone()); return promise.set_value(std::move(state.private_state.secret.value())); } - if (!recursive) { + if (!allow_recursive) { return promise.set_error(Status::Error(400, "Failed to get Telegram Passport secret")); } @@ -379,6 +380,19 @@ void PasswordManager::get_recovery_email_address(string password, })); } +void PasswordManager::check_recovery_email_address_code(string code, Promise promise) { + auto query = + G()->net_query_creator().create(create_storer(telegram_api::account_confirmPasswordEmail(std::move(code)))); + send_with_promise(std::move(query), + PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { + auto r_result = fetch_result(std::move(r_query)); + if (r_result.is_error()) { + return promise.set_error(r_result.move_as_error()); + } + return promise.set_value(Unit()); + })); +} + void PasswordManager::send_email_address_verification_code( string email, Promise> promise) { last_verified_email_address_ = email; @@ -457,8 +471,7 @@ void PasswordManager::update_password_settings(UpdateSettings update_settings, P return promise.set_error(r_update_settings.move_as_error()); } if (!r_update_settings.ok()) { - promise.set_error(Status::Error(5, "account_updatePasswordSettings returned false")); - return; + return promise.set_error(Status::Error(5, "account_updatePasswordSettings returned false")); } send_closure(actor_id, &PasswordManager::get_state, std::move(promise)); }); @@ -557,11 +570,17 @@ void PasswordManager::do_update_password_settings_impl(UpdateSettings update_set auto query = G()->net_query_creator().create( create_storer(telegram_api::account_updatePasswordSettings(std::move(current_hash), std::move(new_settings)))); - send_with_promise(std::move(query), - PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { + send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)]( + Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); if (r_result.is_error()) { - if (r_result.error().code() == 400 && r_result.error().message() == "EMAIL_UNCONFIRMED") { + Slice prefix("EMAIL_UNCONFIRMED"); + Slice message = r_result.error().message(); + if (r_result.error().code() == 400 && begins_with(message, prefix)) { + if (message.size() >= prefix.size() + 2 && message[prefix.size()] == '_') { + send_closure(actor_id, &PasswordManager::on_get_code_length, + to_integer(message.substr(prefix.size() + 1))); + } return promise.set_value(true); } return promise.set_error(r_result.move_as_error()); @@ -570,6 +589,16 @@ void PasswordManager::do_update_password_settings_impl(UpdateSettings update_set })); } +void PasswordManager::on_get_code_length(int32 code_length) { + if (code_length <= 0 || code_length > 100) { + LOG(ERROR) << "Receive invalid code length " << code_length; + return; + } + + LOG(INFO) << "Set code length to " << code_length; + last_code_length_ = code_length; +} + void PasswordManager::get_state(Promise promise) { do_get_state(PromiseCreator::lambda([promise = std::move(promise)](Result r_state) mutable { if (r_state.is_error()) { @@ -582,7 +611,7 @@ void PasswordManager::get_state(Promise promise) { void PasswordManager::do_get_state(Promise promise) { auto query = G()->net_query_creator().create(create_storer(telegram_api::account_getPassword())); send_with_promise( - std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), + std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), code_length = last_code_length_, promise = std::move(promise)](Result r_query) mutable { auto r_result = fetch_result(std::move(r_query)); if (r_result.is_error()) { @@ -623,6 +652,7 @@ void PasswordManager::do_get_state(Promise promise) { send_closure(actor_id, &PasswordManager::drop_cached_secret); } state.unconfirmed_recovery_email_address_pattern = std::move(password->email_unconfirmed_pattern_); + state.code_length = code_length; CHECK(password->new_algo_ != nullptr); switch (password->new_algo_->get_id()) { diff --git a/td/telegram/PasswordManager.h b/td/telegram/PasswordManager.h index 0f3cf4d8..3facd25f 100644 --- a/td/telegram/PasswordManager.h +++ b/td/telegram/PasswordManager.h @@ -66,6 +66,7 @@ class PasswordManager : public NetQueryCallback { string recovery_email_address, Promise promise); void set_recovery_email_address(string password, string new_recovery_email_address, Promise promise); void get_recovery_email_address(string password, Promise> promise); + void check_recovery_email_address_code(string code, Promise promise); void send_email_address_verification_code( string email, Promise> promise); @@ -96,6 +97,7 @@ class PasswordManager : public NetQueryCallback { bool has_recovery_email_address = false; bool has_secure_values = false; string unconfirmed_recovery_email_address_pattern; + int32 code_length = 0; string current_client_salt; string current_server_salt; @@ -111,8 +113,13 @@ class PasswordManager : public NetQueryCallback { string new_secure_salt; State as_td_api() const { + td_api::object_ptr code_info; + if (!unconfirmed_recovery_email_address_pattern.empty()) { + code_info = td_api::make_object( + unconfirmed_recovery_email_address_pattern, code_length); + } return td_api::make_object(has_password, password_hint, has_recovery_email_address, - has_secure_values, unconfirmed_recovery_email_address_pattern); + has_secure_values, std::move(code_info)); } }; @@ -147,6 +154,8 @@ class PasswordManager : public NetQueryCallback { string last_verified_email_address_; + int32 last_code_length_ = 0; + static Result decrypt_secure_secret( Slice password, tl_object_ptr algo_ptr, Slice secret, int64 secret_id); @@ -162,9 +171,10 @@ class PasswordManager : public NetQueryCallback { void do_update_password_settings(UpdateSettings update_settings, PasswordFullState full_state, Promise promise); void do_update_password_settings_impl(UpdateSettings update_settings, PasswordState state, PasswordPrivateState private_state, Promise promise); + void on_get_code_length(int32 code_length); void do_get_state(Promise promise); void get_full_state(string password, Promise promise); - void do_get_secure_secret(bool recursive, string passwod, Promise promise); + void do_get_secure_secret(bool allow_recursive, string password, Promise promise); void do_get_full_state(string password, PasswordState state, Promise promise); void cache_secret(secure_storage::Secret secret); void drop_cached_secret(); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index e7b1075d..fb05f333 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -4650,6 +4650,14 @@ void Td::on_request(uint64 id, td_api::getRecoveryEmailAddress &request) { std::move(promise)); } +void Td::on_request(uint64 id, td_api::checkRecoveryEmailAddressCode &request) { + CHECK_IS_USER(); + CLEAN_INPUT_STRING(request.code_); + CREATE_OK_REQUEST_PROMISE(); + send_closure(password_manager_, &PasswordManager::check_recovery_email_address_code, request.code_, + std::move(promise)); +} + void Td::on_request(uint64 id, td_api::requestPasswordRecovery &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index b536ee70..10259a45 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -377,6 +377,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, td_api::setRecoveryEmailAddress &request); + void on_request(uint64 id, td_api::checkRecoveryEmailAddressCode &request); + void on_request(uint64 id, td_api::requestPasswordRecovery &request); void on_request(uint64 id, td_api::recoverPassword &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 15970144..bd30128f 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1360,6 +1360,10 @@ class CliClient final : public Actor { string recovery_email_address; std::tie(password, recovery_email_address) = split(args); send_request(make_tl_object(password, recovery_email_address)); + } else if (op == "grea" || op == "GetRecoveryEmailAddress") { + send_request(make_tl_object(args)); + } else if (op == "creac") { + send_request(make_tl_object(args)); } else if (op == "spncc") { send_request(make_tl_object(args, false, false)); } else if (op == "cpncc") { @@ -1370,8 +1374,6 @@ class CliClient final : public Actor { send_request(make_tl_object()); } else if (op == "rp" || op == "RecoverPassword") { send_request(make_tl_object(args)); - } else if (op == "grea" || op == "GetRecoveryEmailAddress") { - send_request(make_tl_object(args)); } else if (op == "gtp" || op == "GetTemporaryPassword") { send_request(make_tl_object()); } else if (op == "ctp" || op == "CreateTemporaryPassword") {