main_session.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "main/main_session.h"
  8. #include "apiwrap.h"
  9. #include "api/api_peer_colors.h"
  10. #include "api/api_updates.h"
  11. #include "api/api_user_privacy.h"
  12. #include "main/main_account.h"
  13. #include "main/main_domain.h"
  14. #include "main/main_session_settings.h"
  15. #include "main/main_app_config.h"
  16. #include "main/session/send_as_peers.h"
  17. #include "mtproto/mtproto_config.h"
  18. #include "chat_helpers/stickers_emoji_pack.h"
  19. #include "chat_helpers/stickers_dice_pack.h"
  20. #include "chat_helpers/stickers_gift_box_pack.h"
  21. #include "history/view/reactions/history_view_reactions_strip.h"
  22. #include "history/history.h"
  23. #include "history/history_item.h"
  24. #include "inline_bots/bot_attach_web_view.h"
  25. #include "storage/file_download.h"
  26. #include "storage/download_manager_mtproto.h"
  27. #include "storage/file_upload.h"
  28. #include "storage/storage_account.h"
  29. #include "storage/storage_facade.h"
  30. #include "data/components/credits.h"
  31. #include "data/components/factchecks.h"
  32. #include "data/components/location_pickers.h"
  33. #include "data/components/recent_peers.h"
  34. #include "data/components/scheduled_messages.h"
  35. #include "data/components/sponsored_messages.h"
  36. #include "data/components/top_peers.h"
  37. #include "data/data_session.h"
  38. #include "data/data_changes.h"
  39. #include "data/data_user.h"
  40. #include "data/data_download_manager.h"
  41. #include "data/stickers/data_stickers.h"
  42. #include "window/window_session_controller.h"
  43. #include "window/window_controller.h"
  44. #include "window/window_lock_widgets.h"
  45. #include "base/unixtime.h"
  46. #include "calls/calls_instance.h"
  47. #include "support/support_helper.h"
  48. #include "lang/lang_keys.h"
  49. #include "core/application.h"
  50. #include "ui/text/text_utilities.h"
  51. #include "ui/layers/generic_box.h"
  52. #include "styles/style_layers.h"
  53. #ifndef TDESKTOP_DISABLE_SPELLCHECK
  54. #include "chat_helpers/spellchecker_common.h"
  55. #endif // TDESKTOP_DISABLE_SPELLCHECK
  56. namespace Main {
  57. namespace {
  58. constexpr auto kTmpPasswordReserveTime = TimeId(10);
  59. [[nodiscard]] QString ValidatedInternalLinksDomain(
  60. not_null<const Session*> session) {
  61. // This domain should start with 'http[s]://' and end with '/'.
  62. // Like 'https://telegram.me/' or 'https://t.me/'.
  63. const auto &domain = session->serverConfig().internalLinksDomain;
  64. const auto prefixes = {
  65. u"https://"_q,
  66. u"http://"_q,
  67. };
  68. for (const auto &prefix : prefixes) {
  69. if (domain.startsWith(prefix, Qt::CaseInsensitive)) {
  70. return domain.endsWith('/')
  71. ? domain
  72. : MTP::ConfigFields(
  73. session->mtp().environment()
  74. ).internalLinksDomain;
  75. }
  76. }
  77. return MTP::ConfigFields(
  78. session->mtp().environment()
  79. ).internalLinksDomain;
  80. }
  81. } // namespace
  82. Session::Session(
  83. not_null<Account*> account,
  84. const MTPUser &user,
  85. std::unique_ptr<SessionSettings> settings)
  86. : _userId(user.c_user().vid())
  87. , _account(account)
  88. , _settings(std::move(settings))
  89. , _changes(std::make_unique<Data::Changes>(this))
  90. , _api(std::make_unique<ApiWrap>(this))
  91. , _updates(std::make_unique<Api::Updates>(this))
  92. , _sendProgressManager(std::make_unique<Api::SendProgressManager>(this))
  93. , _downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get()))
  94. , _uploader(std::make_unique<Storage::Uploader>(_api.get()))
  95. , _storage(std::make_unique<Storage::Facade>())
  96. , _data(std::make_unique<Data::Session>(this))
  97. , _user(_data->processUser(user))
  98. , _emojiStickersPack(std::make_unique<Stickers::EmojiPack>(this))
  99. , _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this))
  100. , _giftBoxStickersPacks(std::make_unique<Stickers::GiftBoxPack>(this))
  101. , _sendAsPeers(std::make_unique<SendAsPeers>(this))
  102. , _attachWebView(std::make_unique<InlineBots::AttachWebView>(this))
  103. , _recentPeers(std::make_unique<Data::RecentPeers>(this))
  104. , _scheduledMessages(std::make_unique<Data::ScheduledMessages>(this))
  105. , _sponsoredMessages(std::make_unique<Data::SponsoredMessages>(this))
  106. , _topPeers(std::make_unique<Data::TopPeers>(this, Data::TopPeerType::Chat))
  107. , _topBotApps(
  108. std::make_unique<Data::TopPeers>(this, Data::TopPeerType::BotApp))
  109. , _factchecks(std::make_unique<Data::Factchecks>(this))
  110. , _locationPickers(std::make_unique<Data::LocationPickers>())
  111. , _credits(std::make_unique<Data::Credits>(this))
  112. , _cachedReactionIconFactory(std::make_unique<ReactionIconFactory>())
  113. , _supportHelper(Support::Helper::Create(this))
  114. , _fastButtonsBots(std::make_unique<Support::FastButtonsBots>(this))
  115. , _saveSettingsTimer([=] { saveSettings(); }) {
  116. Expects(_settings != nullptr);
  117. _api->requestTermsUpdate();
  118. _api->requestFullPeer(_user);
  119. _api->instance().setUserPhone(_user->phone());
  120. // Load current userpic and keep it loaded.
  121. _user->loadUserpic();
  122. changes().peerFlagsValue(
  123. _user,
  124. Data::PeerUpdate::Flag::Photo
  125. ) | rpl::start_with_next([=] {
  126. auto view = Ui::PeerUserpicView{ .cloud = _selfUserpicView };
  127. [[maybe_unused]] const auto image = _user->userpicCloudImage(view);
  128. _selfUserpicView = view.cloud;
  129. }, lifetime());
  130. crl::on_main(this, [=] {
  131. using Flag = Data::PeerUpdate::Flag;
  132. changes().peerUpdates(
  133. _user,
  134. Flag::Name
  135. | Flag::Username
  136. | Flag::Photo
  137. | Flag::About
  138. | Flag::PhoneNumber
  139. ) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
  140. local().writeSelf();
  141. if (update.flags & Flag::PhoneNumber) {
  142. const auto phone = _user->phone();
  143. _api->instance().setUserPhone(phone);
  144. if (!phone.isEmpty()) {
  145. _api->instance().requestConfig();
  146. }
  147. }
  148. }, _lifetime);
  149. #ifndef OS_MAC_STORE
  150. appConfig().value(
  151. ) | rpl::start_with_next([=] {
  152. _premiumPossible = !appConfig().get<bool>(
  153. u"premium_purchase_blocked"_q,
  154. true);
  155. }, _lifetime);
  156. #endif // OS_MAC_STORE
  157. if (_settings->hadLegacyCallsPeerToPeerNobody()) {
  158. api().userPrivacy().save(
  159. Api::UserPrivacy::Key::CallsPeer2Peer,
  160. Api::UserPrivacy::Rule{
  161. .option = Api::UserPrivacy::Option::Nobody
  162. });
  163. saveSettingsDelayed();
  164. }
  165. // Storage::Account uses Main::Account::session() in those methods.
  166. // So they can't be called during Main::Session construction.
  167. local().readInstalledStickers();
  168. local().readInstalledMasks();
  169. local().readInstalledCustomEmoji();
  170. local().readFeaturedStickers();
  171. local().readFeaturedCustomEmoji();
  172. local().readRecentStickers();
  173. local().readRecentMasks();
  174. local().readFavedStickers();
  175. local().readSavedGifs();
  176. data().stickers().notifyUpdated(Data::StickersType::Stickers);
  177. data().stickers().notifyUpdated(Data::StickersType::Masks);
  178. data().stickers().notifyUpdated(Data::StickersType::Emoji);
  179. data().stickers().notifySavedGifsUpdated();
  180. });
  181. #ifndef TDESKTOP_DISABLE_SPELLCHECK
  182. Spellchecker::Start(this);
  183. #endif // TDESKTOP_DISABLE_SPELLCHECK
  184. _api->requestNotifySettings(MTP_inputNotifyUsers());
  185. _api->requestNotifySettings(MTP_inputNotifyChats());
  186. _api->requestNotifySettings(MTP_inputNotifyBroadcasts());
  187. Core::App().downloadManager().trackSession(this);
  188. }
  189. void Session::setTmpPassword(const QByteArray &password, TimeId validUntil) {
  190. if (_tmpPassword.isEmpty() || validUntil > _tmpPasswordValidUntil) {
  191. _tmpPassword = password;
  192. _tmpPasswordValidUntil = validUntil;
  193. }
  194. }
  195. QByteArray Session::validTmpPassword() const {
  196. return (_tmpPasswordValidUntil
  197. >= base::unixtime::now() + kTmpPasswordReserveTime)
  198. ? _tmpPassword
  199. : QByteArray();
  200. }
  201. // Can be called only right before ~Session.
  202. void Session::finishLogout() {
  203. unlockTerms();
  204. data().clear();
  205. data().clearLocalStorage();
  206. }
  207. Session::~Session() {
  208. unlockTerms();
  209. data().clear();
  210. ClickHandler::clearActive();
  211. ClickHandler::unpressed();
  212. }
  213. Account &Session::account() const {
  214. return *_account;
  215. }
  216. Storage::Account &Session::local() const {
  217. return _account->local();
  218. }
  219. Domain &Session::domain() const {
  220. return _account->domain();
  221. }
  222. Storage::Domain &Session::domainLocal() const {
  223. return _account->domainLocal();
  224. }
  225. AppConfig &Session::appConfig() const {
  226. return _account->appConfig();
  227. }
  228. void Session::notifyDownloaderTaskFinished() {
  229. downloader().notifyTaskFinished();
  230. }
  231. rpl::producer<> Session::downloaderTaskFinished() const {
  232. return downloader().taskFinished();
  233. }
  234. bool Session::premium() const {
  235. return _user->isPremium();
  236. }
  237. bool Session::premiumPossible() const {
  238. return premium() || premiumCanBuy();
  239. }
  240. bool Session::premiumBadgesShown() const {
  241. return supportMode() || premiumPossible();
  242. }
  243. rpl::producer<bool> Session::premiumPossibleValue() const {
  244. using namespace rpl::mappers;
  245. auto premium = _user->flagsValue(
  246. ) | rpl::filter([=](UserData::Flags::Change change) {
  247. return (change.diff & UserDataFlag::Premium);
  248. }) | rpl::map([=] {
  249. return _user->isPremium();
  250. });
  251. return rpl::combine(
  252. std::move(premium),
  253. _premiumPossible.value(),
  254. _1 || _2);
  255. }
  256. bool Session::premiumCanBuy() const {
  257. return _premiumPossible.current();
  258. }
  259. bool Session::isTestMode() const {
  260. return mtp().isTestMode();
  261. }
  262. uint64 Session::uniqueId() const {
  263. // See also Account::willHaveSessionUniqueId.
  264. return userId().bare
  265. | (isTestMode() ? 0x0100'0000'0000'0000ULL : 0ULL);
  266. }
  267. UserId Session::userId() const {
  268. return _userId;
  269. }
  270. PeerId Session::userPeerId() const {
  271. return _userId;
  272. }
  273. bool Session::validateSelf(UserId id) {
  274. if (id != userId()) {
  275. LOG(("Auth Error: wrong self user received."));
  276. crl::on_main(this, [=] { _account->logOut(); });
  277. return false;
  278. }
  279. return true;
  280. }
  281. void Session::saveSettings() {
  282. local().writeSessionSettings();
  283. }
  284. void Session::saveSettingsDelayed(crl::time delay) {
  285. _saveSettingsTimer.callOnce(delay);
  286. }
  287. void Session::saveSettingsNowIfNeeded() {
  288. if (_saveSettingsTimer.isActive()) {
  289. _saveSettingsTimer.cancel();
  290. saveSettings();
  291. }
  292. }
  293. MTP::DcId Session::mainDcId() const {
  294. return _account->mtp().mainDcId();
  295. }
  296. MTP::Instance &Session::mtp() const {
  297. return _account->mtp();
  298. }
  299. const MTP::ConfigFields &Session::serverConfig() const {
  300. return _account->mtp().configValues();
  301. }
  302. void Session::lockByTerms(const Window::TermsLock &data) {
  303. if (!_termsLock || *_termsLock != data) {
  304. _termsLock = std::make_unique<Window::TermsLock>(data);
  305. _termsLockChanges.fire(true);
  306. }
  307. }
  308. void Session::unlockTerms() {
  309. if (_termsLock) {
  310. _termsLock = nullptr;
  311. _termsLockChanges.fire(false);
  312. }
  313. }
  314. void Session::termsDeleteNow() {
  315. api().request(MTPaccount_DeleteAccount(
  316. MTP_flags(0),
  317. MTP_string("Decline ToS update"),
  318. MTPInputCheckPasswordSRP()
  319. )).send();
  320. }
  321. std::optional<Window::TermsLock> Session::termsLocked() const {
  322. return _termsLock ? base::make_optional(*_termsLock) : std::nullopt;
  323. }
  324. rpl::producer<bool> Session::termsLockChanges() const {
  325. return _termsLockChanges.events();
  326. }
  327. rpl::producer<bool> Session::termsLockValue() const {
  328. return rpl::single(
  329. _termsLock != nullptr
  330. ) | rpl::then(termsLockChanges());
  331. }
  332. QString Session::createInternalLink(const QString &query) const {
  333. return createInternalLink(TextWithEntities{ .text = query }).text;
  334. }
  335. QString Session::createInternalLinkFull(const QString &query) const {
  336. return createInternalLinkFull(TextWithEntities{ .text = query }).text;
  337. }
  338. TextWithEntities Session::createInternalLink(
  339. const TextWithEntities &query) const {
  340. const auto result = createInternalLinkFull(query);
  341. const auto prefixes = {
  342. u"https://"_q,
  343. u"http://"_q,
  344. };
  345. for (auto &prefix : prefixes) {
  346. if (result.text.startsWith(prefix, Qt::CaseInsensitive)) {
  347. return Ui::Text::Mid(result, prefix.size());
  348. }
  349. }
  350. LOG(("Warning: bad internal url '%1'").arg(result.text));
  351. return result;
  352. }
  353. TextWithEntities Session::createInternalLinkFull(
  354. TextWithEntities query) const {
  355. return TextWithEntities::Simple(ValidatedInternalLinksDomain(this))
  356. .append(std::move(query));
  357. }
  358. bool Session::supportMode() const {
  359. return (_supportHelper != nullptr);
  360. }
  361. Support::Helper &Session::supportHelper() const {
  362. Expects(supportMode());
  363. return *_supportHelper;
  364. }
  365. Support::Templates& Session::supportTemplates() const {
  366. return supportHelper().templates();
  367. }
  368. Support::FastButtonsBots &Session::fastButtonsBots() const {
  369. return *_fastButtonsBots;
  370. }
  371. void Session::addWindow(not_null<Window::SessionController*> controller) {
  372. _windows.emplace(controller);
  373. controller->lifetime().add([=] {
  374. _windows.remove(controller);
  375. });
  376. updates().addActiveChat(controller->activeChatChanges(
  377. ) | rpl::map([=](Dialogs::Key chat) {
  378. return chat.peer();
  379. }) | rpl::distinct_until_changed());
  380. }
  381. bool Session::uploadsInProgress() const {
  382. return !!_uploader->currentUploadId();
  383. }
  384. void Session::uploadsStopWithConfirmation(Fn<void()> done) {
  385. const auto id = _uploader->currentUploadId();
  386. const auto message = data().message(id);
  387. const auto exists = (message != nullptr);
  388. const auto window = message
  389. ? Core::App().windowFor(message->history()->peer)
  390. : Core::App().activePrimaryWindow();
  391. if (!window) {
  392. done();
  393. return;
  394. }
  395. auto box = Box([=](not_null<Ui::GenericBox*> box) {
  396. box->addRow(
  397. object_ptr<Ui::FlatLabel>(
  398. box.get(),
  399. tr::lng_upload_sure_stop(),
  400. st::boxLabel),
  401. st::boxPadding + QMargins(0, 0, 0, st::boxPadding.bottom()));
  402. box->setStyle(st::defaultBox);
  403. box->addButton(tr::lng_selected_upload_stop(), [=] {
  404. box->closeBox();
  405. uploadsStop();
  406. if (done) {
  407. done();
  408. }
  409. }, st::attentionBoxButton);
  410. box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
  411. if (exists) {
  412. box->addLeftButton(tr::lng_upload_show_file(), [=] {
  413. box->closeBox();
  414. if (const auto item = data().message(id)) {
  415. if (const auto window = tryResolveWindow()) {
  416. window->showMessage(item);
  417. }
  418. }
  419. });
  420. }
  421. });
  422. window->show(std::move(box));
  423. window->activate();
  424. }
  425. void Session::uploadsStop() {
  426. _uploader->cancelAll();
  427. }
  428. auto Session::windows() const
  429. -> const base::flat_set<not_null<Window::SessionController*>> & {
  430. return _windows;
  431. }
  432. Window::SessionController *Session::tryResolveWindow(
  433. PeerData *forPeer) const {
  434. if (forPeer) {
  435. auto primary = (Window::SessionController*)nullptr;
  436. for (const auto &window : _windows) {
  437. const auto thread = window->windowId().thread;
  438. if (thread && thread->peer() == forPeer) {
  439. return window;
  440. } else if (window->isPrimary()) {
  441. primary = window;
  442. }
  443. }
  444. if (primary) {
  445. return primary;
  446. }
  447. }
  448. if (_windows.empty() || forPeer) {
  449. domain().activate(_account);
  450. if (_windows.empty()) {
  451. return nullptr;
  452. }
  453. }
  454. for (const auto &window : _windows) {
  455. if (window->isPrimary()) {
  456. return window;
  457. }
  458. }
  459. return _windows.front();
  460. }
  461. auto Session::colorIndicesValue()
  462. -> rpl::producer<Ui::ColorIndicesCompressed> {
  463. return api().peerColors().indicesValue();
  464. }
  465. } // namespace Main