/* 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 "boxes/confirm_phone_box.h" #include "base/weak_ptr.h" #include "core/core_cloud_password.h" class BoxContent; namespace Storage { struct UploadSecureDone; struct UploadSecureProgress; } // namespace Storage namespace Window { class Controller; } // namespace Window namespace Passport { struct SavedCredentials { bytes::vector hashForAuth; bytes::vector hashForSecret; uint64 secretId = 0; }; class ViewController; struct FormRequest { FormRequest( UserId botId, const QString &scope, const QString &callbackUrl, const QString &publicKey, const QString &payload, const QString &errors); UserId botId; QString scope; QString callbackUrl; QString publicKey; QString payload; QString errors; }; struct UploadScanData { FullMsgId fullId; uint64 fileId = 0; int partsCount = 0; QByteArray md5checksum; bytes::vector hash; bytes::vector bytes; int offset = 0; }; class UploadScanDataPointer { public: UploadScanDataPointer(std::unique_ptr &&value); UploadScanDataPointer(UploadScanDataPointer &&other); UploadScanDataPointer &operator=(UploadScanDataPointer &&other); ~UploadScanDataPointer(); UploadScanData *get() const; operator UploadScanData*() const; explicit operator bool() const; UploadScanData *operator->() const; private: std::unique_ptr _value; }; struct Value; 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; int downloadOffset = 0; QImage image; QString error; }; struct EditFile { EditFile( not_null value, const File &fields, std::unique_ptr &&uploadData); not_null value; File fields; UploadScanDataPointer uploadData; std::shared_ptr guard; bool deleted = false; }; struct ValueField { QString text; QString error; }; struct ValueMap { std::map 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; std::unique_ptr call; QString error; }; struct Form; enum class SpecialFile { FrontSide, ReverseSide, Selfie, }; 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; Value &operator=(Value &&other) = default; bool requiresSpecialScan(SpecialFile type) const; bool scansAreFilled() const; Type type; ValueData data; std::vector scans; std::vector translations; std::map specialScans; QString error; QString scanMissingError; QString translationMissingError; std::vector scansInEdit; std::vector translationsInEdit; std::map specialScansInEdit; Verification verification; bytes::vector submitHash; bool selfieRequired = false; bool translationRequired = false; bool nativeNames = false; int editScreens = 0; mtpRequestId saveRequestId = 0; }; struct RequestedValue { explicit RequestedValue(Value::Type type); Value::Type type; bool selfieRequired = false; bool translationRequired = false; bool nativeNames = false; }; struct RequestedRow { std::vector values; }; struct Form { using Request = std::vector>; std::map values; Request request; QString privacyPolicyUrl; QVector 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; 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) && ((!newAlgo && !other.newAlgo) || (newAlgo && other.newAlgo)) && ((!newSecureAlgo && !other.newSecureAlgo) || (newSecureAlgo && other.newSecureAlgo)) && (hint == other.hint) && (unconfirmedPattern == other.unconfirmedPattern) && (confirmedEmail == other.confirmedEmail) && (hasRecovery == other.hasRecovery) && (unknownAlgo == other.unknownAlgo); } bool operator!=(const PasswordSettings &other) const { return !(*this == other); } }; struct FileKey { uint64 id = 0; int32 dcId = 0; inline bool operator==(const FileKey &other) const { return (id == other.id) && (dcId == other.dcId); } inline bool operator!=(const FileKey &other) const { return !(*this == other); } inline bool operator<(const FileKey &other) const { return (id < other.id) || ((id == other.id) && (dcId < other.dcId)); } 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 : private MTP::Sender, public base::has_weak_ptr { public: FormController( not_null controller, const FormRequest &request); void show(); UserData *bot() const; QString privacyPolicyUrl() const; std::vector> submitGetErrors(); void submitPassword(const QByteArray &password); void recoverPassword(); rpl::producer passwordError() const; const PasswordSettings &passwordSettings() const; void reloadPassword(); void reloadAndSubmitPassword(const QByteArray &password); void cancelPassword(); bool canAddScan(not_null value) const; void uploadScan(not_null value, QByteArray &&content); void deleteScan(not_null value, int fileIndex); void restoreScan(not_null value, int fileIndex); void uploadSpecialScan( not_null value, SpecialFile type, QByteArray &&content); void deleteSpecialScan( not_null value, SpecialFile type); void restoreSpecialScan( not_null value, SpecialFile type); rpl::producer<> secretReadyEvents() const; QString defaultEmail() const; QString defaultPhoneNumber() const; rpl::producer> scanUpdated() const; rpl::producer> valueSaveFinished() const; rpl::producer> verificationNeeded() const; rpl::producer> verificationUpdate() const; void verify(not_null value, const QString &code); const Form &form() const; void startValueEdit(not_null value); void cancelValueEdit(not_null value); void cancelValueVerification(not_null value); bool editValueChanged( not_null value, const ValueMap &data) const; void saveValueEdit(not_null value, ValueMap &&data); void deleteValueEdit(not_null value); bool savingValue(not_null value) const; bool uploadingScan(not_null value) const; void cancel(); void cancelSure(); rpl::lifetime &lifetime(); ~FormController(); private: using PasswordCheckCallback = Fn; struct FinalData { QVector hashes; QByteArray credentials; std::vector> errors; }; EditFile *findEditFile(const FullMsgId &fullId); EditFile *findEditFile(const FileKey &key); std::pair findFile(const FileKey &key); not_null findValue(not_null 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 &editData = {}) const; std::vector parseFiles( const QVector &data, const std::vector &editData) const; base::optional parseFile( const MTPSecureFile &data, const std::vector &editData) const; void fillDownloadedFile( File &destination, const std::vector &source) const; 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); bool validateValueSecrets(Value &value); void resetValue(Value &value); void fillErrors(); 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 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 value, int fileIndex, bool deleted); void specialScanDeleteRestore( not_null value, SpecialFile type, bool deleted); QString getPhoneFromValue(not_null value) const; QString getEmailFromValue(not_null value) const; QString getPlainTextFromValue(not_null value) const; void startPhoneVerification(not_null value); void startEmailVerification(not_null value); void valueSaveShowError(not_null value, const RPCError &error); void valueSaveFailed(not_null value); void requestPhoneCall(not_null value); void verificationError( not_null value, const QString &text); void valueEditFailed(not_null value); void clearValueEdit(not_null value); void clearValueVerification(not_null value); bool editFileChanged(const EditFile &file) const; bool isEncryptedValue(Value::Type type) const; void saveEncryptedValue(not_null value); void savePlainTextValue(not_null value); void sendSaveRequest( not_null 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 _controller; FormRequest _request; UserData *_bot = nullptr; mtpRequestId _formRequestId = 0; mtpRequestId _passwordRequestId = 0; mtpRequestId _passwordCheckRequestId = 0; PasswordSettings _password; TimeMs _lastSrpIdInvalidTime = 0; bytes::vector _passwordCheckHash; PasswordCheckCallback _passwordCheckCallback; QByteArray _savedPasswordValue; Form _form; bool _cancelled = false; mtpRequestId _recoverRequestId = 0; std::map> _fileLoaders; rpl::event_stream> _scanUpdated; rpl::event_stream> _valueSaveFinished; rpl::event_stream> _verificationNeeded; rpl::event_stream> _verificationUpdate; bytes::vector _secret; uint64 _secretId = 0; std::vector> _secretCallbacks; mtpRequestId _saveSecretRequestId = 0; rpl::event_stream<> _secretReady; rpl::event_stream _passwordError; mtpRequestId _submitRequestId = 0; bool _submitSuccess = false; bool _suggestingRestart = false; QString _serviceErrorText; base::Timer _shortPollTimer; rpl::lifetime _uploaderSubscriptions; rpl::lifetime _lifetime; std::unique_ptr _view; }; } // namespace Passport