/* 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_session.h" #include "apiwrap.h" #include "api/api_updates.h" #include "api/api_send_progress.h" #include "api/api_user_privacy.h" #include "main/main_account.h" #include "main/main_domain.h" #include "main/main_session_settings.h" #include "main/session/send_as_peers.h" #include "mtproto/mtproto_config.h" #include "chat_helpers/stickers_emoji_pack.h" #include "chat_helpers/stickers_dice_pack.h" #include "storage/file_download.h" #include "storage/download_manager_mtproto.h" #include "storage/file_upload.h" #include "storage/storage_account.h" #include "storage/storage_facade.h" #include "storage/storage_account.h" #include "data/data_session.h" #include "data/data_changes.h" #include "data/data_user.h" #include "data/stickers/data_stickers.h" #include "window/window_session_controller.h" #include "window/window_lock_widgets.h" #include "base/unixtime.h" #include "calls/calls_instance.h" #include "support/support_helper.h" #ifndef TDESKTOP_DISABLE_SPELLCHECK #include "chat_helpers/spellchecker_common.h" #endif // TDESKTOP_DISABLE_SPELLCHECK namespace Main { namespace { constexpr auto kTmpPasswordReserveTime = TimeId(10); [[nodiscard]] QString ValidatedInternalLinksDomain( not_null session) { // This domain should start with 'http[s]://' and end with '/'. // Like 'https://telegram.me/' or 'https://t.me/'. const auto &domain = session->serverConfig().internalLinksDomain; const auto prefixes = { qstr("https://"), qstr("http://"), }; for (const auto &prefix : prefixes) { if (domain.startsWith(prefix, Qt::CaseInsensitive)) { return domain.endsWith('/') ? domain : MTP::ConfigFields().internalLinksDomain; } } return MTP::ConfigFields().internalLinksDomain; } } // namespace Session::Session( not_null account, const MTPUser &user, std::unique_ptr settings) : _account(account) , _settings(std::move(settings)) , _changes(std::make_unique(this)) , _api(std::make_unique(this)) , _updates(std::make_unique(this)) , _sendProgressManager(std::make_unique(this)) , _downloader(std::make_unique(_api.get())) , _uploader(std::make_unique(_api.get())) , _storage(std::make_unique()) , _data(std::make_unique(this)) , _userId(user.c_user().vid()) , _user(_data->processUser(user)) , _emojiStickersPack(std::make_unique(this)) , _diceStickersPacks(std::make_unique(this)) , _sendAsPeers(std::make_unique(this)) , _supportHelper(Support::Helper::Create(this)) , _saveSettingsTimer([=] { saveSettings(); }) { Expects(_settings != nullptr); _api->requestTermsUpdate(); _api->requestFullPeer(_user); _api->instance().setUserPhone(_user->phone()); // Load current userpic and keep it loaded. _user->loadUserpic(); changes().peerFlagsValue( _user, Data::PeerUpdate::Flag::Photo ) | rpl::start_with_next([=] { [[maybe_unused]] const auto image = _user->currentUserpic( _selfUserpicView); }, lifetime()); crl::on_main(this, [=] { using Flag = Data::PeerUpdate::Flag; changes().peerUpdates( _user, Flag::Name | Flag::Username | Flag::Photo | Flag::About | Flag::PhoneNumber ) | rpl::start_with_next([=](const Data::PeerUpdate &update) { local().writeSelf(); if (update.flags & Flag::PhoneNumber) { const auto phone = _user->phone(); _api->instance().setUserPhone(phone); if (!phone.isEmpty()) { _api->instance().requestConfig(); } } }, _lifetime); if (_settings->hadLegacyCallsPeerToPeerNobody()) { api().userPrivacy().save( Api::UserPrivacy::Key::CallsPeer2Peer, Api::UserPrivacy::Rule{ .option = Api::UserPrivacy::Option::Nobody }); saveSettingsDelayed(); } // Storage::Account uses Main::Account::session() in those methods. // So they can't be called during Main::Session construction. local().readInstalledStickers(); local().readInstalledMasks(); local().readFeaturedStickers(); local().readRecentStickers(); local().readRecentMasks(); local().readFavedStickers(); local().readSavedGifs(); data().stickers().notifyUpdated(); data().stickers().notifySavedGifsUpdated(); }); #ifndef TDESKTOP_DISABLE_SPELLCHECK Spellchecker::Start(this); #endif // TDESKTOP_DISABLE_SPELLCHECK _api->requestNotifySettings(MTP_inputNotifyUsers()); _api->requestNotifySettings(MTP_inputNotifyChats()); _api->requestNotifySettings(MTP_inputNotifyBroadcasts()); } void Session::setTmpPassword(const QByteArray &password, TimeId validUntil) { if (_tmpPassword.isEmpty() || validUntil > _tmpPasswordValidUntil) { _tmpPassword = password; _tmpPasswordValidUntil = validUntil; } } QByteArray Session::validTmpPassword() const { return (_tmpPasswordValidUntil >= base::unixtime::now() + kTmpPasswordReserveTime) ? _tmpPassword : QByteArray(); } // Can be called only right before ~Session. void Session::finishLogout() { updates().updateOnline(); unlockTerms(); data().clear(); data().clearLocalStorage(); } Session::~Session() { unlockTerms(); data().clear(); ClickHandler::clearActive(); ClickHandler::unpressed(); } Account &Session::account() const { return *_account; } Storage::Account &Session::local() const { return _account->local(); } Domain &Session::domain() const { return _account->domain(); } Storage::Domain &Session::domainLocal() const { return _account->domainLocal(); } void Session::notifyDownloaderTaskFinished() { downloader().notifyTaskFinished(); } rpl::producer<> Session::downloaderTaskFinished() const { return downloader().taskFinished(); } uint64 Session::uniqueId() const { // See also Account::willHaveSessionUniqueId. return userId().bare | (mtp().isTestMode() ? 0x0100'0000'0000'0000ULL : 0ULL); } UserId Session::userId() const { return _userId; } PeerId Session::userPeerId() const { return _userId; } bool Session::validateSelf(UserId id) { if (id != userId()) { LOG(("Auth Error: wrong self user received.")); crl::on_main(this, [=] { _account->logOut(); }); return false; } return true; } void Session::saveSettings() { local().writeSessionSettings(); } void Session::saveSettingsDelayed(crl::time delay) { _saveSettingsTimer.callOnce(delay); } void Session::saveSettingsNowIfNeeded() { if (_saveSettingsTimer.isActive()) { _saveSettingsTimer.cancel(); saveSettings(); } } MTP::DcId Session::mainDcId() const { return _account->mtp().mainDcId(); } MTP::Instance &Session::mtp() const { return _account->mtp(); } const MTP::ConfigFields &Session::serverConfig() const { return _account->mtp().configValues(); } void Session::lockByTerms(const Window::TermsLock &data) { if (!_termsLock || *_termsLock != data) { _termsLock = std::make_unique(data); _termsLockChanges.fire(true); } } void Session::unlockTerms() { if (_termsLock) { _termsLock = nullptr; _termsLockChanges.fire(false); } } void Session::termsDeleteNow() { api().request(MTPaccount_DeleteAccount( MTP_string("Decline ToS update") )).send(); } std::optional Session::termsLocked() const { return _termsLock ? base::make_optional(*_termsLock) : std::nullopt; } rpl::producer Session::termsLockChanges() const { return _termsLockChanges.events(); } rpl::producer Session::termsLockValue() const { return rpl::single( _termsLock != nullptr ) | rpl::then(termsLockChanges()); } QString Session::createInternalLink(const QString &query) const { auto result = createInternalLinkFull(query); auto prefixes = { qstr("https://"), qstr("http://"), }; for (auto &prefix : prefixes) { if (result.startsWith(prefix, Qt::CaseInsensitive)) { return result.mid(prefix.size()); } } LOG(("Warning: bad internal url '%1'").arg(result)); return result; } QString Session::createInternalLinkFull(const QString &query) const { return ValidatedInternalLinksDomain(this) + query; } bool Session::supportMode() const { return (_supportHelper != nullptr); } Support::Helper &Session::supportHelper() const { Expects(supportMode()); return *_supportHelper; } Support::Templates& Session::supportTemplates() const { return supportHelper().templates(); } void Session::addWindow(not_null controller) { _windows.emplace(controller); controller->lifetime().add([=] { _windows.remove(controller); }); updates().addActiveChat(controller->activeChatChanges( ) | rpl::map([=](const Dialogs::Key &chat) { return chat.peer(); }) | rpl::distinct_until_changed()); } auto Session::windows() const -> const base::flat_set> & { return _windows; } } // namespace Main