603 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			603 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| This file is part of Telegram Desktop,
 | |
| the official desktop application for the Telegram messaging service.
 | |
| 
 | |
| For license and copyright information please follow this link:
 | |
| https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | |
| */
 | |
| #pragma once
 | |
| 
 | |
| #include "mtproto/sender.h"
 | |
| #include "base/timer.h"
 | |
| #include "base/weak_ptr.h"
 | |
| #include "core/core_cloud_password.h"
 | |
| 
 | |
| class mtpFileLoader;
 | |
| 
 | |
| namespace Storage {
 | |
| struct UploadSecureDone;
 | |
| struct UploadSecureProgress;
 | |
| } // namespace Storage
 | |
| 
 | |
| namespace Window {
 | |
| class SessionController;
 | |
| } // namespace Window
 | |
| 
 | |
| namespace Main {
 | |
| class Session;
 | |
| } // namespace Main
 | |
| 
 | |
| namespace Ui {
 | |
| class SentCodeCall;
 | |
| } // namespace Ui
 | |
| 
 | |
| namespace Passport {
 | |
| 
 | |
| struct EditDocumentCountry;
 | |
| 
 | |
| struct SavedCredentials {
 | |
| 	bytes::vector hashForAuth;
 | |
| 	bytes::vector hashForSecret;
 | |
| 	uint64 secretId = 0;
 | |
| };
 | |
| 
 | |
| QString NonceNameByScope(const QString &scope);
 | |
| 
 | |
| class ViewController;
 | |
| 
 | |
| struct FormRequest {
 | |
| 	FormRequest(
 | |
| 		UserId botId,
 | |
| 		const QString &scope,
 | |
| 		const QString &callbackUrl,
 | |
| 		const QString &publicKey,
 | |
| 		const QString &nonce);
 | |
| 
 | |
| 	UserId botId;
 | |
| 	QString scope;
 | |
| 	QString callbackUrl;
 | |
| 	QString publicKey;
 | |
| 	QString nonce;
 | |
| 
 | |
| };
 | |
| 
 | |
| class LoadStatus final {
 | |
| public:
 | |
| 	enum class Status {
 | |
| 		Done,
 | |
| 		InProgress,
 | |
| 		Failed,
 | |
| 	};
 | |
| 
 | |
| 	LoadStatus() = default;
 | |
| 
 | |
| 	void set(Status status, int offset = 0) {
 | |
| 		if (!offset) {
 | |
| 			offset = _offset;
 | |
| 		}
 | |
| 		_offset = (status == Status::InProgress) ? offset : 0;
 | |
| 		_status = status;
 | |
| 	}
 | |
| 
 | |
| 	int offset() const {
 | |
| 		return _offset;
 | |
| 	}
 | |
| 	Status status() const {
 | |
| 		return _status;
 | |
| 	}
 | |
| private:
 | |
| 	int _offset = 0;
 | |
| 	Status _status = Status::Done;
 | |
| };
 | |
| 
 | |
| struct UploadScanData {
 | |
| 	FullMsgId fullId;
 | |
| 	uint64 fileId = 0;
 | |
| 	int partsCount = 0;
 | |
| 	QByteArray md5checksum;
 | |
| 	bytes::vector hash;
 | |
| 	bytes::vector bytes;
 | |
| 
 | |
| 	LoadStatus status;
 | |
| };
 | |
| 
 | |
| class UploadScanDataPointer {
 | |
| public:
 | |
| 	UploadScanDataPointer(
 | |
| 		not_null<Main::Session*> session,
 | |
| 		std::unique_ptr<UploadScanData> &&value);
 | |
| 	UploadScanDataPointer(UploadScanDataPointer &&other);
 | |
| 	UploadScanDataPointer &operator=(UploadScanDataPointer &&other);
 | |
| 	~UploadScanDataPointer();
 | |
| 
 | |
| 	UploadScanData *get() const;
 | |
| 	operator UploadScanData*() const;
 | |
| 	explicit operator bool() const;
 | |
| 	UploadScanData *operator->() const;
 | |
| 
 | |
| private:
 | |
| 	not_null<Main::Session*> _session;
 | |
| 	std::unique_ptr<UploadScanData> _value;
 | |
| 
 | |
| };
 | |
| 
 | |
| struct Value;
 | |
| 
 | |
| enum class FileType {
 | |
| 	Scan,
 | |
| 	Translation,
 | |
| 	FrontSide,
 | |
| 	ReverseSide,
 | |
| 	Selfie,
 | |
| };
 | |
| 
 | |
| struct File {
 | |
| 	uint64 id = 0;
 | |
| 	uint64 accessHash = 0;
 | |
| 	int32 size = 0;
 | |
| 	int32 dcId = 0;
 | |
| 	TimeId date = 0;
 | |
| 	bytes::vector hash;
 | |
| 	bytes::vector secret;
 | |
| 	bytes::vector encryptedSecret;
 | |
| 
 | |
| 	LoadStatus downloadStatus;
 | |
| 	QImage image;
 | |
| 	QString error;
 | |
| };
 | |
| 
 | |
| struct EditFile {
 | |
| 	EditFile(
 | |
| 		not_null<Main::Session*> session,
 | |
| 		not_null<const Value*> value,
 | |
| 		FileType type,
 | |
| 		const File &fields,
 | |
| 		std::unique_ptr<UploadScanData> &&uploadData);
 | |
| 
 | |
| 	not_null<const Value*> value;
 | |
| 	FileType type;
 | |
| 	File fields;
 | |
| 	UploadScanDataPointer uploadData;
 | |
| 	std::shared_ptr<bool> guard;
 | |
| 	bool deleted = false;
 | |
| };
 | |
| 
 | |
| struct ValueField {
 | |
| 	QString text;
 | |
| 	QString error;
 | |
| };
 | |
| 
 | |
| struct ValueMap {
 | |
| 	std::map<QString, ValueField> fields;
 | |
| };
 | |
| 
 | |
| struct ValueData {
 | |
| 	QByteArray original;
 | |
| 	bytes::vector secret;
 | |
| 	ValueMap parsed;
 | |
| 	bytes::vector hash;
 | |
| 	bytes::vector encryptedSecret;
 | |
| 	ValueMap parsedInEdit;
 | |
| 	bytes::vector hashInEdit;
 | |
| 	bytes::vector encryptedSecretInEdit;
 | |
| };
 | |
| 
 | |
| struct Verification {
 | |
| 	mtpRequestId requestId = 0;
 | |
| 	QString phoneCodeHash;
 | |
| 	int codeLength = 0;
 | |
| 	QString fragmentUrl;
 | |
| 	std::unique_ptr<Ui::SentCodeCall> call;
 | |
| 
 | |
| 	QString error;
 | |
| 
 | |
| };
 | |
| 
 | |
| struct Form;
 | |
| 
 | |
| struct Value {
 | |
| 	enum class Type {
 | |
| 		PersonalDetails,
 | |
| 		Passport,
 | |
| 		DriverLicense,
 | |
| 		IdentityCard,
 | |
| 		InternalPassport,
 | |
| 		Address,
 | |
| 		UtilityBill,
 | |
| 		BankStatement,
 | |
| 		RentalAgreement,
 | |
| 		PassportRegistration,
 | |
| 		TemporaryRegistration,
 | |
| 		Phone,
 | |
| 		Email,
 | |
| 	};
 | |
| 
 | |
| 
 | |
| 	explicit Value(Type type);
 | |
| 	Value(Value &&other) = default;
 | |
| 
 | |
| 	// Some data is not parsed from server-provided values.
 | |
| 	// It should be preserved through re-parsing (for example when saving).
 | |
| 	// So we hide "operator=(Value&&)" in private and instead provide this.
 | |
| 	void fillDataFrom(Value &&other);
 | |
| 	bool requiresSpecialScan(FileType type) const;
 | |
| 	bool requiresScan(FileType type) const;
 | |
| 	bool scansAreFilled() const;
 | |
| 	void saveInEdit(not_null<Main::Session*> session);
 | |
| 	void clearEditData();
 | |
| 	bool uploadingScan() const;
 | |
| 	bool saving() const;
 | |
| 
 | |
| 	static constexpr auto kNothingFilled = 0x100;
 | |
| 	static constexpr auto kNoTranslationFilled = 0x10;
 | |
| 	static constexpr auto kNoSelfieFilled = 0x001;
 | |
| 	int whatNotFilled() const;
 | |
| 
 | |
| 	std::vector<File> &files(FileType type);
 | |
| 	const std::vector<File> &files(FileType type) const;
 | |
| 	QString &fileMissingError(FileType type);
 | |
| 	const QString &fileMissingError(FileType type) const;
 | |
| 	std::vector<EditFile> &filesInEdit(FileType type);
 | |
| 	const std::vector<EditFile> &filesInEdit(FileType type) const;
 | |
| 	EditFile &fileInEdit(FileType type, std::optional<int> fileIndex);
 | |
| 	const EditFile &fileInEdit(
 | |
| 		FileType type,
 | |
| 		std::optional<int> fileIndex) const;
 | |
| 
 | |
| 	std::vector<EditFile> takeAllFilesInEdit();
 | |
| 
 | |
| 	Type type;
 | |
| 	ValueData data;
 | |
| 	std::map<FileType, File> specialScans;
 | |
| 	QString error;
 | |
| 	std::map<FileType, EditFile> specialScansInEdit;
 | |
| 	Verification verification;
 | |
| 	bytes::vector submitHash;
 | |
| 
 | |
| 	bool selfieRequired = false;
 | |
| 	bool translationRequired = false;
 | |
| 	bool nativeNames = false;
 | |
| 	int editScreens = 0;
 | |
| 
 | |
| 	mtpRequestId saveRequestId = 0;
 | |
| 
 | |
| private:
 | |
| 	Value &operator=(Value &&other) = default;
 | |
| 
 | |
| 	std::vector<File> _scans;
 | |
| 	std::vector<File> _translations;
 | |
| 	std::vector<EditFile> _scansInEdit;
 | |
| 	std::vector<EditFile> _translationsInEdit;
 | |
| 	QString _scanMissingError;
 | |
| 	QString _translationMissingError;
 | |
| 
 | |
| };
 | |
| 
 | |
| bool ValueChanged(not_null<const Value*> value, const ValueMap &data);
 | |
| 
 | |
| struct RequestedValue {
 | |
| 	explicit RequestedValue(Value::Type type);
 | |
| 
 | |
| 	Value::Type type;
 | |
| 	bool selfieRequired = false;
 | |
| 	bool translationRequired = false;
 | |
| 	bool nativeNames = false;
 | |
| };
 | |
| 
 | |
| struct RequestedRow {
 | |
| 	std::vector<RequestedValue> values;
 | |
| };
 | |
| 
 | |
| struct Form {
 | |
| 	using Request = std::vector<std::vector<Value::Type>>;
 | |
| 
 | |
| 	std::map<Value::Type, Value> values;
 | |
| 	Request request;
 | |
| 	QString privacyPolicyUrl;
 | |
| 	QVector<MTPSecureValueError> pendingErrors;
 | |
| };
 | |
| 
 | |
| struct PasswordSettings {
 | |
| 	Core::CloudPasswordCheckRequest request;
 | |
| 	Core::CloudPasswordAlgo newAlgo;
 | |
| 	Core::SecureSecretAlgo newSecureAlgo;
 | |
| 	QString hint;
 | |
| 	QString unconfirmedPattern;
 | |
| 	QString confirmedEmail;
 | |
| 	bool hasRecovery = false;
 | |
| 	bool notEmptyPassport = false;
 | |
| 	bool unknownAlgo = false;
 | |
| 	TimeId pendingResetDate = 0;
 | |
| 
 | |
| 	bool operator==(const PasswordSettings &other) const {
 | |
| 		return (request == other.request)
 | |
| // newAlgo and newSecureAlgo are always different, because they have
 | |
| // different random parts added on the client to the server salts.
 | |
| //			&& (newAlgo == other.newAlgo)
 | |
| //			&& (newSecureAlgo == other.newSecureAlgo)
 | |
| 			&& ((v::is_null(newAlgo) && v::is_null(other.newAlgo))
 | |
| 				|| (!v::is_null(newAlgo) && !v::is_null(other.newAlgo)))
 | |
| 			&& ((v::is_null(newSecureAlgo) && v::is_null(other.newSecureAlgo))
 | |
| 				|| (!v::is_null(newSecureAlgo)
 | |
| 					&& !v::is_null(other.newSecureAlgo)))
 | |
| 			&& (hint == other.hint)
 | |
| 			&& (unconfirmedPattern == other.unconfirmedPattern)
 | |
| 			&& (confirmedEmail == other.confirmedEmail)
 | |
| 			&& (hasRecovery == other.hasRecovery)
 | |
| 			&& (unknownAlgo == other.unknownAlgo)
 | |
| 			&& (pendingResetDate == other.pendingResetDate);
 | |
| 	}
 | |
| 	bool operator!=(const PasswordSettings &other) const {
 | |
| 		return !(*this == other);
 | |
| 	}
 | |
| };
 | |
| 
 | |
| struct FileKey {
 | |
| 	uint64 id = 0;
 | |
| 
 | |
| 	inline bool operator==(const FileKey &other) const {
 | |
| 		return (id == other.id);
 | |
| 	}
 | |
| 	inline bool operator!=(const FileKey &other) const {
 | |
| 		return !(*this == other);
 | |
| 	}
 | |
| 	inline bool operator<(const FileKey &other) const {
 | |
| 		return (id < other.id);
 | |
| 	}
 | |
| 	inline bool operator>(const FileKey &other) const {
 | |
| 		return (other < *this);
 | |
| 	}
 | |
| 	inline bool operator<=(const FileKey &other) const {
 | |
| 		return !(other < *this);
 | |
| 	}
 | |
| 	inline bool operator>=(const FileKey &other) const {
 | |
| 		return !(*this < other);
 | |
| 	}
 | |
| 
 | |
| };
 | |
| 
 | |
| class FormController : public base::has_weak_ptr {
 | |
| public:
 | |
| 	FormController(
 | |
| 		not_null<Window::SessionController*> controller,
 | |
| 		const FormRequest &request);
 | |
| 
 | |
| 	[[nodiscard]] not_null<Window::SessionController*> window() const {
 | |
| 		return _controller;
 | |
| 	}
 | |
| 	[[nodiscard]] Main::Session &session() const;
 | |
| 
 | |
| 	void show();
 | |
| 	UserData *bot() const;
 | |
| 	QString privacyPolicyUrl() const;
 | |
| 	std::vector<not_null<const Value*>> submitGetErrors();
 | |
| 	void submitPassword(const QByteArray &password);
 | |
| 	void recoverPassword();
 | |
| 	rpl::producer<QString> passwordError() const;
 | |
| 	const PasswordSettings &passwordSettings() const;
 | |
| 	void reloadPassword();
 | |
| 	void reloadAndSubmitPassword(const QByteArray &password);
 | |
| 	void cancelPassword();
 | |
| 
 | |
| 	bool canAddScan(not_null<const Value*> value, FileType type) const;
 | |
| 	void uploadScan(
 | |
| 		not_null<const Value*> value,
 | |
| 		FileType type,
 | |
| 		QByteArray &&content);
 | |
| 	void deleteScan(
 | |
| 		not_null<const Value*> value,
 | |
| 		FileType type,
 | |
| 		std::optional<int> fileIndex);
 | |
| 	void restoreScan(
 | |
| 		not_null<const Value*> value,
 | |
| 		FileType type,
 | |
| 		std::optional<int> fileIndex);
 | |
| 
 | |
| 	rpl::producer<> secretReadyEvents() const;
 | |
| 
 | |
| 	QString defaultEmail() const;
 | |
| 	QString defaultPhoneNumber() const;
 | |
| 
 | |
| 	rpl::producer<not_null<const EditFile*>> scanUpdated() const;
 | |
| 	rpl::producer<not_null<const Value*>> valueSaveFinished() const;
 | |
| 	rpl::producer<not_null<const Value*>> verificationNeeded() const;
 | |
| 	rpl::producer<not_null<const Value*>> verificationUpdate() const;
 | |
| 	void verify(not_null<const Value*> value, const QString &code);
 | |
| 
 | |
| 	const Form &form() const;
 | |
| 	void startValueEdit(not_null<const Value*> value);
 | |
| 	void cancelValueEdit(not_null<const Value*> value);
 | |
| 	void cancelValueVerification(not_null<const Value*> value);
 | |
| 	void saveValueEdit(not_null<const Value*> value, ValueMap &&data);
 | |
| 	void deleteValueEdit(not_null<const Value*> value);
 | |
| 
 | |
| 	void cancel();
 | |
| 	void cancelSure();
 | |
| 
 | |
| 	[[nodiscard]] rpl::producer<EditDocumentCountry> preferredLanguage(
 | |
| 		const QString &countryCode);
 | |
| 
 | |
| 	rpl::lifetime &lifetime();
 | |
| 
 | |
| 	~FormController();
 | |
| 
 | |
| private:
 | |
| 	using PasswordCheckCallback = Fn<void(
 | |
| 		const Core::CloudPasswordResult &check)>;
 | |
| 
 | |
| 	struct FinalData {
 | |
| 		QVector<MTPSecureValueHash> hashes;
 | |
| 		QByteArray credentials;
 | |
| 		std::vector<not_null<const Value*>> errors;
 | |
| 	};
 | |
| 
 | |
| 	template <typename Condition>
 | |
| 	EditFile *findEditFileByCondition(Condition &&condition);
 | |
| 	EditFile *findEditFile(const FullMsgId &fullId);
 | |
| 	EditFile *findEditFile(const FileKey &key);
 | |
| 	std::pair<Value*, File*> findFile(const FileKey &key);
 | |
| 	not_null<Value*> findValue(not_null<const Value*> value);
 | |
| 
 | |
| 	void requestForm();
 | |
| 	void requestPassword();
 | |
| 
 | |
| 	void formDone(const MTPaccount_AuthorizationForm &result);
 | |
| 	void formFail(const QString &error);
 | |
| 	bool parseForm(const MTPaccount_AuthorizationForm &result);
 | |
| 	void showForm();
 | |
| 	Value parseValue(
 | |
| 		const MTPSecureValue &value,
 | |
| 		const std::vector<EditFile> &editData = {}) const;
 | |
| 	std::vector<File> parseFiles(
 | |
| 		const QVector<MTPSecureFile> &data,
 | |
| 		const std::vector<EditFile> &editData) const;
 | |
| 	std::optional<File> parseFile(
 | |
| 		const MTPSecureFile &data,
 | |
| 		const std::vector<EditFile> &editData) const;
 | |
| 	void fillDownloadedFile(
 | |
| 		File &destination,
 | |
| 		const std::vector<EditFile> &source) const;
 | |
| 	bool handleAppUpdateError(const QString &error);
 | |
| 
 | |
| 	void submitPassword(
 | |
| 		const Core::CloudPasswordResult &check,
 | |
| 		const QByteArray &password,
 | |
| 		bool submitSaved);
 | |
| 	void checkPasswordHash(
 | |
| 		mtpRequestId &guard,
 | |
| 		bytes::vector hash,
 | |
| 		PasswordCheckCallback callback);
 | |
| 	bool handleSrpIdInvalid(mtpRequestId &guard);
 | |
| 	void requestPasswordData(mtpRequestId &guard);
 | |
| 	void passwordChecked();
 | |
| 	void passwordServerError();
 | |
| 	void passwordDone(const MTPaccount_Password &result);
 | |
| 	bool applyPassword(const MTPDaccount_password &settings);
 | |
| 	bool applyPassword(PasswordSettings &&settings);
 | |
| 	bytes::vector passwordHashForAuth(bytes::const_span password) const;
 | |
| 	void checkSavedPasswordSettings(const SavedCredentials &credentials);
 | |
| 	void checkSavedPasswordSettings(
 | |
| 		const Core::CloudPasswordResult &check,
 | |
| 		const SavedCredentials &credentials);
 | |
| 	void validateSecureSecret(
 | |
| 		bytes::const_span encryptedSecret,
 | |
| 		bytes::const_span passwordHashForSecret,
 | |
| 		bytes::const_span passwordBytes,
 | |
| 		uint64 serverSecretId);
 | |
| 	void decryptValues();
 | |
| 	void decryptValue(Value &value) const;
 | |
| 	bool validateValueSecrets(Value &value) const;
 | |
| 	void resetValue(Value &value) const;
 | |
| 	void fillErrors();
 | |
| 	void fillNativeFromFallback();
 | |
| 
 | |
| 	void loadFile(File &file);
 | |
| 	void fileLoadDone(FileKey key, const QByteArray &bytes);
 | |
| 	void fileLoadProgress(FileKey key, int offset);
 | |
| 	void fileLoadFail(FileKey key);
 | |
| 	void generateSecret(bytes::const_span password);
 | |
| 	void saveSecret(
 | |
| 		const Core::CloudPasswordResult &check,
 | |
| 		const SavedCredentials &saved,
 | |
| 		const bytes::vector &secret);
 | |
| 
 | |
| 	void subscribeToUploader();
 | |
| 	void encryptFile(
 | |
| 		EditFile &file,
 | |
| 		QByteArray &&content,
 | |
| 		Fn<void(UploadScanData &&result)> callback);
 | |
| 	void prepareFile(
 | |
| 		EditFile &file,
 | |
| 		const QByteArray &content);
 | |
| 	void uploadEncryptedFile(
 | |
| 		EditFile &file,
 | |
| 		UploadScanData &&data);
 | |
| 	void scanUploadDone(const Storage::UploadSecureDone &data);
 | |
| 	void scanUploadProgress(const Storage::UploadSecureProgress &data);
 | |
| 	void scanUploadFail(const FullMsgId &fullId);
 | |
| 	void scanDeleteRestore(
 | |
| 		not_null<const Value*> value,
 | |
| 		FileType type,
 | |
| 		std::optional<int> fileIndex,
 | |
| 		bool deleted);
 | |
| 
 | |
| 	QString getPhoneFromValue(not_null<const Value*> value) const;
 | |
| 	QString getEmailFromValue(not_null<const Value*> value) const;
 | |
| 	QString getPlainTextFromValue(not_null<const Value*> value) const;
 | |
| 	void startPhoneVerification(not_null<Value*> value);
 | |
| 	void startEmailVerification(not_null<Value*> value);
 | |
| 	void valueSaveShowError(not_null<Value*> value, const MTP::Error &error);
 | |
| 	void valueSaveFailed(not_null<Value*> value);
 | |
| 	void requestPhoneCall(not_null<Value*> value);
 | |
| 	void verificationError(
 | |
| 		not_null<Value*> value,
 | |
| 		const QString &text);
 | |
| 	void valueEditFailed(not_null<Value*> value);
 | |
| 	void clearValueEdit(not_null<Value*> value);
 | |
| 	void clearValueVerification(not_null<Value*> value);
 | |
| 
 | |
| 	bool isEncryptedValue(Value::Type type) const;
 | |
| 	void saveEncryptedValue(not_null<Value*> value);
 | |
| 	void savePlainTextValue(not_null<Value*> value);
 | |
| 	void sendSaveRequest(
 | |
| 		not_null<Value*> value,
 | |
| 		const MTPInputSecureValue &data);
 | |
| 	FinalData prepareFinalData();
 | |
| 
 | |
| 	void suggestReset(bytes::vector password);
 | |
| 	void resetSecret(
 | |
| 		const Core::CloudPasswordResult &check,
 | |
| 		const bytes::vector &password);
 | |
| 	void suggestRestart();
 | |
| 	void cancelAbort();
 | |
| 	void shortPollEmailConfirmation();
 | |
| 
 | |
| 	not_null<Window::SessionController*> _controller;
 | |
| 	MTP::Sender _api;
 | |
| 	FormRequest _request;
 | |
| 	UserData *_bot = nullptr;
 | |
| 
 | |
| 	mtpRequestId _formRequestId = 0;
 | |
| 	mtpRequestId _passwordRequestId = 0;
 | |
| 	mtpRequestId _passwordCheckRequestId = 0;
 | |
| 
 | |
| 	PasswordSettings _password;
 | |
| 	crl::time _lastSrpIdInvalidTime = 0;
 | |
| 	bytes::vector _passwordCheckHash;
 | |
| 	PasswordCheckCallback _passwordCheckCallback;
 | |
| 	QByteArray _savedPasswordValue;
 | |
| 	Form _form;
 | |
| 	bool _cancelled = false;
 | |
| 	mtpRequestId _recoverRequestId = 0;
 | |
| 	base::flat_map<FileKey, std::unique_ptr<mtpFileLoader>> _fileLoaders;
 | |
| 
 | |
| 	struct {
 | |
| 		int32 hash = 0;
 | |
| 		std::map<QString, QString> languagesByCountryCode;
 | |
| 	} _passportConfig;
 | |
| 
 | |
| 	rpl::event_stream<not_null<const EditFile*>> _scanUpdated;
 | |
| 	rpl::event_stream<not_null<const Value*>> _valueSaveFinished;
 | |
| 	rpl::event_stream<not_null<const Value*>> _verificationNeeded;
 | |
| 	rpl::event_stream<not_null<const Value*>> _verificationUpdate;
 | |
| 
 | |
| 	bytes::vector _secret;
 | |
| 	uint64 _secretId = 0;
 | |
| 	std::vector<Fn<void()>> _secretCallbacks;
 | |
| 	mtpRequestId _saveSecretRequestId = 0;
 | |
| 	rpl::event_stream<> _secretReady;
 | |
| 	rpl::event_stream<QString> _passwordError;
 | |
| 	mtpRequestId _submitRequestId = 0;
 | |
| 	bool _submitSuccess = false;
 | |
| 	bool _suggestingRestart = false;
 | |
| 	QString _serviceErrorText;
 | |
| 	base::Timer _shortPollTimer;
 | |
| 
 | |
| 	rpl::lifetime _uploaderSubscriptions;
 | |
| 	rpl::lifetime _lifetime;
 | |
| 
 | |
| 	std::unique_ptr<ViewController> _view;
 | |
| 
 | |
| };
 | |
| 
 | |
| } // namespace Passport
 | 
