From 6fc5e2288232ce7bf6205efd43c26b4d0dc9c276 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 15 Jun 2020 20:25:02 +0400 Subject: [PATCH] Allow several accounts in Core::App. --- Telegram/CMakeLists.txt | 4 + Telegram/SourceFiles/boxes/passcode_box.cpp | 7 +- Telegram/SourceFiles/calls/calls_panel.cpp | 2 +- .../chat_helpers/emoji_keywords.cpp | 4 +- Telegram/SourceFiles/core/application.cpp | 84 +++--- Telegram/SourceFiles/core/application.h | 11 +- Telegram/SourceFiles/core/changelogs.cpp | 8 +- Telegram/SourceFiles/intro/intro_code.cpp | 1 - Telegram/SourceFiles/intro/intro_qr.cpp | 2 - .../SourceFiles/lang/lang_cloud_manager.cpp | 7 +- Telegram/SourceFiles/main/main_account.cpp | 72 ++++-- Telegram/SourceFiles/main/main_account.h | 16 +- Telegram/SourceFiles/main/main_accounts.cpp | 112 ++++++++ Telegram/SourceFiles/main/main_accounts.h | 61 +++++ Telegram/SourceFiles/main/main_app_config.cpp | 1 - Telegram/SourceFiles/main/main_session.cpp | 13 +- Telegram/SourceFiles/mainwindow.cpp | 29 +-- .../media/player/media_player_instance.cpp | 4 +- .../media/view/media_view_overlay_widget.cpp | 13 +- Telegram/SourceFiles/settings.cpp | 1 - Telegram/SourceFiles/settings.h | 1 - .../details/storage_file_utilities.cpp | 24 ++ .../storage/details/storage_file_utilities.h | 3 + .../details/storage_settings_scheme.cpp | 12 +- .../storage/details/storage_settings_scheme.h | 3 + Telegram/SourceFiles/storage/localstorage.cpp | 17 +- .../SourceFiles/storage/storage_account.cpp | 131 ++++------ .../SourceFiles/storage/storage_account.h | 33 +-- .../SourceFiles/storage/storage_accounts.cpp | 243 ++++++++++++++++++ .../SourceFiles/storage/storage_accounts.h | 73 ++++++ Telegram/SourceFiles/window/main_window.cpp | 22 +- Telegram/SourceFiles/window/main_window.h | 9 +- .../window/themes/window_theme.cpp | 5 +- .../SourceFiles/window/window_controller.cpp | 43 +++- .../SourceFiles/window/window_controller.h | 15 +- .../window/window_lock_widgets.cpp | 15 +- 36 files changed, 834 insertions(+), 267 deletions(-) create mode 100644 Telegram/SourceFiles/main/main_accounts.cpp create mode 100644 Telegram/SourceFiles/main/main_accounts.h create mode 100644 Telegram/SourceFiles/storage/storage_accounts.cpp create mode 100644 Telegram/SourceFiles/storage/storage_accounts.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index cd5431495..23b203112 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -668,6 +668,8 @@ PRIVATE lang/lang_values.h main/main_account.cpp main/main_account.h + main/main_accounts.cpp + main/main_accounts.h main/main_app_config.cpp main/main_app_config.h main/main_session.cpp @@ -912,6 +914,8 @@ PRIVATE storage/serialize_document.h storage/storage_account.cpp storage/storage_account.h + storage/storage_accounts.cpp + storage/storage_accounts.h storage/storage_cloud_blob.cpp storage/storage_cloud_blob.h storage/storage_facade.cpp diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 804a67c10..865cf1235 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -14,8 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "apiwrap.h" #include "main/main_session.h" +#include "main/main_accounts.h" #include "core/application.h" -#include "storage/storage_account.h" +#include "storage/storage_accounts.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" @@ -451,7 +452,7 @@ void PasscodeBox::save(bool force) { return; } - if (_session->local().checkPasscode(old.toUtf8())) { + if (Core::App().accounts().local().checkPasscode(old.toUtf8())) { cSetPasscodeBadTries(0); if (_turningOff) pwd = conf = QString(); } else { @@ -519,7 +520,7 @@ void PasscodeBox::save(bool force) { closeReplacedBy(); const auto weak = Ui::MakeWeak(this); cSetPasscodeBadTries(0); - _session->local().setPasscode(pwd.toUtf8()); + Core::App().accounts().local().setPasscode(pwd.toUtf8()); Core::App().localPasscodeChanged(); if (weak) { closeBox(); diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index abf7c9ec7..465799906 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -315,7 +315,7 @@ Panel::Panel(not_null call) _cancel->setDuration(st::callPanelDuration); setMouseTracking(true); - setWindowIcon(Window::CreateIcon(&_user->account())); + setWindowIcon(Window::CreateIcon(&_user->session())); initControls(); initLayout(); showAndActivate(); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp index a06b55203..dc93f4561 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp @@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "base/platform/base_platform_info.h" #include "ui/emoji_config.h" -#include "main/main_account.h" +#include "main/main_accounts.h" #include "main/main_session.h" #include "apiwrap.h" @@ -516,7 +516,7 @@ void EmojiKeywords::langPackRefreshed() { } void EmojiKeywords::handleSessionChanges() { - Core::App().activeAccount().sessionValue( // #TODO multi someSessionValue + Core::App().accounts().activeSessionValue( // #TODO multi someSessionValue ) | rpl::map([](Main::Session *session) { return session ? &session->api() : nullptr; }) | rpl::start_with_next([=](ApiWrap *api) { diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 0b3768971..1389b56a7 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -21,13 +21,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/launcher.h" #include "core/ui_integration.h" #include "chat_helpers/emoji_keywords.h" -#include "storage/localstorage.h" #include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" #include "mainwindow.h" #include "dialogs/dialogs_entry.h" #include "history/history.h" -#include "main/main_session.h" #include "apiwrap.h" #include "api/api_updates.h" #include "calls/calls_instance.h" @@ -35,10 +33,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_translator.h" #include "lang/lang_cloud_manager.h" #include "lang/lang_hardcoded.h" -#include "storage/storage_databases.h" #include "mainwidget.h" #include "core/file_utilities.h" #include "main/main_account.h" +#include "main/main_accounts.h" +#include "main/main_session.h" #include "media/view/media_view_overlay_widget.h" #include "mtproto/dc_options.h" #include "mtproto/mtp_instance.h" @@ -56,7 +55,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/emoji_config.h" #include "ui/effects/animations.h" #include "storage/serialize_common.h" -#include "storage/storage_account.h" +#include "storage/storage_accounts.h" +#include "storage/storage_databases.h" +#include "storage/localstorage.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "base/qthelp_regex.h" @@ -95,7 +96,7 @@ Application::Application(not_null launcher) , _databases(std::make_unique()) , _animationsManager(std::make_unique()) , _dcOptions(std::make_unique()) -, _account(std::make_unique(cDataFile())) +, _accounts(std::make_unique(cDataFile())) , _langpack(std::make_unique()) , _langCloudManager(std::make_unique(langpack())) , _emojiKeywords(std::make_unique()) @@ -113,22 +114,22 @@ Application::Application(not_null launcher) _shouldLockAt = 0; }, _lifetime); - activeAccount().sessionChanges( // #TODO multi activeSessionValue + accounts().activeSessionChanges( ) | rpl::start_with_next([=](Main::Session *session) { - if (_mediaView) { - hideMediaView(); - _mediaView->clearData(); - } if (session && !UpdaterDisabled()) { // #TODO multi someSessionValue UpdateChecker().setMtproto(session); } }, _lifetime); - activeAccount().mtpChanges( + accounts().activeValue( + ) | rpl::map([=](Main::Account *account) { + return account ? account->mtpValue() : rpl::never(); + }) | rpl::flatten_latest( ) | rpl::filter([=](MTP::Instance *instance) { return instance != nullptr; }) | rpl::start_with_next([=] { if (_window) { + // Global::DesktopNotify is used in updateTrayMenu. // This should be called when user settings are read. // Right now after they are read the startMtp() is called. _window->widget()->updateTrayMenu(); @@ -137,7 +138,11 @@ Application::Application(not_null launcher) } Application::~Application() { + // Depend on activeWindow() for now :( + Shortcuts::Finish(); + _window.reset(); + if (_mediaView) { _mediaView->clearData(); _mediaView = nullptr; @@ -224,7 +229,7 @@ void Application::run() { // Create mime database, so it won't be slow later. QMimeDatabase().mimeTypeForName(qsl("text/plain")); - _window = std::make_unique(&activeAccount()); + _window = std::make_unique(); QCoreApplication::instance()->installEventFilter(this); connect( @@ -235,24 +240,19 @@ void Application::run() { DEBUG_LOG(("Application Info: window created...")); + // Depend on activeWindow() for now :( startShortcuts(); + App::initMedia(); - const auto state = activeAccount().local().start(QByteArray()); + const auto state = accounts().start(QByteArray()); if (state == Storage::StartResult::IncorrectPasscode) { Global::SetLocalPasscode(true); Global::RefLocalPasscodeChanged().notify(); lockByPasscode(); DEBUG_LOG(("Application Info: passcode needed...")); } else { - DEBUG_LOG(("Application Info: local map read...")); - activeAccount().startMtp(); - DEBUG_LOG(("Application Info: MTP started...")); - if (activeAccount().sessionExists()) { - _window->setupMain(); - } else { - _window->setupIntro(); - } + _window->showAccount(&activeAccount()); } _window->widget()->show(); @@ -552,6 +552,10 @@ void Application::writeInstallBetaVersionsSetting() { _launcher->writeInstallBetaVersionsSetting(); } +Main::Account &Application::activeAccount() const { + return _accounts->active(); +} + bool Application::exportPreventsQuit() { if (!activeAccount().sessionExists() || !activeAccount().session().data().exportInProgress()) { @@ -564,25 +568,28 @@ bool Application::exportPreventsQuit() { } int Application::unreadBadge() const { - return activeAccount().sessionExists() + return (accounts().started() && activeAccount().sessionExists()) ? activeAccount().session().data().unreadBadge() : 0; } bool Application::unreadBadgeMuted() const { - return activeAccount().sessionExists() + return (accounts().started() && activeAccount().sessionExists()) ? activeAccount().session().data().unreadBadgeMuted() : false; } bool Application::offerLegacyLangPackSwitch() const { - // #TODO multi we offer only if we were upgraded from an old authed app. - return activeAccount().sessionExists(); + return (accounts().list().size() == 1) && activeAccount().sessionExists(); } bool Application::canApplyLangPackWithoutRestart() const { - // #TODO multi we can't if at least one account is authorized. - return !activeAccount().sessionExists(); + for (const auto &[index, account] : accounts().list()) { + if (account->sessionExists()) { + return false; + } + } + return true; } void Application::setInternalLinkDomain(const QString &domain) const { @@ -674,10 +681,6 @@ void Application::lockByPasscode() { void Application::unlockPasscode() { clearPasscodeLock(); - if (!activeAccount().mtp()) { - // We unlocked initial passcode, so we just start mtproto. - activeAccount().startMtp(); - } if (_window) { _window->clearPasscodeLock(); } @@ -720,10 +723,20 @@ void Application::lockByTerms(const Window::TermsLock &data) { } } +bool Application::someSessionExists() const { + const auto &list = _accounts->list(); + for (const auto &[index, account] : list) { + if (account->sessionExists()) { + return true; + } + } + return false; +} + void Application::checkAutoLock() { if (!Global::LocalPasscode() || passcodeLocked() - || !_account->sessionExists()) { + || !someSessionExists()) { _shouldLockAt = 0; _autoLockTimer.cancel(); return; @@ -949,6 +962,13 @@ void Application::quitDelayed() { void Application::startShortcuts() { Shortcuts::Start(); + _accounts->activeSessionChanges( + ) | rpl::start_with_next([=](Main::Session *session) { + const auto support = session && session->supportMode(); + Shortcuts::ToggleSupportShortcuts(support); + Platform::SetApplicationIcon(Window::CreateIcon(session)); + }, _lifetime); + Shortcuts::Requests( ) | rpl::start_with_next([=](not_null request) { using Command = Shortcuts::Command; diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 498f5f278..42307bab1 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -36,6 +36,7 @@ void quit(); } // namespace App namespace Main { +class Accounts; class Account; class Session; } // namespace Main @@ -150,10 +151,12 @@ public: return *_databases; } - // Account component. - [[nodiscard]] Main::Account &activeAccount() const { - return *_account; + // Accounts component. + [[nodiscard]] Main::Accounts &accounts() const { + return *_accounts; } + [[nodiscard]] Main::Account &activeAccount() const; + [[nodiscard]] bool someSessionExists() const; [[nodiscard]] bool exportPreventsQuit(); // Main::Session component. @@ -278,7 +281,7 @@ private: const std::unique_ptr _databases; const std::unique_ptr _animationsManager; const std::unique_ptr _dcOptions; - const std::unique_ptr _account; + const std::unique_ptr _accounts; std::unique_ptr _window; std::unique_ptr _mediaView; const std::unique_ptr _langpack; diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index bbe5b2d1c..995ff2bad 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -7,9 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "core/changelogs.h" -#include "storage/storage_account.h" #include "lang/lang_keys.h" +#include "core/application.h" +#include "main/main_accounts.h" #include "main/main_session.h" +#include "storage/storage_accounts.h" #include "data/data_session.h" #include "mainwindow.h" #include "apiwrap.h" @@ -80,7 +82,9 @@ Changelogs::Changelogs(not_null session, int oldVersion) std::unique_ptr Changelogs::Create( not_null session) { - const auto oldVersion = session->local().oldMapVersion(); + auto &local = Core::App().accounts().local(); + const auto oldVersion = local.oldVersion(); + local.clearOldVersion(); return (oldVersion > 0 && oldVersion < AppVersion) ? std::make_unique(session, oldVersion) : nullptr; diff --git a/Telegram/SourceFiles/intro/intro_code.cpp b/Telegram/SourceFiles/intro/intro_code.cpp index 0d6f56405..b0e8ce2ae 100644 --- a/Telegram/SourceFiles/intro/intro_code.cpp +++ b/Telegram/SourceFiles/intro/intro_code.cpp @@ -248,7 +248,6 @@ void CodeWidget::codeSubmitDone(const MTPauth_Authorization &result) { showError(rpl::single(Lang::Hard::ServerError())); return; } - cSetLoggedPhoneNumber(getData()->phone); finish(data.vuser()); }, [&](const MTPDauth_authorizationSignUpRequired &data) { if (const auto terms = data.vterms_of_service()) { diff --git a/Telegram/SourceFiles/intro/intro_qr.cpp b/Telegram/SourceFiles/intro/intro_qr.cpp index 936db11f4..84daee0b3 100644 --- a/Telegram/SourceFiles/intro/intro_qr.cpp +++ b/Telegram/SourceFiles/intro/intro_qr.cpp @@ -373,8 +373,6 @@ void QrWidget::done(const MTPauth_Authorization &authorization) { showError(rpl::single(Lang::Hard::ServerError())); return; } - const auto phone = data.vuser().c_user().vphone().value_or_empty(); - cSetLoggedPhoneNumber(phone); finish(data.vuser()); }, [&](const MTPDauth_authorizationSignUpRequired &data) { _requestId = 0; diff --git a/Telegram/SourceFiles/lang/lang_cloud_manager.cpp b/Telegram/SourceFiles/lang/lang_cloud_manager.cpp index bbfb74155..61ae993dc 100644 --- a/Telegram/SourceFiles/lang/lang_cloud_manager.cpp +++ b/Telegram/SourceFiles/lang/lang_cloud_manager.cpp @@ -13,8 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtp_instance.h" #include "storage/localstorage.h" #include "core/application.h" -#include "main/main_session.h" #include "main/main_account.h" +#include "main/main_accounts.h" #include "boxes/confirm_box.h" #include "ui/wrap/padding_wrap.h" #include "ui/widgets/labels.h" @@ -158,7 +158,10 @@ Language ParseLanguage(const MTPLangPackLanguage &data) { CloudManager::CloudManager(Instance &langpack) : _langpack(langpack) { - Core::App().activeAccount().mtpValue( // #TODO multi activeAccountValue + Core::App().accounts().activeValue( + ) | rpl::map([=](Main::Account *account) { + return account ? account->mtpValue() : rpl::never(); + }) | rpl::flatten_latest( ) | rpl::start_with_next([=](MTP::Instance *instance) { if (instance) { _api.emplace(instance); diff --git a/Telegram/SourceFiles/main/main_account.cpp b/Telegram/SourceFiles/main/main_account.cpp index 3fa341200..7e182343b 100644 --- a/Telegram/SourceFiles/main/main_account.cpp +++ b/Telegram/SourceFiles/main/main_account.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/launcher.h" #include "core/shortcuts.h" #include "storage/storage_account.h" +#include "storage/storage_accounts.h" // Storage::StartResult. #include "storage/serialize_common.h" #include "storage/localstorage.h" #include "data/data_session.h" @@ -26,16 +27,49 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" namespace Main { +namespace { -Account::Account(const QString &dataName) -: _local(std::make_unique(this, dataName)) -, _appConfig(std::make_unique(this)) { - watchProxyChanges(); - watchSessionChanges(); +[[nodiscard]] QString ComposeDataString(const QString &dataName, int index) { + auto result = dataName; + result.replace('#', QString()); + if (index > 0) { + result += '#' + QString::number(index + 1); + } + return result; +} + +} // namespace + +Account::Account(const QString &dataName, int index) +: _local(std::make_unique( + this, + ComposeDataString(dataName, index))) { } Account::~Account() = default; +[[nodiscard]] Storage::StartResult Account::legacyStart( + const QByteArray &passcode) { + Expects(!_appConfig); + + const auto result = _local->legacyStart(passcode); + if (result == Storage::StartResult::Success) { + finishStarting(); + } + return result; +} + +void Account::start(std::shared_ptr localKey) { + _local->start(std::move(localKey)); + finishStarting(); +} + +void Account::finishStarting() { + _appConfig = std::make_unique(this); + watchProxyChanges(); + watchSessionChanges(); +} + void Account::watchProxyChanges() { using ProxyChange = Core::Application::ProxyChange; @@ -60,28 +94,17 @@ void Account::watchProxyChanges() { void Account::watchSessionChanges() { sessionChanges( - ) | rpl::start_with_next([=] { - crl::on_main(this, [=] { - const auto phone = sessionExists() - ? session().user()->phone() - : QString(); - const auto support = sessionExists() && session().supportMode(); - if (cLoggedPhoneNumber() != phone) { - cSetLoggedPhoneNumber(phone); - if (_mtp) { - _mtp->setUserPhone(phone); - } - Local::writeSettings(); - } - if (_mtp) { - _mtp->requestConfig(); - } - Shortcuts::ToggleSupportShortcuts(support); - Platform::SetApplicationIcon(Window::CreateIcon(this)); - }); + ) | rpl::start_with_next([=](Session *session) { + if (!session && _mtp) { + _mtp->setUserPhone(QString()); + } }, _lifetime); } +UserId Account::willHaveUserId() const { + return _sessionUserId; +} + void Account::createSession(const MTPUser &user) { createSession(user, QByteArray(), 0, Settings()); } @@ -330,7 +353,6 @@ void Account::startMtp() { Core::App().dcOptions(), MTP::Instance::Mode::Normal, std::move(config)); - _mtp->setUserPhone(cLoggedPhoneNumber()); _mtp->writeKeysRequests( ) | rpl::start_with_next([=] { local().writeMtpData(); diff --git a/Telegram/SourceFiles/main/main_account.h b/Telegram/SourceFiles/main/main_account.h index f46065e25..f4676ee92 100644 --- a/Telegram/SourceFiles/main/main_account.h +++ b/Telegram/SourceFiles/main/main_account.h @@ -13,8 +13,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Storage { class Account; +enum class StartResult : uchar; } // namespace Storage +namespace MTP { +class AuthKey; +} // namespace MTP + namespace Main { class Session; @@ -23,12 +28,17 @@ class AppConfig; class Account final : public base::has_weak_ptr { public: - explicit Account(const QString &dataName); + Account(const QString &dataName, int index); ~Account(); Account(const Account &other) = delete; Account &operator=(const Account &other) = delete; + [[nodiscard]] Storage::StartResult legacyStart( + const QByteArray &passcode); + void start(std::shared_ptr localKey); + + [[nodiscard]] UserId willHaveUserId() const; void createSession(const MTPUser &user); void createSession( UserId id, @@ -41,8 +51,11 @@ public: void forcedLogOut(); [[nodiscard]] AppConfig &appConfig() const { + Expects(_appConfig != nullptr); + return *_appConfig; } + [[nodiscard]] Storage::Account &local() const { return *_local; } @@ -87,6 +100,7 @@ private: QByteArray serialized, int streamVersion, Settings &&settings); + void finishStarting(); void watchProxyChanges(); void watchSessionChanges(); bool checkForUpdates(const mtpPrime *from, const mtpPrime *end); diff --git a/Telegram/SourceFiles/main/main_accounts.cpp b/Telegram/SourceFiles/main/main_accounts.cpp new file mode 100644 index 000000000..4528cc529 --- /dev/null +++ b/Telegram/SourceFiles/main/main_accounts.cpp @@ -0,0 +1,112 @@ +/* +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 +*/ +#include "main/main_accounts.h" + +#include "core/application.h" +#include "core/shortcuts.h" +#include "main/main_account.h" +#include "main/main_session.h" +#include "storage/storage_accounts.h" +#include "storage/localstorage.h" + +namespace Main { + +Accounts::Accounts(const QString &dataName) +: _dataName(dataName) +, _local(std::make_unique(this, dataName)) { +} + +Accounts::~Accounts() = default; + +bool Accounts::started() const { + return !_accounts.empty(); +} + +Storage::StartResult Accounts::start(const QByteArray &passcode) { + Expects(!started()); + + auto active = -1; + const auto callback = [&](int index, std::unique_ptr account) { + Expects(account != nullptr); + Expects(!_accounts.contains(index)); + + if (_accounts.empty()) { + active = index; + } + _accounts.emplace(index, std::move(account)); + }; + const auto result = _local->start(passcode, callback); + if (result == Storage::StartResult::Success) { + Assert(started()); + + activate(active); + + for (const auto &[index, account] : _accounts) { + account->startMtp(); + } + if (Local::oldSettingsVersion() < AppVersion) { + Local::writeSettings(); + } + } else { + Assert(!started()); + } + return result; +} + +const base::flat_map> &Accounts::list() const { + return _accounts; +} + +rpl::producer Accounts::activeValue() const { + return _active.value(); +} + +Account &Accounts::active() const { + Expects(!_accounts.empty()); + + Ensures(_active.current() != nullptr); + return *_active.current(); +} + +rpl::producer> Accounts::activeChanges() const { + return _active.changes() | rpl::map([](Account *value) { + return not_null{ value }; + }); +} + +rpl::producer Accounts::activeSessionChanges() const { + return _activeSessions.events(); +} + +rpl::producer Accounts::activeSessionValue() const { + const auto current = (_accounts.empty() || !active().sessionExists()) + ? nullptr + : &active().session(); + return rpl::single(current) | rpl::then(_activeSessions.events()); +} + +int Accounts::add() { + auto index = 0; + while (_accounts.contains(index)) { + ++index; + } + _accounts.emplace(index, std::make_unique(_dataName, index)); + return index; +} + +void Accounts::activate(int index) { + Expects(_accounts.contains(index)); + + _activeLifetime.destroy(); + _activeIndex = index; + _active = _accounts.find(index)->second.get(); + _active.current()->sessionValue( + ) | rpl::start_to_stream(_activeSessions, _activeLifetime); +} + +} // namespace Main diff --git a/Telegram/SourceFiles/main/main_accounts.h b/Telegram/SourceFiles/main/main_accounts.h new file mode 100644 index 000000000..f976153c1 --- /dev/null +++ b/Telegram/SourceFiles/main/main_accounts.h @@ -0,0 +1,61 @@ +/* +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 + +namespace Storage { +class Accounts; +enum class StartResult : uchar; +} // namespace Storage + +namespace Main { + +class Account; +class Session; + +class Accounts final { +public: + explicit Accounts(const QString &dataName); + ~Accounts(); + + [[nodiscard]] bool started() const; + Storage::StartResult start(const QByteArray &passcode); + + [[nodiscard]] Storage::Accounts &local() const { + return *_local; + } + + [[nodiscard]] auto list() const + -> const base::flat_map> &; + [[nodiscard]] rpl::producer activeValue() const; + + // Expects(started()); + [[nodiscard]] Account &active() const; + [[nodiscard]] rpl::producer> activeChanges() const; + + [[nodiscard]] rpl::producer activeSessionValue() const; + [[nodiscard]] rpl::producer activeSessionChanges() const; + + [[nodiscard]] int add(); + void activate(int index); + +private: + const QString _dataName; + const std::unique_ptr _local; + + base::flat_map> _accounts; + rpl::variable _active = nullptr; + int _activeIndex = 0; + + rpl::event_stream _activeSessions; + + rpl::lifetime _activeLifetime; + rpl::lifetime _lifetime; + +}; + +} // namespace Main diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp index 8518dec00..4057ed396 100644 --- a/Telegram/SourceFiles/main/main_app_config.cpp +++ b/Telegram/SourceFiles/main/main_app_config.cpp @@ -29,7 +29,6 @@ AppConfig::AppConfig(not_null account) : _account(account) { _requestId = 0; } }, _lifetime); - refresh(); } void AppConfig::refresh() { diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 3e77a141a..8c1bf9cde 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -67,6 +67,7 @@ Session::Session( ) | rpl::start_with_next([=] { notifications().updateAll(); }, _lifetime); + subscribe(Global::RefConnectionTypeChanged(), [=] { _api->refreshTopPromotion(); }); @@ -74,6 +75,8 @@ Session::Session( _api->requestTermsUpdate(); _api->requestFullPeer(_user); + _api->instance()->setUserPhone(_user->phone()); + crl::on_main(this, [=] { using Flag = Data::PeerUpdate::Flag; changes().peerUpdates( @@ -83,8 +86,16 @@ Session::Session( | Flag::Photo | Flag::About | Flag::PhoneNumber - ) | rpl::start_with_next([=] { + ) | rpl::start_with_next([=](const Data::PeerUpdate &update) { local().writeSelf(); + + if (update.flags & Flag::PhoneNumber) { + const auto phone = _user->phone(); + _api->instance()->setUserPhone(phone); + if (!phone.isEmpty()) { + _api->instance()->requestConfig(); + } + } }, _lifetime); if (_settings.hadLegacyCallsPeerToPeerNobody()) { diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 9bdf1abef..6053f7ca1 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -94,13 +94,6 @@ MainWindow::MainWindow(not_null controller) setLocale(QLocale(QLocale::English, QLocale::UnitedStates)); - account().sessionValue( - ) | rpl::start_with_next([=](Main::Session *session) { - updateGlobalMenu(); - if (!session) { - _mediaPreview.destroy(); - } - }, lifetime()); subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &data) { themeUpdated(data); }); @@ -202,6 +195,7 @@ void MainWindow::finishFirstShow() { void MainWindow::clearWidgetsHook() { destroyLayer(); + _mediaPreview.destroy(); _main.destroy(); _passcodeLock.destroy(); _intro.destroy(); @@ -241,20 +235,19 @@ void MainWindow::setupPasscodeLock() { } void MainWindow::clearPasscodeLock() { - if (!_passcodeLock) return; + if (!_passcodeLock) { + return; + } - auto bg = grabInner(); - - _passcodeLock.destroy(); if (_intro) { + auto bg = grabInner(); + _passcodeLock.destroy(); _intro->showAnimated(bg, true); } else if (_main) { + auto bg = grabInner(); + _passcodeLock.destroy(); _main->showAnimated(bg, true); Core::App().checkStartUrl(); - } else if (account().sessionExists()) { - setupMain(); - } else { - setupIntro(); } } @@ -984,8 +977,10 @@ QImage MainWindow::iconWithCounter(int size, int count, style::color bg, style:: } QImage img(smallIcon ? ((size == 16) ? iconbig16 : (size == 32 ? iconbig32 : iconbig64)) : ((size == 16) ? icon16 : (size == 32 ? icon32 : icon64))); - if (account().sessionExists() && account().session().supportMode()) { - Window::ConvertIconToBlack(img); + if (const auto controller = sessionController()) { + if (controller->session().supportMode()) { + Window::ConvertIconToBlack(img); + } } if (!count) return img; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index a5263b00a..46d3cf047 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "core/shortcuts.h" #include "core/application.h" -#include "main/main_account.h" // Account::sessionValue. +#include "main/main_accounts.h" // Accounts::activeSessionValue. #include "mainwindow.h" #include "main/main_session.h" #include "facades.h" @@ -114,7 +114,7 @@ Instance::Instance() }); // While we have one Media::Player::Instance for all sessions we have to do this. - Core::App().activeAccount().sessionValue( // #TODO multi activeSessionValue + Core::App().accounts().activeSessionValue( ) | rpl::start_with_next([=](Main::Session *session) { if (session) { subscribe(session->calls().currentCallChanged(), [=](Calls::Call *call) { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index aa50b19c4..828fdc550 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -46,9 +46,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_peer_menu.h" #include "window/window_session_controller.h" #include "window/window_controller.h" -#include "main/main_account.h" // Account::sessionValue. #include "base/platform/base_platform_info.h" #include "base/unixtime.h" +#include "main/main_account.h" +#include "main/main_accounts.h" // Accounts::activeSessionValue. #include "main/main_session.h" #include "layout.h" #include "storage/file_download.h" @@ -303,8 +304,6 @@ OverlayWidget::OverlayWidget() ? Global::VideoVolume() : Global::kDefaultVolume; - // #TODO multi activeSessionValue change icon on show? - setWindowIcon(Window::CreateIcon(&Core::App().activeAccount())); setWindowTitle(qsl("Media viewer")); const auto text = tr::lng_mediaview_saved_to( @@ -320,9 +319,15 @@ OverlayWidget::OverlayWidget() connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(onScreenResized(int))); // While we have one mediaview for all sessions we have to do this. - Core::App().activeAccount().sessionValue( // #TODO multi activeSessionValue + Core::App().accounts().activeSessionValue( ) | rpl::start_with_next([=](Main::Session *session) { + if (!isHidden()) { + close(); + } + clearData(); + setWindowIcon(Window::CreateIcon(session)); if (session) { + // #TODO multi subscribe(session->downloaderTaskFinished(), [=] { if (!isHidden()) { updateControls(); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 3eddc2f80..682f3ab83 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -51,7 +51,6 @@ bool gNoStartUpdate = false; bool gStartToSettings = false; uint32 gConnectionsInSession = 1; -QString gLoggedPhoneNumber; QByteArray gLocalSalt; int gScreenScale = style::kScaleAuto; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 940752d79..b87c300b1 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -36,7 +36,6 @@ DeclareSetting(uint64, RealAlphaVersion); DeclareSetting(QByteArray, AlphaPrivateKey); DeclareSetting(bool, TestMode); -DeclareSetting(QString, LoggedPhoneNumber); DeclareSetting(bool, AutoStart); DeclareSetting(bool, StartMinimized); DeclareSetting(bool, StartInTray); diff --git a/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp b/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp index a3e3bcc7c..b27f0e182 100644 --- a/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp +++ b/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp @@ -21,6 +21,8 @@ namespace { constexpr char TdfMagic[] = { 'T', 'D', 'F', '$' }; constexpr auto TdfMagicLen = int(sizeof(TdfMagic)); +constexpr auto kStrongIterationsCount = 100'000; + } // namespace QString ToFilePart(FileKey val) { @@ -86,6 +88,28 @@ bool CheckStreamStatus(QDataStream &stream) { MTP::AuthKeyPtr CreateLocalKey( const QByteArray &passcode, const QByteArray &salt) { + const auto s = bytes::make_span(salt); + const auto hash = openssl::Sha512(s, bytes::make_span(passcode), s); + const auto iterationsCount = passcode.isEmpty() + ? 1 // Don't slow down for no password. + : kStrongIterationsCount; + + auto key = MTP::AuthKey::Data{ { gsl::byte{} } }; + PKCS5_PBKDF2_HMAC( + reinterpret_cast(hash.data()), + hash.size(), + reinterpret_cast(s.data()), + s.size(), + iterationsCount, + EVP_sha512(), + key.size(), + reinterpret_cast(key.data())); + return std::make_shared(key); +} + +MTP::AuthKeyPtr CreateLegacyLocalKey( + const QByteArray &passcode, + const QByteArray &salt) { auto key = MTP::AuthKey::Data{ { gsl::byte{} } }; const auto iterationsCount = passcode.isEmpty() ? LocalEncryptNoPwdIterCount // Don't slow down for no password. diff --git a/Telegram/SourceFiles/storage/details/storage_file_utilities.h b/Telegram/SourceFiles/storage/details/storage_file_utilities.h index 34f47cb97..dcc12ac3b 100644 --- a/Telegram/SourceFiles/storage/details/storage_file_utilities.h +++ b/Telegram/SourceFiles/storage/details/storage_file_utilities.h @@ -23,6 +23,9 @@ void ClearKey(const FileKey &key, const QString &basePath); [[nodiscard]] MTP::AuthKeyPtr CreateLocalKey( const QByteArray &passcode, const QByteArray &salt); +[[nodiscard]] MTP::AuthKeyPtr CreateLegacyLocalKey( + const QByteArray &passcode, + const QByteArray &salt); struct FileReadDescriptor final { ~FileReadDescriptor(); diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp index bec9b88b7..f4fc6e7a4 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -681,12 +681,10 @@ bool ReadSetting( cSetWindowPos(position); } break; - case dbiLoggedPhoneNumber: { + case dbiLoggedPhoneNumber: { // deprecated QString v; stream >> v; if (!CheckStreamStatus(stream)) return false; - - cSetLoggedPhoneNumber(v); } break; case dbiMutePeer: { // deprecated @@ -737,9 +735,9 @@ bool ReadSetting( ? false : (v == 1); if (Window::Theme::IsNightMode()) { - Window::Theme::Background()->setTileNightValue(tile); + context.tileNight = tile; } else { - Window::Theme::Background()->setTileDayValue(tile); + context.tileDay = tile; } } break; @@ -748,8 +746,8 @@ bool ReadSetting( stream >> tileDay >> tileNight; if (!CheckStreamStatus(stream)) return false; - Window::Theme::Background()->setTileDayValue(tileDay == 1); - Window::Theme::Background()->setTileNightValue(tileNight == 1); + context.tileDay = (tileDay == 1); + context.tileNight = (tileNight == 1); } break; case dbiAdaptiveForWide: { diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.h b/Telegram/SourceFiles/storage/details/storage_settings_scheme.h index da5b5caec..09f68ec72 100644 --- a/Telegram/SourceFiles/storage/details/storage_settings_scheme.h +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.h @@ -51,6 +51,9 @@ struct ReadSettingsContext { std::vector> mtpLegacyKeys; qint32 mtpLegacyMainDcId = 0; qint32 mtpLegacyUserId = 0; + + bool tileDay = false; + bool tileNight = true; }; [[nodiscard]] bool ReadSetting( diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index c07e0ddbc..317e46e98 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -88,14 +88,6 @@ enum class WriteMapWhen { Soon, }; -std::unique_ptr StoredSessionSettings; -Main::Settings &GetStoredSessionSettings() { - if (!StoredSessionSettings) { - StoredSessionSettings = std::make_unique(); - } - return *StoredSessionSettings; -} - void applyReadContext(ReadSettingsContext &&context) { Core::App().dcOptions()->addFromOther(std::move(context.dcOptions)); @@ -169,7 +161,7 @@ void _readOldUserSettingsFields( continue; } - OldKey = CreateLocalKey(QByteArray(), salt); + OldKey = CreateLegacyLocalKey(QByteArray(), salt); if (data.size() <= 16 || (data.size() & 0x0F)) { LOG(("App Error: bad encrypted part size in old user config: %1").arg(data.size())); @@ -344,7 +336,7 @@ void start() { LOG(("App Error: bad salt in settings file, size: %1").arg(salt.size())); return writeSettings(); } - SettingsKey = CreateLocalKey(QByteArray(), salt); + SettingsKey = CreateLegacyLocalKey(QByteArray(), salt); EncryptedDescriptor settings; if (!DecryptLocal(settings, settingsEncrypted, SettingsKey)) { @@ -388,7 +380,7 @@ void writeSettings() { if (_settingsSalt.isEmpty() || !SettingsKey) { _settingsSalt.resize(LocalEncryptSaltSize); memset_rand(_settingsSalt.data(), _settingsSalt.size()); - SettingsKey = CreateLocalKey(QByteArray(), _settingsSalt); + SettingsKey = CreateLegacyLocalKey(QByteArray(), _settingsSalt); } settings.writeData(_settingsSalt); @@ -398,7 +390,6 @@ void writeSettings() { quint32 size = 12 * (sizeof(quint32) + sizeof(qint32)); size += sizeof(quint32) + Serialize::bytearraySize(dcOptionsSerialized); size += sizeof(quint32) + Serialize::bytearraySize(applicationSettings); - size += sizeof(quint32) + Serialize::stringSize(cLoggedPhoneNumber()); size += sizeof(quint32) + Serialize::stringSize(Global::TxtDomainString()); size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath()); @@ -438,7 +429,6 @@ void writeSettings() { data.stream << quint32(dbiScalePercent) << qint32(cConfigScale()); data.stream << quint32(dbiDcOptions) << dcOptionsSerialized; data.stream << quint32(dbiApplicationSettings) << applicationSettings; - data.stream << quint32(dbiLoggedPhoneNumber) << cLoggedPhoneNumber(); data.stream << quint32(dbiTxtDomainString) << Global::TxtDomainString(); data.stream << quint32(dbiDialogLastPath) << cDialogLastPath(); data.stream << quint32(dbiAnimationsDisabled) << qint32(anim::Disabled() ? 1 : 0); @@ -542,7 +532,6 @@ void reset() { Window::Theme::Background()->reset(); _oldSettingsVersion = 0; - StoredSessionSettings.reset(); } int32 oldSettingsVersion() { diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index 73fe9a7f7..189f9ccca 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_account.h" #include "storage/localstorage.h" +#include "storage/storage_accounts.h" #include "storage/storage_encryption.h" #include "storage/storage_clear_legacy.h" #include "storage/cache/storage_cache_types.h" @@ -35,7 +36,6 @@ namespace { using namespace details; using Database = Cache::Database; -constexpr auto kLocalKeySize = MTP::AuthKey::kSize; constexpr auto kDelayedWriteTimeout = crl::time(1000); constexpr auto kSavedBackgroundFormat = QImage::Format_ARGB32_Premultiplied; @@ -120,14 +120,28 @@ Account::~Account() { } } -StartResult Account::start(const QByteArray &passcode) { - const auto result = readMap(passcode); +StartResult Account::legacyStart(const QByteArray &passcode) { + const auto result = readMapWith(MTP::AuthKeyPtr(), passcode); if (result == ReadMapResult::Failed) { - _mapChanged = true; - writeMap(); + Assert(_localKey == nullptr); + //_mapChanged = true; + //writeMap(); } else if (result == ReadMapResult::IncorrectPasscode) { return StartResult::IncorrectPasscode; } + clearLegacyFiles(); + return StartResult::Success; +} + +void Account::start(MTP::AuthKeyPtr localKey) { + Expects(localKey != nullptr); + + _localKey = std::move(localKey); + readMapWith(_localKey); + clearLegacyFiles(); +} + +void Account::clearLegacyFiles() { const auto weak = base::make_weak(_owner.get()); ClearLegacyFiles(_basePath, [weak, this]( FnMut&&)> then) { @@ -135,12 +149,6 @@ StartResult Account::start(const QByteArray &passcode) { then(collectGoodNames()); }); }); - - if (Local::oldSettingsVersion() < AppVersion) { - Local::writeSettings(); - } - - return StartResult::Success; } base::flat_set Account::collectGoodNames() const { @@ -184,7 +192,9 @@ base::flat_set Account::collectGoodNames() const { return result; } -Account::ReadMapResult Account::readMap(const QByteArray &passcode) { +Account::ReadMapResult Account::readMapWith( + MTP::AuthKeyPtr localKey, + const QByteArray &legacyPasscode) { auto ms = crl::now(); FileReadDescriptor mapData; @@ -193,34 +203,33 @@ Account::ReadMapResult Account::readMap(const QByteArray &passcode) { } LOG(("App Info: reading map...")); - QByteArray salt, keyEncrypted, mapEncrypted; - mapData.stream >> salt >> keyEncrypted >> mapEncrypted; + QByteArray legacySalt, legacyKeyEncrypted, mapEncrypted; + mapData.stream >> legacySalt >> legacyKeyEncrypted >> mapEncrypted; if (!CheckStreamStatus(mapData.stream)) { return ReadMapResult::Failed; } + if (!localKey) { + if (legacySalt.size() != LocalEncryptSaltSize) { + LOG(("App Error: bad salt in map file, size: %1").arg(legacySalt.size())); + return ReadMapResult::Failed; + } + auto legacyPasscodeKey = CreateLegacyLocalKey(legacyPasscode, legacySalt); - if (salt.size() != LocalEncryptSaltSize) { - LOG(("App Error: bad salt in map file, size: %1").arg(salt.size())); - return ReadMapResult::Failed; + EncryptedDescriptor keyData; + if (!DecryptLocal(keyData, legacyKeyEncrypted, legacyPasscodeKey)) { + LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password...")); + return ReadMapResult::IncorrectPasscode; + } + auto key = Serialize::read(keyData.stream); + if (keyData.stream.status() != QDataStream::Ok || !keyData.stream.atEnd()) { + LOG(("App Error: could not read pass-protected key from map file")); + return ReadMapResult::Failed; + } + localKey = std::make_shared(key); } - _passcodeKey = CreateLocalKey(passcode, salt); - EncryptedDescriptor keyData, map; - if (!DecryptLocal(keyData, keyEncrypted, _passcodeKey)) { - LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password...")); - return ReadMapResult::IncorrectPasscode; - } - auto key = Serialize::read(keyData.stream); - if (keyData.stream.status() != QDataStream::Ok || !keyData.stream.atEnd()) { - LOG(("App Error: could not read pass-protected key from map file")); - return ReadMapResult::Failed; - } - _localKey = std::make_shared(key); - - _passcodeKeyEncrypted = keyEncrypted; - _passcodeKeySalt = salt; - - if (!DecryptLocal(map, mapEncrypted, _localKey)) { + EncryptedDescriptor map; + if (!DecryptLocal(map, mapEncrypted, localKey)) { LOG(("App Error: could not decrypt map.")); return ReadMapResult::Failed; } @@ -336,6 +345,8 @@ Account::ReadMapResult Account::readMap(const QByteArray &passcode) { } } + _localKey = std::move(localKey); + _draftsMap = draftsMap; _draftCursorsMap = draftCursorsMap; _draftsNotReadMap = draftsNotReadMap; @@ -355,6 +366,7 @@ Account::ReadMapResult Account::readMap(const QByteArray &passcode) { _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey; _exportSettingsKey = exportSettingsKey; _oldMapVersion = mapData.version; + if (_oldMapVersion < AppVersion) { writeMapDelayed(); } else { @@ -391,6 +403,8 @@ void Account::writeMapQueued() { } void Account::writeMap() { + Expects(_localKey != nullptr); + _writeMapTimer.cancel(); if (!_mapChanged) { return; @@ -402,23 +416,8 @@ void Account::writeMap() { } FileWriteDescriptor map(u"map"_q, _basePath); - if (_passcodeKeySalt.isEmpty() || _passcodeKeyEncrypted.isEmpty()) { - auto pass = QByteArray(kLocalKeySize, Qt::Uninitialized); - auto salt = QByteArray(LocalEncryptSaltSize, Qt::Uninitialized); - memset_rand(pass.data(), pass.size()); - memset_rand(salt.data(), salt.size()); - _localKey = CreateLocalKey(pass, salt); - - _passcodeKeySalt.resize(LocalEncryptSaltSize); - memset_rand(_passcodeKeySalt.data(), _passcodeKeySalt.size()); - _passcodeKey = CreateLocalKey(QByteArray(), _passcodeKeySalt); - - EncryptedDescriptor passKeyData(kLocalKeySize); - _localKey->write(passKeyData.stream); - _passcodeKeyEncrypted = PrepareEncrypted(passKeyData, _passcodeKey); - } - map.writeData(_passcodeKeySalt); - map.writeData(_passcodeKeyEncrypted); + map.writeData(QByteArray()); + map.writeData(QByteArray()); uint32 mapSize = 0; const auto self = [&] { @@ -514,34 +513,8 @@ void Account::writeMap() { _mapChanged = false; } -bool Account::checkPasscode(const QByteArray &passcode) const { - Expects(!_passcodeKeySalt.isEmpty()); - Expects(_passcodeKey != nullptr); - - const auto checkKey = CreateLocalKey(passcode, _passcodeKeySalt); - return checkKey->equals(_passcodeKey); -} - -void Account::setPasscode(const QByteArray &passcode) { - Expects(!_passcodeKeySalt.isEmpty()); - Expects(_localKey != nullptr); - - _passcodeKey = CreateLocalKey(passcode, _passcodeKeySalt); - - EncryptedDescriptor passKeyData(kLocalKeySize); - _localKey->write(passKeyData.stream); - _passcodeKeyEncrypted = PrepareEncrypted(passKeyData, _passcodeKey); - - _mapChanged = true; - writeMap(); - - Global::SetLocalPasscode(!passcode.isEmpty()); - Global::RefLocalPasscodeChanged().notify(); -} - void Account::reset() { auto names = collectGoodNames(); - _passcodeKeySalt.clear(); _draftsMap.clear(); _draftCursorsMap.clear(); _draftsNotReadMap.clear(); @@ -921,6 +894,10 @@ std::unique_ptr Account::applyReadContext( } } + // #TODO multi + //Window::Theme::Background()->setTileDayValue(context.tileDay); + //Window::Theme::Background()->setTileNightValue(context.tileNight); + return std::move(context.sessionSettingsStorage); } diff --git a/Telegram/SourceFiles/storage/storage_account.h b/Telegram/SourceFiles/storage/storage_account.h index 2473b04f0..d6f91da25 100644 --- a/Telegram/SourceFiles/storage/storage_account.h +++ b/Telegram/SourceFiles/storage/storage_account.h @@ -41,10 +41,7 @@ class EncryptionKey; using FileKey = quint64; -enum class StartResult : uchar { - Success, - IncorrectPasscode, -}; +enum class StartResult : uchar; struct MessageDraft { MsgId msgId = 0; @@ -57,13 +54,15 @@ public: Account(not_null owner, const QString &dataName); ~Account(); - [[nodiscard]] StartResult start(const QByteArray &passcode); + [[nodiscard]] void start(MTP::AuthKeyPtr localKey); + [[nodiscard]] StartResult legacyStart(const QByteArray &passcode); [[nodiscard]] int oldMapVersion() const { return _oldMapVersion; } - [[nodiscard]] bool checkPasscode(const QByteArray &passcode) const; - void setPasscode(const QByteArray &passcode); + MTP::AuthKeyPtr peekLegacyLocalKey() const { + return _localKey; + } void writeSettings(); void writeMtpData(); @@ -156,7 +155,10 @@ private: [[nodiscard]] auto prepareReadSettingsContext() const -> details::ReadSettingsContext; - [[nodiscard]] ReadMapResult readMap(const QByteArray &passcode); + ReadMapResult readMapWith( + MTP::AuthKeyPtr localKey, + const QByteArray &legacyPasscode = QByteArray()); + void clearLegacyFiles(); void writeMapDelayed(); void writeMapQueued(); void writeMap(); @@ -168,18 +170,6 @@ private: std::unique_ptr readSettings(); void writeSettings(Main::Settings *stored); - bool readOldUserSettings( - bool remove, - details::ReadSettingsContext &context); - void readOldUserSettingsFields( - QIODevice *device, - qint32 &version, - details::ReadSettingsContext &context); - void readOldMtpDataFields( - QIODevice *device, - qint32 &version, - details::ReadSettingsContext &context); - bool readOldMtpData(bool remove, details::ReadSettingsContext &context); void readMtpData(); std::unique_ptr applyReadContext( @@ -222,9 +212,6 @@ private: const QString _databasePath; MTP::AuthKeyPtr _localKey; - MTP::AuthKeyPtr _passcodeKey; - QByteArray _passcodeKeySalt; - QByteArray _passcodeKeyEncrypted; base::flat_map _draftsMap; base::flat_map _draftCursorsMap; diff --git a/Telegram/SourceFiles/storage/storage_accounts.cpp b/Telegram/SourceFiles/storage/storage_accounts.cpp new file mode 100644 index 000000000..53621fb92 --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_accounts.cpp @@ -0,0 +1,243 @@ +/* +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 +*/ +#include "storage/storage_accounts.h" + +#include "storage/details/storage_file_utilities.h" +#include "storage/serialize_common.h" +#include "main/main_accounts.h" +#include "main/main_account.h" +#include "facades.h" + +namespace Storage { +namespace { + +using namespace details; + +constexpr auto kMaxAccounts = 3; + +[[nodiscard]] QString BaseGlobalPath() { + return cWorkingDir() + qsl("tdata/"); +} + +[[nodiscard]] QString ComputeKeyName(const QString &dataName) { + return "key_" + dataName + (cTestMode() ? "[test]" : ""); +} + +[[nodiscard]] QString ComputeInfoName(const QString &dataName) { + return "info_" + dataName + (cTestMode() ? "[test]" : ""); +} + +} // namespace + +Accounts::Accounts(not_null owner, const QString &dataName) +: _owner(owner) +, _dataName(dataName) { +} + +Accounts::~Accounts() = default; + +StartResult Accounts::start( + const QByteArray &passcode, + Fn)> callback) { + const auto modern = startModern(passcode, callback); + if (modern == StartModernResult::Success) { + if (_oldVersion < AppVersion) { + writeAccounts(); + } + return StartResult::Success; + } else if (modern == StartModernResult::IncorrectPasscode) { + return StartResult::IncorrectPasscode; + } else if (modern == StartModernResult::Failed) { + startWithSingleAccount( + passcode, + std::move(callback), + std::make_unique(_dataName, 0)); + return StartResult::Success; + } + auto legacy = std::make_unique(_dataName, 0); + const auto result = legacy->legacyStart(passcode); + if (result == StartResult::Success) { + _oldVersion = legacy->local().oldMapVersion(); + startWithSingleAccount( + passcode, + std::move(callback), + std::move(legacy)); + } + return result; +} + +void Accounts::startWithSingleAccount( + const QByteArray &passcode, + Fn)> callback, + std::unique_ptr account) { + Expects(account != nullptr); + + if (auto localKey = account->local().peekLegacyLocalKey()) { + _localKey = std::move(localKey); + encryptLocalKey(passcode); + } else { + generateLocalKey(); + account->start(_localKey); + } + callback(0, std::move(account)); + writeAccounts(); +} + +void Accounts::generateLocalKey() { + Expects(_localKey == nullptr); + Expects(_passcodeKeySalt.isEmpty()); + Expects(_passcodeKeyEncrypted.isEmpty()); + + auto pass = QByteArray(MTP::AuthKey::kSize, Qt::Uninitialized); + auto salt = QByteArray(LocalEncryptSaltSize, Qt::Uninitialized); + memset_rand(pass.data(), pass.size()); + memset_rand(salt.data(), salt.size()); + _localKey = CreateLocalKey(pass, salt); + + encryptLocalKey(QByteArray()); +} + +void Accounts::encryptLocalKey(const QByteArray &passcode) { + _passcodeKeySalt.resize(LocalEncryptSaltSize); + memset_rand(_passcodeKeySalt.data(), _passcodeKeySalt.size()); + _passcodeKey = CreateLocalKey(passcode, _passcodeKeySalt); + + EncryptedDescriptor passKeyData(MTP::AuthKey::kSize); + _localKey->write(passKeyData.stream); + _passcodeKeyEncrypted = PrepareEncrypted(passKeyData, _passcodeKey); +} + +Accounts::StartModernResult Accounts::startModern( + const QByteArray &passcode, + Fn)> callback) { + const auto name = ComputeKeyName(_dataName); + + FileReadDescriptor keyData; + if (!ReadFile(keyData, name, BaseGlobalPath())) { + return StartModernResult::Empty; + } + LOG(("App Info: reading accounts info...")); + + QByteArray salt, keyEncrypted, infoEncrypted; + keyData.stream >> salt >> keyEncrypted >> infoEncrypted; + if (!CheckStreamStatus(keyData.stream)) { + return StartModernResult::Failed; + } + + if (salt.size() != LocalEncryptSaltSize) { + LOG(("App Error: bad salt in info file, size: %1").arg(salt.size())); + return StartModernResult::Failed; + } + _passcodeKey = CreateLocalKey(passcode, salt); + + EncryptedDescriptor keyInnerData, info; + if (!DecryptLocal(keyInnerData, keyEncrypted, _passcodeKey)) { + LOG(("App Info: could not decrypt pass-protected key from info file, " + "maybe bad password...")); + return StartModernResult::IncorrectPasscode; + } + auto key = Serialize::read(keyInnerData.stream); + if (keyInnerData.stream.status() != QDataStream::Ok + || !keyInnerData.stream.atEnd()) { + LOG(("App Error: could not read pass-protected key from info file")); + return StartModernResult::Failed; + } + _localKey = std::make_shared(key); + + _passcodeKeyEncrypted = keyEncrypted; + _passcodeKeySalt = salt; + + if (!DecryptLocal(info, infoEncrypted, _localKey)) { + LOG(("App Error: could not decrypt info.")); + return StartModernResult::Failed; + } + LOG(("App Info: reading encrypted info...")); + auto count = qint32(); + info.stream >> count; + if (count <= 0 || count > kMaxAccounts) { + LOG(("App Error: bad accounts count: %1").arg(count)); + return StartModernResult::Failed; + } + + _oldVersion = keyData.version; + + auto tried = base::flat_set(); + auto users = base::flat_set(); + for (auto i = 0; i != count; ++i) { + auto index = qint32(); + info.stream >> index; + if (index >= 0 + && index < kMaxAccounts + && tried.emplace(index).second) { + auto account = std::make_unique(_dataName, index); + account->start(_localKey); + const auto userId = account->willHaveUserId(); + if (!users.contains(userId) + && (userId != 0 || (users.empty() && i + 1 == count))) { + callback(index, std::move(account)); + users.emplace(userId); + } + } + } + + Ensures(!users.empty()); + return StartModernResult::Success; +} + +void Accounts::writeAccounts() { + Expects(!_owner->list().empty()); + + const auto path = BaseGlobalPath(); + if (!QDir().exists(path)) { + QDir().mkpath(path); + } + + FileWriteDescriptor key(ComputeKeyName(_dataName), path); + key.writeData(_passcodeKeySalt); + key.writeData(_passcodeKeyEncrypted); + + const auto &list = _owner->list(); + + auto keySize = sizeof(qint32) + sizeof(qint32) * list.size(); + + EncryptedDescriptor keyData(keySize); + keyData.stream << qint32(list.size()); + for (const auto &[index, account] : list) { + keyData.stream << qint32(index); + } + key.writeEncrypted(keyData, _localKey); +} + +bool Accounts::checkPasscode(const QByteArray &passcode) const { + Expects(!_passcodeKeySalt.isEmpty()); + Expects(_passcodeKey != nullptr); + + const auto checkKey = CreateLocalKey(passcode, _passcodeKeySalt); + return checkKey->equals(_passcodeKey); +} + +void Accounts::setPasscode(const QByteArray &passcode) { + Expects(!_passcodeKeySalt.isEmpty()); + Expects(_localKey != nullptr); + + encryptLocalKey(passcode); + writeAccounts(); + + Global::SetLocalPasscode(!passcode.isEmpty()); + Global::RefLocalPasscodeChanged().notify(); +} + +int Accounts::oldVersion() const { + return _oldVersion; +} + +void Accounts::clearOldVersion() { + _oldVersion = 0; +} + +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_accounts.h b/Telegram/SourceFiles/storage/storage_accounts.h new file mode 100644 index 000000000..a9dfe350e --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_accounts.h @@ -0,0 +1,73 @@ +/* +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 + +namespace MTP { +class AuthKey; +using AuthKeyPtr = std::shared_ptr; +} // namespace MTP + +namespace Main { +class Account; +class Accounts; +} // namespace Main + +namespace Storage { + +enum class StartResult : uchar { + Success, + IncorrectPasscode, +}; + +class Accounts final { +public: + Accounts(not_null owner, const QString &dataName); + ~Accounts(); + + [[nodiscard]] StartResult start( + const QByteArray &passcode, + Fn)> callback); + void writeAccounts(); + + [[nodiscard]] bool checkPasscode(const QByteArray &passcode) const; + void setPasscode(const QByteArray &passcode); + + [[nodiscard]] int oldVersion() const; + void clearOldVersion(); + +private: + enum class StartModernResult { + Success, + IncorrectPasscode, + Failed, + Empty, + }; + + [[nodiscard]] StartModernResult startModern( + const QByteArray &passcode, + Fn)> callback); + void startWithSingleAccount( + const QByteArray &passcode, + Fn)> callback, + std::unique_ptr account); + void generateLocalKey(); + void encryptLocalKey(const QByteArray &passcode); + void writeInfo(); + + const not_null _owner; + const QString _dataName; + + MTP::AuthKeyPtr _localKey; + MTP::AuthKeyPtr _passcodeKey; + QByteArray _passcodeKeySalt; + QByteArray _passcodeKeyEncrypted; + int _oldVersion = 0; + +}; + +} // namespace Storage diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index d90294d3a..65fe7b4ec 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -105,18 +105,16 @@ void ConvertIconToBlack(QImage &image) { } } -QIcon CreateOfficialIcon(Main::Account *account) { +QIcon CreateOfficialIcon(Main::Session *session) { auto image = Core::IsAppLaunched() ? Core::App().logo() : LoadLogo(); - if (account - && account->sessionExists() - && account->session().supportMode()) { + if (session && session->supportMode()) { ConvertIconToBlack(image); } return QIcon(App::pixmapFromImageInPlace(std::move(image))); } -QIcon CreateIcon(Main::Account *account) { - auto result = CreateOfficialIcon(account); +QIcon CreateIcon(Main::Session *session) { + auto result = CreateOfficialIcon(session); #if defined Q_OS_UNIX && !defined Q_OS_MAC return QIcon::fromTheme(Platform::GetIconName(), result); #endif @@ -295,10 +293,12 @@ bool MainWindow::computeIsActive() const { } void MainWindow::updateWindowIcon() { - const auto supportIcon = account().sessionExists() - && account().session().supportMode(); + const auto session = sessionController() + ? &sessionController()->session() + : nullptr; + const auto supportIcon = session && session->supportMode(); if (supportIcon != _usingSupportIcon || _icon.isNull()) { - _icon = CreateIcon(&account()); + _icon = CreateIcon(session); _usingSupportIcon = supportIcon; } setWindowIcon(_icon); @@ -528,9 +528,7 @@ void MainWindow::updateControlsGeometry() { void MainWindow::updateUnreadCounter() { if (!Global::started() || App::quitting()) return; - const auto counter = account().sessionExists() - ? account().session().data().unreadBadge() - : 0; + const auto counter = Core::App().unreadBadge(); _titleText = (counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram"); unreadCounterChangedHook(); diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 1580dfbde..3392bd9a5 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace Main { +class Session; class Account; } // namespace Main @@ -31,7 +32,7 @@ struct TermsLock; QImage LoadLogo(); QImage LoadLogoNoMargin(); -QIcon CreateIcon(Main::Account *account = nullptr); +QIcon CreateIcon(Main::Session *session = nullptr); void ConvertIconToBlack(QImage &image); class MainWindow : public Ui::RpWidget, protected base::Subscriber { @@ -40,11 +41,11 @@ class MainWindow : public Ui::RpWidget, protected base::Subscriber { public: explicit MainWindow(not_null controller); - Window::Controller &controller() const { + [[nodiscard]] Window::Controller &controller() const { return *_controller; } - Main::Account &account() const; - Window::SessionController *sessionController() const; + [[nodiscard]] Main::Account &account() const; + [[nodiscard]] Window::SessionController *sessionController() const; bool hideNoQuit(); diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 9f9670e25..6c3ee8d31 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -22,7 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "base/crc32hash.h" #include "data/data_session.h" -#include "main/main_account.h" // Account::sessionValue. +#include "main/main_account.h" // Account::local. +#include "main/main_accounts.h" // Accounts::activeSessionValue. #include "ui/image/image.h" #include "boxes/background_box.h" #include "core/application.h" @@ -547,7 +548,7 @@ void ChatBackground::start() { set(Data::ThemeWallPaper()); } - Core::App().activeAccount().sessionValue( // #TODO multi activeSessionValue + Core::App().accounts().activeSessionValue( ) | rpl::filter([=](Main::Session *session) { return session != _session; }) | rpl::start_with_next([=](Main::Session *session) { diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 723f71537..3b8a01bf9 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "main/main_account.h" +#include "main/main_accounts.h" #include "main/main_session.h" #include "ui/layers/box_content.h" #include "ui/layers/layer_widget.h" @@ -27,11 +28,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Window { -Controller::Controller(not_null account) -: _account(account) -, _widget(this) { +Controller::Controller() +: _widget(this) { + _widget.init(); +} + +Controller::~Controller() { + // We want to delete all widgets before the _sessionController. + _widget.clearWidgets(); +} + +void Controller::showAccount(not_null account) { + _accountLifetime.destroy(); + _account = account; + _account->sessionValue( - ) | rpl::start_with_next([=](Main::Session *session) { + ) | rpl::start_with_next([=](Main::Session *session) { _sessionController = session ? std::make_unique(session, this) : nullptr; @@ -47,14 +59,13 @@ Controller::Controller(not_null account) sideBarChanged(); } _widget.updateWindowIcon(); + _widget.updateGlobalMenu(); + if (session) { + setupMain(); + } else { + setupIntro(); + } }, _lifetime); - - _widget.init(); -} - -Controller::~Controller() { - // We want to delete all widgets before the _sessionController. - _widget.clearWidgets(); } void Controller::finishFirstShow() { @@ -75,7 +86,11 @@ void Controller::setupPasscodeLock() { } void Controller::clearPasscodeLock() { - _widget.clearPasscodeLock(); + if (!_account) { + showAccount(&Core::App().activeAccount()); + } else { + _widget.clearPasscodeLock(); + } } void Controller::setupIntro() { @@ -83,12 +98,12 @@ void Controller::setupIntro() { } void Controller::setupMain() { - Expects(_account->sessionExists()); + Expects(_sessionController != nullptr); _widget.setupMain(); if (const auto id = Ui::Emoji::NeedToSwitchBackToId()) { - Ui::Emoji::LoadAndSwitchTo(&_account->session(), id); + Ui::Emoji::LoadAndSwitchTo(&_sessionController->session(), id); } } diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index 75953a07c..ec7a5493a 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -18,18 +18,22 @@ namespace Window { class Controller final { public: - explicit Controller(not_null account); + Controller(); ~Controller(); Controller(const Controller &other) = delete; Controller &operator=(const Controller &other) = delete; - Main::Account &account() const { - return *_account; - } + void showAccount(not_null account); + not_null<::MainWindow*> widget() { return &_widget; } + Main::Account &account() const { + Expects(_account != nullptr); + + return *_account; + } SessionController *sessionController() const { return _sessionController.get(); } @@ -74,10 +78,11 @@ private: anim::type animated); void checkThemeEditor(); - not_null _account; + Main::Account *_account = nullptr; ::MainWindow _widget; std::unique_ptr _sessionController; + rpl::lifetime _accountLifetime; rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/window/window_lock_widgets.cpp b/Telegram/SourceFiles/window/window_lock_widgets.cpp index 9302b42de..08fd57f79 100644 --- a/Telegram/SourceFiles/window/window_lock_widgets.cpp +++ b/Telegram/SourceFiles/window/window_lock_widgets.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_lock_widgets.h" #include "lang/lang_keys.h" -#include "storage/storage_account.h" +#include "storage/storage_accounts.h" #include "mainwindow.h" #include "core/application.h" #include "api/api_text_entities.h" @@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_controller.h" #include "window/window_slide_animation.h" #include "window/window_session_controller.h" -#include "main/main_account.h" +#include "main/main_accounts.h" #include "facades.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -147,11 +147,12 @@ void PasscodeLockWidget::submit() { } const auto passcode = _passcode->text().toUtf8(); - auto &account = window()->account(); - auto &local = window()->account().local(); - const auto correct = account.sessionExists() - ? local.checkPasscode(passcode) - : (local.start(passcode) != Storage::StartResult::IncorrectPasscode); + const auto controller = window()->sessionController(); + auto &accounts = Core::App().accounts(); + const auto correct = controller + ? accounts.local().checkPasscode(passcode) + : (accounts.start(passcode) + != Storage::StartResult::IncorrectPasscode); if (!correct) { cSetPasscodeBadTries(cPasscodeBadTries() + 1); cSetPasscodeLastTry(crl::now());