diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 4d87c6a9..92720968 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -95,8 +95,8 @@ authorizationStateClosing = AuthorizationState; authorizationStateClosed = AuthorizationState; -//@description Represents the current state of 2-step verification @has_password True if a 2-step verification password has been set up @password_hint Hint for the password; can be empty @has_recovery_email_address True if a recovery email has been set up @unconfirmed_recovery_email_address_pattern Pattern of the email address to which a confirmation email was sent -passwordState has_password:Bool password_hint:string has_recovery_email_address: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 has been set up @password_hint Hint for the password; can be empty @has_recovery_email_address True if a recovery email has been set up @has_passport_data True if some Telegram Passport data has been saved @unconfirmed_recovery_email_address_pattern Pattern of the email address to which a 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 Contains information about the current recovery email address @recovery_email_address Recovery email address recoveryEmailAddress recovery_email_address:string = RecoveryEmailAddress; @@ -916,10 +916,29 @@ inputPassportDataEmailAddress email_address:string = InputPassportData; allPassportData data:vector = AllPassportData; +//@class PassportDataErrorSource @description Contains description of an error in a Telegram Passport data; for bots only + +//@description A field of data contains an error. The error is considered resolved when the field's value changes @field_name Field name +passportDataErrorSourceDataField field_name:string = PassportDataErrorSource; + +//@description A file contains an error. The error is considered resolved when the file changes +passportDataErrorSourceFile = PassportDataErrorSource; + +//@description A list of attached files contains an error. The error is considered resolved when the file list changes +passportDataErrorSourceFiles = PassportDataErrorSource; + +//@description A selfie contains an error. The error is considered resolved when the file with the selfie changes +passportDataErrorSourceSelfie = PassportDataErrorSource; + + +//@description Contains description of an error in a Telegram Passport data @type Telegram Passport data type with the error @message Error message @source Error source +passportDataError type:PassportDataType message:string source:PassportDataErrorSource = PassportDataError; + + //@description Contains information about requested Telegram Passport authorization form @id Authorization form unique identifier //@required_types Types required to complete the form. If there are more than one identity document or address proof, then any of them can be chosen -//@data Already available data @is_selfie_required True, if selfie is required with an identity document @privacy_policy_url URL with the service privacy policy; can be empty -passportAuthorizationForm id:int32 required_types:vector data:vector is_selfie_required:Bool privacy_policy_url:string = PassportAuthorizationForm; +//@data Already available data @errors Errors in already available data @is_selfie_required True, if selfie is required with an identity document @privacy_policy_url URL with the service privacy policy; can be empty +passportAuthorizationForm id:int32 required_types:vector data:vector errors:vector is_selfie_required:Bool privacy_policy_url:string = PassportAuthorizationForm; //@description Contains an encrypted Telegram Passport data credentials @data The encrypted credentials @hash The decrypted data hash @secret Encrypted by service public key secret for data decryption @@ -938,7 +957,7 @@ inputPassportDataErrorSourceDataField field_name:string data_hash:bytes = InputP //@description A file contains an error. The error is considered resolved when the file changes @file_hash Hash of the file with an error inputPassportDataErrorSourceFile file_hash:bytes = InputPassportDataErrorSource; -//@description A list of attached files contains an error. The error is considered resolved when the file list changes @type Telegram Passport data type @file_hashes Hashes of all files +//@description A list of attached files contains an error. The error is considered resolved when the file list changes @file_hashes Hashes of all files inputPassportDataErrorSourceFiles file_hashes:vector = InputPassportDataErrorSource; //@description A selfie contains an error. The error is considered resolved when the file with the selfie changes @file_hash Current file with the selfie hash diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 6141d2b9..9f8d06ad 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 28594dc4..998c0921 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -463,7 +463,7 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s account.authorizations#1250abde authorizations:Vector = account.Authorizations; account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password; -account.password#d06c5fc3 current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password; +account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password; account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings; @@ -883,7 +883,6 @@ initConnection#c7481da6 {X:Type} api_id:int device_model:string system_version:s invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X; -auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone; auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode; auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; @@ -935,7 +934,6 @@ account.getAllSecureValues#b288bc7d = Vector; account.getSecureValue#73665bc2 types:Vector = Vector; account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = SecureValue; account.deleteSecureValue#b880bc4b types:Vector = Bool; -account.setSecureValueErrors#d0093ce4 user_id:InputUser errors:Vector = Bool; account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm; account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector credentials:SecureCredentialsEncrypted = Bool; account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode; @@ -945,6 +943,7 @@ account.verifyEmail#ecba39db email:string code:string = Bool; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; +users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector = Bool; contacts.getStatuses#c4a353ee = Vector; contacts.getContacts#c023849f hash:int = contacts.Contacts; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index bb5a1471..e64081c6 100644 Binary files a/td/generate/scheme/telegram_api.tlo and b/td/generate/scheme/telegram_api.tlo differ diff --git a/td/telegram/PasswordManager.cpp b/td/telegram/PasswordManager.cpp index 7572254f..648db138 100644 --- a/td/telegram/PasswordManager.cpp +++ b/td/telegram/PasswordManager.cpp @@ -447,6 +447,7 @@ void PasswordManager::do_get_state(Promise promise) { state.new_secure_salt = no_password->new_secure_salt_.as_slice().str(); secure_random = no_password->secure_random_.as_slice().str(); state.has_recovery_email_address = false; + state.has_secure_values = false; state.unconfirmed_recovery_email_address_pattern = no_password->email_unconfirmed_pattern_; } else if (result->get_id() == telegram_api::account_password::ID) { auto password = move_tl_object_as(result); @@ -456,7 +457,11 @@ void PasswordManager::do_get_state(Promise promise) { state.new_salt = password->new_salt_.as_slice().str(); state.new_secure_salt = password->new_secure_salt_.as_slice().str(); secure_random = password->secure_random_.as_slice().str(); - state.has_recovery_email_address = password->has_recovery_; + state.has_recovery_email_address = + (password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0; + state.has_secure_values = + (password->flags_ & telegram_api::account_password::HAS_SECURE_VALUES_MASK) != 0; + ; state.unconfirmed_recovery_email_address_pattern = password->email_unconfirmed_pattern_; } else { UNREACHABLE(); diff --git a/td/telegram/PasswordManager.h b/td/telegram/PasswordManager.h index c608f32c..f79f5fc2 100644 --- a/td/telegram/PasswordManager.h +++ b/td/telegram/PasswordManager.h @@ -84,6 +84,7 @@ class PasswordManager : public NetQueryCallback { bool has_password = false; string password_hint; bool has_recovery_email_address = false; + bool has_secure_values = false; string unconfirmed_recovery_email_address_pattern = ""; string current_salt; @@ -93,7 +94,7 @@ class PasswordManager : public NetQueryCallback { State as_td_api() const { return td_api::make_object(has_password, password_hint, has_recovery_email_address, - unconfirmed_recovery_email_address_pattern); + has_secure_values, unconfirmed_recovery_email_address_pattern); } }; diff --git a/td/telegram/SecureManager.cpp b/td/telegram/SecureManager.cpp index 7e19ea53..a897a203 100644 --- a/td/telegram/SecureManager.cpp +++ b/td/telegram/SecureManager.cpp @@ -33,11 +33,11 @@ class SetSecureValueErrorsQuery : public Td::ResultHandler { void send(tl_object_ptr input_user, vector> input_errors) { send_query(G()->net_query_creator().create( - create_storer(telegram_api::account_setSecureValueErrors(std::move(input_user), std::move(input_errors))))); + create_storer(telegram_api::users_setSecureValueErrors(std::move(input_user), std::move(input_errors))))); } void on_result(uint64 id, BufferSlice packet) override { - auto result_ptr = fetch_result(packet); + auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(id, result_ptr.move_as_error()); } @@ -449,9 +449,60 @@ class GetPassportAuthorizationForm : public NetQueryCallback { break; } } + + vector> errors; + for (auto &error_ptr : authorization_form_->errors_) { + CHECK(error_ptr != nullptr); + SecureValueType type = SecureValueType::None; + td_api::object_ptr source; + string message; + switch (error_ptr->get_id()) { + case telegram_api::secureValueErrorData::ID: { + auto error = move_tl_object_as(error_ptr); + type = get_secure_value_type(error->type_); + message = std::move(error->text_); + string field_name = get_secure_value_data_field_name(type, error->field_); + if (field_name.empty()) { + break; + } + source = td_api::make_object(std::move(field_name)); + break; + } + case telegram_api::secureValueErrorFile::ID: { + auto error = move_tl_object_as(error_ptr); + type = get_secure_value_type(error->type_); + message = std::move(error->text_); + source = td_api::make_object(); + break; + } + case telegram_api::secureValueErrorFiles::ID: { + auto error = move_tl_object_as(error_ptr); + type = get_secure_value_type(error->type_); + message = std::move(error->text_); + source = td_api::make_object(); + break; + } + case telegram_api::secureValueErrorSelfie::ID: { + auto error = move_tl_object_as(error_ptr); + type = get_secure_value_type(error->type_); + message = std::move(error->text_); + source = td_api::make_object(); + break; + } + default: + UNREACHABLE(); + } + if (source == nullptr) { + continue; + } + + errors.push_back(td_api::make_object(get_passport_data_type_object(type), message, + std::move(source))); + } + promise_.set_value(make_tl_object( - authorization_form_id_, get_passport_data_types_object(types), std::move(values), is_selfie_required, - authorization_form_->privacy_policy_url_)); + authorization_form_id_, get_passport_data_types_object(types), std::move(values), std::move(errors), + is_selfie_required, authorization_form_->privacy_policy_url_)); stop(); } }; diff --git a/td/telegram/SecureValue.cpp b/td/telegram/SecureValue.cpp index bed51a0f..769d5cae 100644 --- a/td/telegram/SecureValue.cpp +++ b/td/telegram/SecureValue.cpp @@ -25,6 +25,36 @@ namespace td { +StringBuilder &operator<<(StringBuilder &string_builder, const SecureValueType &type) { + switch (type) { + case SecureValueType::PersonalDetails: + return string_builder << "PersonalDetails"; + case SecureValueType::Passport: + return string_builder << "Passport"; + case SecureValueType::DriverLicense: + return string_builder << "DriverLicense"; + case SecureValueType::IdentityCard: + return string_builder << "IdentityCard"; + case SecureValueType::Address: + return string_builder << "Address"; + case SecureValueType::UtilityBill: + return string_builder << "UtilityBill"; + case SecureValueType::BankStatement: + return string_builder << "BankStatement"; + case SecureValueType::RentalAgreement: + return string_builder << "RentalAgreement"; + case SecureValueType::PhoneNumber: + return string_builder << "PhoneNumber"; + case SecureValueType::EmailAddress: + return string_builder << "EmailAddress"; + case SecureValueType::None: + return string_builder << "None"; + default: + UNREACHABLE(); + return string_builder; + } +} + SecureValueType get_secure_value_type(const tl_object_ptr &secure_value_type) { CHECK(secure_value_type != nullptr); switch (secure_value_type->get_id()) { @@ -83,14 +113,29 @@ SecureValueType get_secure_value_type_td_api(const tl_object_ptr unique_types(vector types) { + size_t size = types.size(); + for (size_t i = 0; i < size; i++) { + for (size_t j = 0; j < i; j++) { + if (types[i] == types[j]) { + LOG(ERROR) << "Have duplicate Passport Data type " << types[i] << " at positions " << i << " and " << j; + types[i--] = types[--size]; + break; + } + } + } + types.resize(size); + return types; +} + vector get_secure_value_types( const vector> &secure_value_types) { - return transform(secure_value_types, get_secure_value_type); + return unique_types(transform(secure_value_types, get_secure_value_type)); } vector get_secure_value_types_td_api( const vector> &secure_value_types) { - return transform(secure_value_types, get_secure_value_type_td_api); + return unique_types(transform(secure_value_types, get_secure_value_type_td_api)); } td_api::object_ptr get_passport_data_type_object(SecureValueType type) { @@ -156,6 +201,51 @@ vector> get_passport_data_types_obj return transform(types, get_passport_data_type_object); } +string get_secure_value_data_field_name(SecureValueType type, string field_name) { + switch (type) { + case SecureValueType::PersonalDetails: + if (field_name == "first_name" || field_name == "last_name" || field_name == "gender" || + field_name == "country_code") { + return field_name; + } + if (field_name == "birth_date") { + return "birthdate"; + } + break; + case SecureValueType::Passport: + case SecureValueType::DriverLicense: + case SecureValueType::IdentityCard: + if (field_name == "expiry_date") { + return field_name; + } + if (field_name == "document_no") { + return "number"; + } + break; + case SecureValueType::Address: + if (field_name == "state" || field_name == "city" || field_name == "street_line1" || + field_name == "street_line2" || field_name == "country_code") { + return field_name; + } + if (field_name == "post_code") { + return "postal_code"; + } + break; + case SecureValueType::UtilityBill: + case SecureValueType::BankStatement: + case SecureValueType::RentalAgreement: + case SecureValueType::PhoneNumber: + case SecureValueType::EmailAddress: + break; + case SecureValueType::None: + default: + UNREACHABLE(); + break; + } + LOG(ERROR) << "Receive error about unknown field \"" << field_name << "\" in type " << type; + return string(); +} + bool operator==(const EncryptedSecureFile &lhs, const EncryptedSecureFile &rhs) { return lhs.file_id == rhs.file_id && lhs.file_hash == rhs.file_hash && lhs.encrypted_secret == rhs.encrypted_secret; } diff --git a/td/telegram/SecureValue.h b/td/telegram/SecureValue.h index b18e1ad7..199c6ea0 100644 --- a/td/telegram/SecureValue.h +++ b/td/telegram/SecureValue.h @@ -23,7 +23,7 @@ namespace td { class FileManager; -enum class SecureValueType { +enum class SecureValueType : int32 { None, PersonalDetails, Passport, @@ -37,6 +37,8 @@ enum class SecureValueType { EmailAddress }; +StringBuilder &operator<<(StringBuilder &string_builder, const SecureValueType &type); + SecureValueType get_secure_value_type(const tl_object_ptr &secure_value_type); SecureValueType get_secure_value_type_td_api(const tl_object_ptr &passport_data_type); @@ -51,6 +53,8 @@ td_api::object_ptr get_secure_value_type_object(S vector> get_passport_data_types_object( const vector &types); +string get_secure_value_data_field_name(SecureValueType type, string field_name); + struct EncryptedSecureFile { FileId file_id; string file_hash;