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