tdlight/td/telegram/PhoneNumberManager.cpp
2024-05-13 15:55:02 +03:00

350 lines
12 KiB
C++

//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
//
// 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/PhoneNumberManager.h"
#include "td/telegram/ConfigManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/misc.h"
#include "td/telegram/SuggestedAction.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/UserManager.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
namespace td {
class SendCodeQuery final : public Td::ResultHandler {
Promise<telegram_api::object_ptr<telegram_api::auth_sentCode>> promise_;
public:
explicit SendCodeQuery(Promise<telegram_api::object_ptr<telegram_api::auth_sentCode>> &&promise)
: promise_(std::move(promise)) {
}
void send(const telegram_api::Function &send_code) {
send_query(G()->net_query_creator().create(send_code));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_sendChangePhoneCode>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
auto ptr = result_ptr.move_as_ok();
switch (ptr->get_id()) {
case telegram_api::auth_sentCodeSuccess::ID:
return on_error(Status::Error(500, "Receive invalid response"));
case telegram_api::auth_sentCode::ID:
return promise_.set_value(telegram_api::move_object_as<telegram_api::auth_sentCode>(ptr));
default:
UNREACHABLE();
}
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class RequestFirebaseSmsQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit RequestFirebaseSmsQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const telegram_api::auth_requestFirebaseSms &query) {
send_query(G()->net_query_creator().create(query));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::auth_requestFirebaseSms>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ReportMissingCodeQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ReportMissingCodeQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const telegram_api::auth_reportMissingCode &query) {
send_query(G()->net_query_creator().create(query));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::auth_reportMissingCode>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ChangePhoneQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ChangePhoneQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const string &phone_number, const string &phone_code_hash, const string &code) {
send_query(G()->net_query_creator().create(telegram_api::account_changePhone(phone_number, phone_code_hash, code)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_changePhone>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
td_->user_manager_->on_get_user(result_ptr.move_as_ok(), "ChangePhoneQuery");
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class VerifyPhoneQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit VerifyPhoneQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const string &phone_number, const string &phone_code_hash, const string &code) {
send_query(G()->net_query_creator().create(telegram_api::account_verifyPhone(phone_number, phone_code_hash, code)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_verifyPhone>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
class ConfirmPhoneQuery final : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit ConfirmPhoneQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(const string &phone_code_hash, const string &code) {
send_query(G()->net_query_creator().create(telegram_api::account_confirmPhone(phone_code_hash, code)));
}
void on_result(BufferSlice packet) final {
auto result_ptr = fetch_result<telegram_api::account_confirmPhone>(packet);
if (result_ptr.is_error()) {
return on_error(result_ptr.move_as_error());
}
promise_.set_value(Unit());
}
void on_error(Status status) final {
promise_.set_error(std::move(status));
}
};
PhoneNumberManager::PhoneNumberManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
}
void PhoneNumberManager::tear_down() {
parent_.reset();
}
void PhoneNumberManager::inc_generation() {
generation_++;
state_ = State::Ok;
send_code_helper_ = {};
}
void PhoneNumberManager::send_new_send_code_query(
const telegram_api::Function &send_code, Promise<td_api::object_ptr<td_api::authenticationCodeInfo>> &&promise) {
auto query_promise =
PromiseCreator::lambda([actor_id = actor_id(this), generation = generation_, promise = std::move(promise)](
Result<telegram_api::object_ptr<telegram_api::auth_sentCode>> r_sent_code) mutable {
send_closure(actor_id, &PhoneNumberManager::on_send_code_result, std::move(r_sent_code), generation,
std::move(promise));
});
td_->create_handler<SendCodeQuery>(std::move(query_promise))->send(send_code);
}
void PhoneNumberManager::on_send_code_result(Result<telegram_api::object_ptr<telegram_api::auth_sentCode>> r_sent_code,
int64 generation,
Promise<td_api::object_ptr<td_api::authenticationCodeInfo>> &&promise) {
G()->ignore_result_if_closing(r_sent_code);
if (r_sent_code.is_error()) {
return promise.set_error(r_sent_code.move_as_error());
}
if (generation != generation_) {
return promise.set_error(Status::Error(500, "Request was canceled"));
}
auto sent_code = r_sent_code.move_as_ok();
LOG(INFO) << "Receive " << to_string(sent_code);
switch (sent_code->type_->get_id()) {
case telegram_api::auth_sentCodeTypeSetUpEmailRequired::ID:
case telegram_api::auth_sentCodeTypeEmailCode::ID:
return promise.set_error(Status::Error(500, "Receive incorrect response"));
default:
break;
}
send_code_helper_.on_sent_code(std::move(sent_code));
state_ = State::WaitCode;
promise.set_value(send_code_helper_.get_authentication_code_info_object());
}
void PhoneNumberManager::set_phone_number(string phone_number,
td_api::object_ptr<td_api::phoneNumberAuthenticationSettings> settings,
td_api::object_ptr<td_api::PhoneNumberCodeType> type,
Promise<td_api::object_ptr<td_api::authenticationCodeInfo>> &&promise) {
inc_generation();
if (phone_number.empty()) {
return promise.set_error(Status::Error(400, "Phone number must be non-empty"));
}
if (type == nullptr) {
return promise.set_error(Status::Error(400, "Type must be non-empty"));
}
switch (type->get_id()) {
case td_api::phoneNumberCodeTypeChange::ID:
type_ = Type::ChangePhone;
send_closure(G()->config_manager(), &ConfigManager::hide_suggested_action,
SuggestedAction{SuggestedAction::Type::CheckPhoneNumber});
return send_new_send_code_query(send_code_helper_.send_change_phone_code(phone_number, settings),
std::move(promise));
case td_api::phoneNumberCodeTypeVerify::ID:
type_ = Type::VerifyPhone;
return send_new_send_code_query(send_code_helper_.send_verify_phone_code(phone_number, settings),
std::move(promise));
case td_api::phoneNumberCodeTypeConfirmOwnership::ID: {
auto hash = std::move(static_cast<td_api::phoneNumberCodeTypeConfirmOwnership *>(type.get())->hash_);
if (!clean_input_string(hash)) {
return promise.set_error(Status::Error(400, "Hash must be encoded in UTF-8"));
}
if (hash.empty()) {
return promise.set_error(Status::Error(400, "Hash must be non-empty"));
}
type_ = Type::ConfirmPhone;
return send_new_send_code_query(send_code_helper_.send_confirm_phone_code(hash, phone_number, settings),
std::move(promise));
}
default:
UNREACHABLE();
}
}
void PhoneNumberManager::send_firebase_sms(const string &token, Promise<Unit> &&promise) {
if (state_ != State::WaitCode) {
return promise.set_error(Status::Error(400, "Can't send Firebase SMS"));
}
td_->create_handler<RequestFirebaseSmsQuery>(std::move(promise))->send(send_code_helper_.request_firebase_sms(token));
}
void PhoneNumberManager::report_missing_code(const string &mobile_network_code, Promise<Unit> &&promise) {
if (state_ != State::WaitCode) {
return promise.set_error(Status::Error(400, "Can't report missing code"));
}
td_->create_handler<ReportMissingCodeQuery>(std::move(promise))
->send(send_code_helper_.report_missing_code(mobile_network_code));
}
void PhoneNumberManager::resend_authentication_code(
td_api::object_ptr<td_api::ResendCodeReason> &&reason,
Promise<td_api::object_ptr<td_api::authenticationCodeInfo>> &&promise) {
if (state_ != State::WaitCode) {
return promise.set_error(Status::Error(400, "Can't resend code"));
}
auto r_resend_code = send_code_helper_.resend_code(std::move(reason));
if (r_resend_code.is_error()) {
return promise.set_error(r_resend_code.move_as_error());
}
send_new_send_code_query(r_resend_code.move_as_ok(), std::move(promise));
}
void PhoneNumberManager::check_code(string code, Promise<Unit> &&promise) {
if (state_ != State::WaitCode) {
return promise.set_error(Status::Error(400, "Can't check code"));
}
auto query_promise = PromiseCreator::lambda(
[actor_id = actor_id(this), generation = generation_, promise = std::move(promise)](Result<Unit> result) mutable {
send_closure(actor_id, &PhoneNumberManager::on_check_code_result, std::move(result), generation,
std::move(promise));
});
switch (type_) {
case Type::ChangePhone:
td_->create_handler<ChangePhoneQuery>(std::move(query_promise))
->send(send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code);
break;
case Type::VerifyPhone:
td_->create_handler<VerifyPhoneQuery>(std::move(query_promise))
->send(send_code_helper_.phone_number().str(), send_code_helper_.phone_code_hash().str(), code);
break;
case Type::ConfirmPhone:
td_->create_handler<ConfirmPhoneQuery>(std::move(query_promise))
->send(send_code_helper_.phone_code_hash().str(), code);
break;
default:
UNREACHABLE();
}
}
void PhoneNumberManager::on_check_code_result(Result<Unit> result, int64 generation, Promise<Unit> &&promise) {
G()->ignore_result_if_closing(result);
if (result.is_error()) {
return promise.set_error(result.move_as_error());
}
if (generation != generation_) {
return promise.set_error(Status::Error(500, "Request was canceled"));
}
inc_generation();
promise.set_value(Unit());
}
} // namespace td