diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d3c8186b0c..ea058fc407 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -155,6 +155,7 @@ PRIVATE api/api_chat_filters.cpp api/api_chat_filters.h api/api_common.h + api/api_hash.cpp api/api_hash.h api/api_self_destruct.cpp api/api_self_destruct.h @@ -883,6 +884,10 @@ PRIVATE settings/settings_privacy_controllers.h settings/settings_privacy_security.cpp settings/settings_privacy_security.h + storage/details/storage_file_utilities.cpp + storage/details/storage_file_utilities.h + storage/details/storage_settings_scheme.cpp + storage/details/storage_settings_scheme.h storage/download_manager_mtproto.cpp storage/download_manager_mtproto.h storage/file_download.cpp @@ -901,6 +906,8 @@ PRIVATE storage/serialize_common.h storage/serialize_document.cpp storage/serialize_document.h + storage/storage_account.cpp + storage/storage_account.h storage/storage_cloud_blob.cpp storage/storage_cloud_blob.h storage/storage_facade.cpp diff --git a/Telegram/SourceFiles/api/api_hash.cpp b/Telegram/SourceFiles/api/api_hash.cpp new file mode 100644 index 0000000000..3d366d53e1 --- /dev/null +++ b/Telegram/SourceFiles/api/api_hash.cpp @@ -0,0 +1,94 @@ +/* +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 "api/api_hash.h" + +#include "data/data_document.h" +#include "data/data_session.h" +#include "data/stickers/data_stickers.h" +#include "main/main_session.h" + +namespace Api { +namespace { + +[[nodiscard]] int32 CountDocumentVectorHash( + const QVector vector) { + auto result = HashInit(); + for (const auto document : vector) { + HashUpdate(result, document->id); + } + return HashFinalize(result); +} + +[[nodiscard]] int32 CountSpecialStickerSetHash( + not_null session, + uint64 setId) { + const auto &sets = session->data().stickers().sets(); + const auto it = sets.find(setId); + if (it != sets.cend()) { + return CountDocumentVectorHash(it->second->stickers); + } + return 0; +} + +} // namespace + +int32 CountStickersHash( + not_null session, + bool checkOutdatedInfo) { + auto result = HashInit(); + bool foundOutdated = false; + const auto &sets = session->data().stickers().sets(); + const auto &order = session->data().stickers().setsOrder(); + for (auto i = order.cbegin(), e = order.cend(); i != e; ++i) { + auto it = sets.find(*i); + if (it != sets.cend()) { + const auto set = it->second.get(); + if (set->id == Data::Stickers::DefaultSetId) { + foundOutdated = true; + } else if (!(set->flags & MTPDstickerSet_ClientFlag::f_special) + && !(set->flags & MTPDstickerSet::Flag::f_archived)) { + HashUpdate(result, set->hash); + } + } + } + return (!checkOutdatedInfo || !foundOutdated) + ? HashFinalize(result) + : 0; +} + +int32 CountRecentStickersHash(not_null session) { + return CountSpecialStickerSetHash( + session, + Data::Stickers::CloudRecentSetId); +} + +int32 CountFavedStickersHash(not_null session) { + return CountSpecialStickerSetHash(session, Data::Stickers::FavedSetId); +} + +int32 CountFeaturedStickersHash(not_null session) { + auto result = HashInit(); + const auto &sets = session->data().stickers().sets(); + const auto &featured = session->data().stickers().featuredSetsOrder(); + for (const auto setId : featured) { + HashUpdate(result, setId); + + const auto it = sets.find(setId); + if (it != sets.cend() + && (it->second->flags & MTPDstickerSet_ClientFlag::f_unread)) { + HashUpdate(result, 1); + } + } + return HashFinalize(result); +} + +int32 CountSavedGifsHash(not_null session) { + return CountDocumentVectorHash(session->data().stickers().savedGifs()); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_hash.h b/Telegram/SourceFiles/api/api_hash.h index ed760cb964..6eb5f204c4 100644 --- a/Telegram/SourceFiles/api/api_hash.h +++ b/Telegram/SourceFiles/api/api_hash.h @@ -7,8 +7,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace Main { +class Session; +} // namespace Main + namespace Api { +[[nodiscard]] int32 CountStickersHash( + not_null session, + bool checkOutdatedInfo = false); +[[nodiscard]] int32 CountRecentStickersHash( + not_null session); +[[nodiscard]] int32 CountFavedStickersHash(not_null session); +[[nodiscard]] int32 CountFeaturedStickersHash( + not_null session); +[[nodiscard]] int32 CountSavedGifsHash(not_null session); + [[nodiscard]] inline uint32 HashInit() { return 0; } diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index e3866dc758..f4a08928af 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "apiwrap.h" +#include "api/api_hash.h" #include "api/api_sending.h" #include "api/api_text_entities.h" #include "api/api_self_destruct.h" @@ -45,7 +46,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "history/history_item_components.h" //#include "history/feed/history_feed_section.h" // #feed -#include "storage/localstorage.h" #include "main/main_session.h" #include "main/main_account.h" #include "boxes/confirm_box.h" @@ -67,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_shared_media.h" #include "storage/storage_user_photos.h" #include "storage/storage_media_prepare.h" +#include "storage/storage_account.h" #include "facades.h" #include "app.h" //#include "storage/storage_feed_messages.h" // #feed @@ -267,6 +268,10 @@ Main::Session &ApiWrap::session() const { return *_session; } +Storage::Account &ApiWrap::local() const { + return _session->local(); +} + void ApiWrap::setupSupportMode() { if (!_session->supportMode()) { return; @@ -1980,11 +1985,12 @@ void ApiWrap::saveStickerSets( } } - if (writeInstalled) Local::writeInstalledStickers(); - if (writeRecent) Local::writeUserSettings(); - if (writeArchived) Local::writeArchivedStickers(); - if (writeCloudRecent) Local::writeRecentStickers(); - if (writeFaved) Local::writeFavedStickers(); + auto &storage = local(); + if (writeInstalled) storage.writeInstalledStickers(); + if (writeRecent) storage.writeSettings(); + if (writeArchived) storage.writeArchivedStickers(); + if (writeCloudRecent) storage.writeRecentStickers(); + if (writeFaved) storage.writeFavedStickers(); _session->data().stickers().notifyUpdated(); if (_stickerSetDisenableRequests.empty()) { @@ -2927,25 +2933,25 @@ void ApiWrap::refreshFileReference( request(MTPmessages_GetRecentStickers( MTP_flags(0), MTP_int(0)), - [] { crl::on_main([] { Local::writeRecentStickers(); }); }); + [=] { crl::on_main(&session(), [=] { local().writeRecentStickers(); }); }); } else if (data.setId == Data::Stickers::FavedSetId) { request(MTPmessages_GetFavedStickers(MTP_int(0)), - [] { crl::on_main([] { Local::writeFavedStickers(); }); }); + [=] { crl::on_main(&session(), [=] { local().writeFavedStickers(); }); }); } else { request(MTPmessages_GetStickerSet( MTP_inputStickerSetID( MTP_long(data.setId), MTP_long(data.accessHash))), - [] { crl::on_main([] { - Local::writeInstalledStickers(); - Local::writeRecentStickers(); - Local::writeFavedStickers(); + [=] { crl::on_main(&session(), [=] { + local().writeInstalledStickers(); + local().writeRecentStickers(); + local().writeFavedStickers(); }); }); } }, [&](Data::FileOriginSavedGifs data) { request( MTPmessages_GetSavedGifs(MTP_int(0)), - [] { crl::on_main([] { Local::writeSavedGifs(); }); }); + [=] { crl::on_main(&session(), [=] { local().writeSavedGifs(); }); }); }, [&](Data::FileOriginWallpaper data) { request(MTPaccount_GetWallPaper( MTP_inputWallPaper( @@ -3165,7 +3171,7 @@ void ApiWrap::requestStickers(TimeId now) { } }; _stickersUpdateRequest = request(MTPmessages_GetAllStickers( - MTP_int(Local::countStickersHash(true)) + MTP_int(Api::CountStickersHash(&session(), true)) )).done(onDone).fail([=](const RPCError &error) { LOG(("App Fail: Failed to get stickers!")); onDone(MTP_messages_allStickersNotModified()); @@ -3176,7 +3182,8 @@ void ApiWrap::requestRecentStickers(TimeId now) { if (!_session->data().stickers().recentUpdateNeeded(now)) { return; } - requestRecentStickersWithHash(Local::countRecentStickersHash()); + requestRecentStickersWithHash( + Api::CountRecentStickersHash(&session())); } void ApiWrap::requestRecentStickersWithHash(int32 hash) { @@ -3218,7 +3225,7 @@ void ApiWrap::requestFavedStickers(TimeId now) { return; } _favedStickersUpdateRequest = request(MTPmessages_GetFavedStickers( - MTP_int(Local::countFavedStickersHash()) + MTP_int(Api::CountFavedStickersHash(&session())) )).done([=](const MTPmessages_FavedStickers &result) { _session->data().stickers().setLastFavedUpdate(crl::now()); _favedStickersUpdateRequest = 0; @@ -3250,7 +3257,7 @@ void ApiWrap::requestFeaturedStickers(TimeId now) { return; } _featuredStickersUpdateRequest = request(MTPmessages_GetFeaturedStickers( - MTP_int(Local::countFeaturedStickersHash()) + MTP_int(Api::CountFeaturedStickersHash(&session())) )).done([=](const MTPmessages_FeaturedStickers &result) { _session->data().stickers().setLastFeaturedUpdate(crl::now()); _featuredStickersUpdateRequest = 0; @@ -3280,7 +3287,7 @@ void ApiWrap::requestSavedGifs(TimeId now) { return; } _savedGifsUpdateRequest = request(MTPmessages_GetSavedGifs( - MTP_int(Local::countSavedGifsHash()) + MTP_int(Api::CountSavedGifsHash(&session())) )).done([=](const MTPmessages_SavedGifs &result) { _session->data().stickers().setLastSavedGifsUpdate(crl::now()); _savedGifsUpdateRequest = 0; @@ -3331,7 +3338,7 @@ void ApiWrap::readFeaturedSets() { auto requestData = MTPmessages_ReadFeaturedStickers( MTP_vector(wrappedIds)); request(std::move(requestData)).done([=](const MTPBool &result) { - Local::writeFeaturedStickers(); + local().writeFeaturedStickers(); _session->data().stickers().notifyUpdated(); }).send(); @@ -4786,7 +4793,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { if (!peer->canWrite() || Api::SendDice(message)) { return; } - Local::saveRecentSentHashtags(textWithTags.text); + local().saveRecentSentHashtags(textWithTags.text); auto sending = TextWithEntities(); auto left = TextWithEntities { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 33631a9d71..3d2a2467c5 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -38,6 +38,7 @@ namespace Storage { enum class SharedMediaType : signed char; struct PreparedList; class DownloadMtprotoTask; +class Account; } // namespace Storage namespace Dialogs { @@ -136,7 +137,8 @@ public: explicit ApiWrap(not_null session); ~ApiWrap(); - Main::Session &session() const; + [[nodiscard]] Main::Session &session() const; + [[nodiscard]] Storage::Account &local() const; void applyUpdates( const MTPUpdates &updates, diff --git a/Telegram/SourceFiles/boxes/auto_download_box.cpp b/Telegram/SourceFiles/boxes/auto_download_box.cpp index b7bf9915a4..fc246bdd6b 100644 --- a/Telegram/SourceFiles/boxes/auto_download_box.cpp +++ b/Telegram/SourceFiles/boxes/auto_download_box.cpp @@ -210,7 +210,7 @@ void AutoDownloadBox::setupContent() { } } if (changed || hiddenChanged) { - Local::writeUserSettings(); + _session->saveSettingsDelayed(); } if (allowMoreTypes.contains(Type::Photo)) { _session->data().photoLoadSettingsChanged(); diff --git a/Telegram/SourceFiles/boxes/auto_lock_box.cpp b/Telegram/SourceFiles/boxes/auto_lock_box.cpp index bdde54b6e4..129f60a321 100644 --- a/Telegram/SourceFiles/boxes/auto_lock_box.cpp +++ b/Telegram/SourceFiles/boxes/auto_lock_box.cpp @@ -43,7 +43,7 @@ void AutoLockBox::prepare() { void AutoLockBox::durationChanged(int seconds) { Global::SetAutoLock(seconds); - Local::writeUserSettings(); + _session->saveSettingsDelayed(); Global::RefLocalPasscodeChanged().notify(); Core::App().checkAutoLock(); diff --git a/Telegram/SourceFiles/boxes/download_path_box.cpp b/Telegram/SourceFiles/boxes/download_path_box.cpp index ecd00ff84c..5ced23d4b3 100644 --- a/Telegram/SourceFiles/boxes/download_path_box.cpp +++ b/Telegram/SourceFiles/boxes/download_path_box.cpp @@ -8,17 +8,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/download_path_box.h" #include "lang/lang_keys.h" -#include "storage/localstorage.h" #include "core/file_utilities.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" #include "platform/platform_specific.h" #include "facades.h" +#include "window/window_session_controller.h" +#include "main/main_session.h" +#include "storage/storage_account.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" -DownloadPathBox::DownloadPathBox(QWidget *parent) -: _path(Global::DownloadPath()) +DownloadPathBox::DownloadPathBox( + QWidget *parent, + not_null controller) +: _controller(controller) +, _path(Global::DownloadPath()) , _pathBookmark(Global::DownloadPathBookmark()) , _group(std::make_shared>(typeFromPath(_path))) , _default(this, _group, Directory::Downloads, tr::lng_download_path_default_radio(tr::now), st::defaultBoxCheckbox) @@ -119,7 +124,7 @@ void DownloadPathBox::save() { }; Global::SetDownloadPath(computePath()); Global::SetDownloadPathBookmark((value == Directory::Custom) ? _pathBookmark : QByteArray()); - Local::writeUserSettings(); + _controller->session().local().writeSettings(); Global::RefDownloadPathChanged().notify(); closeBox(); #endif // OS_WIN_STORE diff --git a/Telegram/SourceFiles/boxes/download_path_box.h b/Telegram/SourceFiles/boxes/download_path_box.h index 4ff63efce6..76b0f0b473 100644 --- a/Telegram/SourceFiles/boxes/download_path_box.h +++ b/Telegram/SourceFiles/boxes/download_path_box.h @@ -18,9 +18,15 @@ class Radioenum; class LinkButton; } // namespace Ui +namespace Window { +class SessionController; +} // namespace Window + class DownloadPathBox : public Ui::BoxContent { public: - DownloadPathBox(QWidget *parent); + DownloadPathBox( + QWidget *parent, + not_null controller); protected: void prepare() override; @@ -48,6 +54,7 @@ private: void setPathText(const QString &text); void editPath(); + const not_null _controller; QString _path; QByteArray _pathBookmark; diff --git a/Telegram/SourceFiles/boxes/local_storage_box.cpp b/Telegram/SourceFiles/boxes/local_storage_box.cpp index b850b4005f..4f6d685e8e 100644 --- a/Telegram/SourceFiles/boxes/local_storage_box.cpp +++ b/Telegram/SourceFiles/boxes/local_storage_box.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/continuous_sliders.h" #include "ui/effects/radial_animation.h" #include "ui/emoji_config.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "storage/cache/storage_cache_database.h" #include "data/data_session.h" #include "lang/lang_keys.h" @@ -271,8 +271,8 @@ LocalStorageBox::LocalStorageBox( : _session(session) , _db(&session->data().cache()) , _dbBig(&session->data().cacheBigFile()) { - const auto &settings = Local::cacheSettings(); - const auto &settingsBig = Local::cacheBigFileSettings(); + const auto &settings = session->local().cacheSettings(); + const auto &settingsBig = session->local().cacheBigFileSettings(); _totalSizeLimit = settings.totalSizeLimit + settingsBig.totalSizeLimit; _mediaSizeLimit = settingsBig.totalSizeLimit; _timeLimit = settings.totalTimeLimit; @@ -560,8 +560,8 @@ void LocalStorageBox::setupLimits(not_null container) { } void LocalStorageBox::limitsChanged() { - const auto &settings = Local::cacheSettings(); - const auto &settingsBig = Local::cacheBigFileSettings(); + const auto &settings = _session->local().cacheSettings(); + const auto &settingsBig = _session->local().cacheBigFileSettings(); const auto sizeLimit = _totalSizeLimit - _mediaSizeLimit; const auto changed = (settings.totalSizeLimit != sizeLimit) || (settingsBig.totalSizeLimit != _mediaSizeLimit) @@ -590,7 +590,7 @@ void LocalStorageBox::save() { auto updateBig = Storage::Cache::Database::SettingsUpdate(); updateBig.totalSizeLimit = _mediaSizeLimit; updateBig.totalTimeLimit = _timeLimit; - Local::updateCacheSettings(update, updateBig); + _session->local().updateCacheSettings(update, updateBig); _session->data().cache().updateSettings(update); closeBox(); } diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 43cc168f29..85a5e3bc88 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "main/main_session.h" #include "core/application.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" @@ -451,7 +451,7 @@ void PasscodeBox::save(bool force) { return; } - if (Local::checkPasscode(old.toUtf8())) { + if (_session->local().checkPasscode(old.toUtf8())) { cSetPasscodeBadTries(0); if (_turningOff) pwd = conf = QString(); } else { @@ -519,7 +519,7 @@ void PasscodeBox::save(bool force) { closeReplacedBy(); const auto weak = Ui::MakeWeak(this); cSetPasscodeBadTries(0); - Local::setPasscode(pwd.toUtf8()); + _session->local().setPasscode(pwd.toUtf8()); Core::App().localPasscodeChanged(); if (weak) { closeBox(); diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 09bb042f06..b44f76d277 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "mainwidget.h" #include "base/qthelp_url.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "boxes/confirm_box.h" #include "apiwrap.h" #include "ui/toast/toast.h" @@ -1101,7 +1101,7 @@ QString AppendShareGameScoreUrl( *reinterpret_cast(shareHashEncrypted.data()) ^= *reinterpret_cast(channelAccessHashInts); // Encrypt data. - if (!Local::encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) { + if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) { return url; } @@ -1137,7 +1137,7 @@ void ShareGameScoreByHash( // Decrypt data. auto hashData = QByteArray(hashEncrypted.size() - key128Size, Qt::Uninitialized); - if (!Local::decrypt(hashEncrypted.constData() + key128Size, hashData.data(), hashEncrypted.size() - key128Size, hashEncrypted.constData())) { + if (!session->local().decrypt(hashEncrypted.constData() + key128Size, hashData.data(), hashEncrypted.size() - key128Size, hashEncrypted.constData())) { return; } diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index d4c560d9db..0ee09b5096 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "core/application.h" #include "mtproto/sender.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "dialogs/dialogs_layout.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" @@ -417,9 +417,9 @@ void StickerSetBox::Inner::installDone( result.c_messages_stickerSetInstallResultArchive()); } else { if (wasArchived) { - Local::writeArchivedStickers(); + _controller->session().local().writeArchivedStickers(); } - Local::writeInstalledStickers(); + _controller->session().local().writeInstalledStickers(); _controller->session().data().stickers().notifyUpdated(); } _setInstalled.fire_copy(_setId); diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 871ea31069..7f2d83a124 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "boxes/sticker_set_box.h" #include "apiwrap.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "dialogs/dialogs_layout.h" #include "lottie/lottie_single_player.h" #include "chat_helpers/stickers_lottie.h" @@ -498,7 +498,7 @@ void StickersBox::getArchivedDone( void StickersBox::prepare() { if (_section == Section::Installed) { if (_tabs) { - Local::readArchivedStickers(); + _session->local().readArchivedStickers(); } else { setTitle(tr::lng_stickers_group_set()); } @@ -866,7 +866,7 @@ void StickersBox::saveChanges() { rebuildList(&_installed); if (_someArchivedLoaded) { - Local::writeArchivedStickers(); + _session->local().writeArchivedStickers(); } _session->api().saveStickerSets(_installed.widget()->getOrder(), _installed.widget()->getRemovedSets()); } diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 27dac2b80e..c1480a007e 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -19,7 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers_lottie.h" #include "mainwindow.h" #include "apiwrap.h" -#include "storage/localstorage.h" +#include "main/main_session.h" +#include "storage/storage_account.h" #include "lottie/lottie_single_player.h" #include "ui/widgets/scroll_area.h" #include "ui/image/image.h" @@ -931,7 +932,7 @@ void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) { } } if (removed) { - Local::writeRecentHashtagsAndBots(); + _controller->session().local().writeRecentHashtagsAndBots(); } _parent->updateFiltered(); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index c5e1a0dbd6..d0d48499de 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_animation.h" #include "boxes/stickers_box.h" #include "inline_bots/inline_bot_result.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "lang/lang_keys.h" #include "mainwindow.h" #include "dialogs/dialogs_layout.h" @@ -2127,7 +2127,7 @@ void StickersListWidget::removeRecentSticker(int section, int index) { for (int32 i = 0, l = recent.size(); i < l; ++i) { if (recent.at(i).first == document) { recent.removeAt(i); - Local::writeUserSettings(); + session().local().writeSettings(); refresh = true; break; } @@ -2142,7 +2142,7 @@ void StickersListWidget::removeRecentSticker(int section, int index) { if (set->stickers.isEmpty()) { sets.erase(it); } - Local::writeInstalledStickers(); + session().local().writeInstalledStickers(); refresh = true; break; } @@ -2573,7 +2573,7 @@ void StickersListWidget::refreshMegagroupStickers(GroupStickersPlace place) { auto removeHiddenForGroup = [this, &hidden] { if (hidden) { session().settings().removeGroupStickersSectionHidden(_megagroupSet->id); - Local::writeUserSettings(); + session().local().writeSettings(); hidden = false; } }; @@ -2984,7 +2984,7 @@ void StickersListWidget::sendInstallRequest( void StickersListWidget::removeMegagroupSet(bool locally) { if (locally) { session().settings().setGroupStickersSectionHidden(_megagroupSet->id); - Local::writeUserSettings(); + session().local().writeSettings(); refreshStickers(); return; } @@ -3044,8 +3044,8 @@ void StickersListWidget::removeSet(uint64 setId) { int removeIndex = session().data().stickers().setsOrder().indexOf(_removingSetId); if (removeIndex >= 0) session().data().stickers().setsOrderRef().removeAt(removeIndex); refreshStickers(); - Local::writeInstalledStickers(); - if (writeRecent) Local::writeUserSettings(); + session().local().writeInstalledStickers(); + if (writeRecent) session().local().writeSettings(); } _removingSetId = 0; _checkForHide.fire({}); diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index d85c534965..3350f0c1b6 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -56,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/emoji_config.h" #include "ui/effects/animations.h" #include "storage/serialize_common.h" +#include "storage/storage_account.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "base/qthelp_regex.h" @@ -124,6 +125,11 @@ Application::Application(not_null launcher) ) | rpl::filter([=](MTP::Instance *instance) { return instance != nullptr; }) | rpl::start_with_next([=](not_null mtp) { + if (_window) { + // This should be called when user settings are read. + // Right now after they are read the startMtp() is called. + _window->widget()->updateTrayMenu(); + } if (!UpdaterDisabled()) { UpdateChecker().setMtproto(mtp.get()); } @@ -232,8 +238,8 @@ void Application::run() { startShortcuts(); App::initMedia(); - Local::ReadMapState state = Local::readMap(QByteArray()); - if (state == Local::ReadMapPassNeeded) { + const auto state = activeAccount().local().start(QByteArray()); + if (state == Storage::StartResult::IncorrectPasscode) { Global::SetLocalPasscode(true); Global::RefLocalPasscodeChanged().notify(); lockByPasscode(); diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index 828a6efb72..bbe5b2d1c6 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -7,8 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "core/changelogs.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "lang/lang_keys.h" +#include "main/main_session.h" #include "data/data_session.h" #include "mainwindow.h" #include "apiwrap.h" @@ -79,7 +80,7 @@ Changelogs::Changelogs(not_null session, int oldVersion) std::unique_ptr Changelogs::Create( not_null session) { - const auto oldVersion = Local::oldMapVersion(); + const auto oldVersion = session->local().oldMapVersion(); return (oldVersion > 0 && oldVersion < AppVersion) ? std::make_unique(session, oldVersion) : nullptr; diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index a81402d77a..f84d1128f2 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "boxes/confirm_box.h" #include "base/qthelp_regex.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "history/view/history_view_element.h" #include "history/history_item.h" #include "data/data_user.h" @@ -83,12 +83,14 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const { }; if (url.startsWith(qstr("tg://"), Qt::CaseInsensitive)) { open(); - } else if (!_bot || _bot->isVerified() || Local::isBotTrusted(_bot)) { + } else if (!_bot + || _bot->isVerified() + || _bot->session().local().isBotTrusted(_bot)) { open(); } else { const auto callback = [=, bot = _bot] { Ui::hideLayer(); - Local::makeBotTrusted(bot); + bot->session().local().markBotTrusted(bot); open(); }; Ui::show(Box( diff --git a/Telegram/SourceFiles/core/file_utilities.cpp b/Telegram/SourceFiles/core/file_utilities.cpp index 2354e0559f..e1f8eb2683 100644 --- a/Telegram/SourceFiles/core/file_utilities.cpp +++ b/Telegram/SourceFiles/core/file_utilities.cpp @@ -345,7 +345,7 @@ bool GetDefault( QString path = files.isEmpty() ? QString() : QFileInfo(files.back()).absoluteDir().absolutePath(); if (!path.isEmpty() && path != cDialogLastPath()) { cSetDialogLastPath(path); - Local::writeUserSettings(); + Local::writeSettings(); } return !files.isEmpty(); } else if (type == Type::ReadFolder) { @@ -366,7 +366,7 @@ bool GetDefault( auto path = QFileInfo(file).absoluteDir().absolutePath(); if (!path.isEmpty() && path != cDialogLastPath()) { cSetDialogLastPath(path); - Local::writeUserSettings(); + Local::writeSettings(); } } files = QStringList(file); diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 7c63556aba..cd1a9305b0 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_loader_mtproto.h" #include "media/streaming/media_streaming_loader_local.h" #include "storage/localstorage.h" +#include "storage/storage_account.h" #include "storage/streamed_file_downloader.h" #include "storage/file_download_mtproto.h" #include "storage/file_download_web.h" @@ -138,7 +139,7 @@ QString FileNameUnsafe( QString path = dir.absolutePath(); if (path != cDialogLastPath()) { cSetDialogLastPath(path); - Local::writeUserSettings(); + Local::writeSettings(); } } @@ -901,11 +902,11 @@ void DocumentData::setLoadedInMediaCache(bool loaded) { _flags = flags; if (filepath().isEmpty()) { if (loadedInMediaCache()) { - Local::writeFileLocation( + session().local().writeFileLocation( mediaKey(), FileLocation::InMediaCacheLocation()); } else { - Local::removeFileLocation(mediaKey()); + session().local().removeFileLocation(mediaKey()); } owner().requestDocumentViewRepaint(this); } @@ -941,7 +942,9 @@ void DocumentData::save( f.close(); setLocation(FileLocation(toFile)); - Local::writeFileLocation(mediaKey(), FileLocation(toFile)); + session().local().writeFileLocation( + mediaKey(), + FileLocation(toFile)); } else if (l.accessEnable()) { const auto &alreadyName = l.name(); if (alreadyName != toFile) { @@ -1136,7 +1139,7 @@ QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform) { const FileLocation &DocumentData::location(bool check) const { if (check && !_location.check()) { - const auto location = Local::readFileLocation(mediaKey()); + const auto location = session().local().readFileLocation(mediaKey()); const auto that = const_cast(this); if (location.inMediaCache()) { that->setLoadedInMediaCacheLocation(); @@ -1190,7 +1193,7 @@ bool DocumentData::saveFromDataChecked() { } file.close(); _location = FileLocation(path); - Local::writeFileLocation(mediaKey(), _location); + session().local().writeFileLocation(mediaKey(), _location); return true; } @@ -1543,13 +1546,13 @@ void DocumentData::setRemoteLocation( _access = access; if (!isNull()) { if (_location.check()) { - Local::writeFileLocation(mediaKey(), _location); + session().local().writeFileLocation(mediaKey(), _location); } else { - _location = Local::readFileLocation(mediaKey()); + _location = session().local().readFileLocation(mediaKey()); if (_location.inMediaCache()) { setLoadedInMediaCacheLocation(); } else if (_location.isEmpty() && loadedInMediaCache()) { - Local::writeFileLocation( + session().local().writeFileLocation( mediaKey(), FileLocation::InMediaCacheLocation()); } @@ -1579,7 +1582,7 @@ void DocumentData::collectLocalData(not_null local) { } if (!local->_location.inMediaCache() && !local->_location.isEmpty()) { _location = local->_location; - Local::writeFileLocation(mediaKey(), _location); + session().local().writeFileLocation(mediaKey(), _location); } } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 10c3e768ba..600aa1366a 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media.h" #include "history/view/history_view_element.h" #include "inline_bots/inline_bot_layout_item.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "storage/storage_encrypted_file.h" #include "main/main_account.h" #include "media/player/media_player_instance.h" // instance()->play() @@ -203,11 +203,11 @@ std::vector ExtractUnavailableReasons( Session::Session(not_null session) : _session(session) , _cache(Core::App().databases().get( - Local::cachePath(), - Local::cacheSettings())) + _session->local().cachePath(), + _session->local().cacheSettings())) , _bigFileCache(Core::App().databases().get( - Local::cacheBigFilePath(), - Local::cacheBigFileSettings())) + _session->local().cacheBigFilePath(), + _session->local().cacheBigFileSettings())) , _chatsList(FilterId(), PinnedDialogsCountMaxValue(session)) , _contactsList(Dialogs::SortMode::Name) , _contactsNoChatsList(Dialogs::SortMode::Name) @@ -225,11 +225,11 @@ Session::Session(not_null session) , _mediaRotation(std::make_unique()) , _histories(std::make_unique(this)) , _stickers(std::make_unique(this)) { - _cache->open(Local::cacheKey()); - _bigFileCache->open(Local::cacheBigFileKey()); + _cache->open(_session->local().cacheKey()); + _bigFileCache->open(_session->local().cacheBigFileKey()); if constexpr (Platform::IsLinux()) { - const auto wasVersion = Local::oldMapVersion(); + const auto wasVersion = _session->local().oldMapVersion(); if (wasVersion >= 1007011 && wasVersion < 1007015) { _bigFileCache->clear(); _cache->clearByTag(Data::kImageCacheTag); @@ -446,7 +446,7 @@ not_null Session::processUser(const MTPUser &data) { && (result->id != _session->userPeerId()); } - // see also Local::readPeer + // see also Serialize::readPeer const auto pname = (showPhoneChanged || phoneChanged || nameChanged) ? ((showPhone && !phone.isEmpty()) @@ -2433,7 +2433,7 @@ void Session::documentConvert( cache().moveIfEmpty(oldCacheKey, original->cacheKey()); cache().moveIfEmpty(oldGoodKey, original->goodThumbnailCacheKey()); if (stickers().savedGifs().indexOf(original) >= 0) { - Local::writeSavedGifs(); + _session->local().writeSavedGifs(); } } } diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 9389f5dbe3..f9b64be55d 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -71,7 +71,7 @@ void UserData::setIsContact(bool is) { } } -// see Local::readPeer as well +// see Serialize::readPeer as well void UserData::setPhoto(const MTPUserProfilePhoto &photo) { photo.match([&](const MTPDuserProfilePhoto &data) { updateUserpic( diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index adc0358fda..756e7a6a5f 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/stickers/data_stickers.h" +#include "api/api_hash.h" #include "data/data_document.h" #include "data/data_session.h" #include "data/data_user.h" @@ -16,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/history_item_components.h" #include "apiwrap.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "main/main_session.h" #include "ui/toast/toast.h" #include "ui/image/image_location_factory.h" @@ -73,7 +74,7 @@ void Stickers::addSavedGif(not_null document) { if (_savedGifs.size() > Global::SavedGifsLimit()) { _savedGifs.pop_back(); } - Local::writeSavedGifs(); + session().local().writeSavedGifs(); notifySavedGifsUpdated(); setLastSavedGifsUpdate(0); @@ -136,8 +137,8 @@ void Stickers::applyArchivedResult( } session().api().requestStickerSets(); } - Local::writeInstalledStickers(); - Local::writeArchivedStickers(); + session().local().writeInstalledStickers(); + session().local().writeArchivedStickers(); Ui::Toast::Show(Ui::Toast::Config{ .text = { tr::lng_stickers_packs_archived(tr::now) }, @@ -219,15 +220,15 @@ void Stickers::installLocally(uint64 setId) { sets.erase(customIt); } } - Local::writeInstalledStickers(); + session().local().writeInstalledStickers(); if (changedFlags & MTPDstickerSet_ClientFlag::f_unread) { - Local::writeFeaturedStickers(); + session().local().writeFeaturedStickers(); } if (changedFlags & MTPDstickerSet::Flag::f_archived) { auto index = archivedSetsOrderRef().indexOf(setId); if (index >= 0) { archivedSetsOrderRef().removeAt(index); - Local::writeArchivedStickers(); + session().local().writeArchivedStickers(); } } notifyUpdated(); @@ -250,7 +251,7 @@ void Stickers::undoInstallLocally(uint64 setId) { order.removeAt(currentIndex); } - Local::writeInstalledStickers(); + session().local().writeInstalledStickers(); notifyUpdated(); Ui::show( @@ -353,7 +354,7 @@ void Stickers::setIsFaved( requestSetToPushFaved(document); return; } - Local::writeFavedStickers(); + session().local().writeFavedStickers(); notifyUpdated(); session().api().stickerSetInstalled(FavedSetId); } @@ -423,7 +424,7 @@ void Stickers::setIsNotFaved(not_null document) { if (set->stickers.empty()) { sets.erase(it); } - Local::writeFavedStickers(); + session().local().writeFavedStickers(); notifyUpdated(); } @@ -492,11 +493,14 @@ void Stickers::setsReceived(const QVector &data, int32 hash) { api.requestStickerSets(); } - Local::writeInstalledStickers(); - if (writeRecent) Local::writeUserSettings(); + session().local().writeInstalledStickers(); + if (writeRecent) session().local().writeSettings(); - if (Local::countStickersHash() != hash) { - LOG(("API Error: received stickers hash %1 while counted hash is %2").arg(hash).arg(Local::countStickersHash())); + const auto counted = Api::CountStickersHash(&session()); + if (counted != hash) { + LOG(("API Error: received stickers hash %1 while counted hash is %2" + ).arg(hash + ).arg(counted)); } notifyUpdated(); @@ -613,22 +617,30 @@ void Stickers::specialSetReceived( } if (writeRecent) { - Local::writeUserSettings(); + session().local().writeSettings(); } } switch (setId) { case CloudRecentSetId: { - if (Local::countRecentStickersHash() != hash) { - LOG(("API Error: received recent stickers hash %1 while counted hash is %2").arg(hash).arg(Local::countRecentStickersHash())); + const auto counted = Api::CountRecentStickersHash(&session()); + if (counted != hash) { + LOG(("API Error: " + "received recent stickers hash %1 while counted hash is %2" + ).arg(hash + ).arg(counted)); } - Local::writeRecentStickers(); + session().local().writeRecentStickers(); } break; case FavedSetId: { - if (Local::countFavedStickersHash() != hash) { - LOG(("API Error: received faved stickers hash %1 while counted hash is %2").arg(hash).arg(Local::countFavedStickersHash())); + const auto counted = Api::CountFavedStickersHash(&session()); + if (counted != hash) { + LOG(("API Error: " + "received faved stickers hash %1 while counted hash is %2" + ).arg(hash + ).arg(counted)); } - Local::writeFavedStickers(); + session().local().writeFavedStickers(); } break; default: Unexpected("setId in SpecialSetReceived()"); } @@ -735,8 +747,12 @@ void Stickers::featuredSetsReceived( } setFeaturedSetsUnreadCount(unreadCount); - if (Local::countFeaturedStickersHash() != hash) { - LOG(("API Error: received featured stickers hash %1 while counted hash is %2").arg(hash).arg(Local::countFeaturedStickersHash())); + const auto counted = Api::CountFeaturedStickersHash(&session()); + if (counted != hash) { + LOG(("API Error: " + "received featured stickers hash %1 while counted hash is %2" + ).arg(hash + ).arg(counted)); } if (!setsToRequest.empty()) { @@ -747,7 +763,7 @@ void Stickers::featuredSetsReceived( api.requestStickerSets(); } - Local::writeFeaturedStickers(); + session().local().writeFeaturedStickers(); notifyUpdated(); } @@ -767,11 +783,15 @@ void Stickers::gifsReceived(const QVector &items, int32 hash) { saved.push_back(document); } - if (Local::countSavedGifsHash() != hash) { - LOG(("API Error: received saved gifs hash %1 while counted hash is %2").arg(hash).arg(Local::countSavedGifsHash())); + const auto counted = Api::CountSavedGifsHash(&session()); + if (counted != hash) { + LOG(("API Error: " + "received saved gifs hash %1 while counted hash is %2" + ).arg(hash + ).arg(counted)); } - Local::writeSavedGifs(); + session().local().writeSavedGifs(); notifySavedGifsUpdated(); } @@ -1118,21 +1138,21 @@ StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) { } if (writeRecent) { - Local::writeUserSettings(); + session().local().writeSettings(); } if (set) { const auto isArchived = !!(set->flags & MTPDstickerSet::Flag::f_archived); if (set->flags & MTPDstickerSet::Flag::f_installed_date) { if (!isArchived) { - Local::writeInstalledStickers(); + session().local().writeInstalledStickers(); } } if (set->flags & MTPDstickerSet_ClientFlag::f_featured) { - Local::writeFeaturedStickers(); + session().local().writeFeaturedStickers(); } if (wasArchived != isArchived) { - Local::writeArchivedStickers(); + session().local().writeArchivedStickers(); } } @@ -1176,7 +1196,7 @@ QString Stickers::getSetTitle(const MTPDstickerSet &s) { return title; } -RecentStickerPack &Stickers::getRecentPack() { +RecentStickerPack &Stickers::getRecentPack() const { if (cRecentStickers().isEmpty() && !cRecentStickersPreload().isEmpty()) { const auto p = cRecentStickersPreload(); cSetRecentStickersPreload(RecentStickerPreload()); diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.h b/Telegram/SourceFiles/data/stickers/data_stickers.h index 78f9ba7022..9abdcf7ac4 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.h +++ b/Telegram/SourceFiles/data/stickers/data_stickers.h @@ -165,7 +165,7 @@ public: QString getSetTitle(const MTPDstickerSet &s); - RecentStickerPack &getRecentPack(); + RecentStickerPack &getRecentPack() const; private: bool updateNeeded(crl::time lastUpdate, crl::time now) const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index f48f07f379..1bb96971dc 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "mainwindow.h" #include "mainwidget.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "apiwrap.h" #include "window/themes/window_theme.h" #include "observer_peer.h" @@ -1902,7 +1902,7 @@ void InnerWidget::onHashtagFilterUpdate(QStringRef newFilter) { } _hashtagFilter = newFilter.toString(); if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) { - Local::readRecentHashtagsAndBots(); + session().local().readRecentHashtagsAndBots(); } auto &recent = cRecentSearchHashtags(); _hashtagResults.clear(); @@ -2665,11 +2665,11 @@ bool InnerWidget::chooseHashtag() { } } cSetRecentSearchHashtags(recent); - Local::writeRecentHashtagsAndBots(); + session().local().writeRecentHashtagsAndBots(); emit refreshHashtags(); selectByMouse(QCursor::pos()); } else { - Local::saveRecentSearchHashtags('#' + hashtag->tag); + session().local().saveRecentSearchHashtags('#' + hashtag->tag); emit completeHashtag(hashtag->tag); } return true; @@ -2723,7 +2723,7 @@ bool InnerWidget::chooseRow() { const auto chosen = computeChosenRow(); if (chosen.key) { if (IsServerMsgId(chosen.message.fullId.msg)) { - Local::saveRecentSearchHashtags(_filter); + session().local().saveRecentSearchHashtags(_filter); } _chosenRow.fire_copy(chosen); return true; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 87844beedf..7582c36686 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -32,7 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_slide_animation.h" #include "window/window_connecting_widget.h" #include "storage/storage_media_prepare.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "data/data_session.h" #include "data/data_channel.h" #include "data/data_chat.h" @@ -942,7 +942,7 @@ void Widget::searchMessages( _searchTimer.stop(); onSearchMessages(); - Local::saveRecentSearchHashtags(query); + session().local().saveRecentSearchHashtags(query); } } diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp index 6be6f96c56..4c98e7f6c5 100644 --- a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp @@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/padding_wrap.h" #include "boxes/confirm_box.h" #include "lang/lang_keys.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "core/file_utilities.h" #include "main/main_session.h" #include "data/data_session.h" @@ -51,7 +51,8 @@ void SuggestBox::prepare() { addButton(tr::lng_box_ok(), [=] { closeBox(); - _session->data().startExport(Local::ReadExportSettings().singlePeer); + _session->data().startExport( + _session->local().readExportSettings().singlePeer); }); addButton(tr::lng_export_suggest_cancel(), [=] { closeBox(); }); setCloseByOutsideClick(false); @@ -99,10 +100,10 @@ QPointer SuggestStart(not_null session) { void ClearSuggestStart(not_null session) { session->data().clearExportSuggestion(); - auto settings = Local::ReadExportSettings(); + auto settings = session->local().readExportSettings(); if (settings.availableAt) { settings.availableAt = 0; - Local::WriteExportSettings(settings); + session->local().writeExportSettings(settings); } } @@ -133,7 +134,8 @@ PanelController::PanelController( not_null process) : _session(session) , _process(process) -, _settings(std::make_unique(Local::ReadExportSettings())) +, _settings( + std::make_unique(_session->local().readExportSettings())) , _saveSettingsTimer([=] { saveSettings(); }) { ResolveSettings(session, *_settings); @@ -401,7 +403,7 @@ void PanelController::saveSettings() const { if (check(settings.path) == check(File::DefaultDownloadPath(_session))) { settings.path = QString(); } - Local::WriteExportSettings(settings); + _session->local().writeExportSettings(settings); } } // namespace View diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 2a4dbdf379..f03dbad5c9 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -68,7 +68,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "storage/localimageloader.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "storage/file_upload.h" #include "storage/storage_media_prepare.h" #include "media/audio/media_audio.h" @@ -1180,7 +1180,7 @@ void HistoryWidget::onDraftSaveDelayed() { if (!_field->textCursor().position() && !_field->textCursor().anchor() && !_field->scrollTop().current()) { - if (!Local::hasDraftCursors(_peer->id)) { + if (!session().local().hasDraftCursors(_peer->id)) { return; } } @@ -1231,24 +1231,40 @@ void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraf _saveDraftTimer.stop(); if (_saveDraftText) { if (save) { - Local::MessageDraft storedLocalDraft, storedEditDraft; + Storage::MessageDraft storedLocalDraft, storedEditDraft; if (localDraft) { if (*localDraft) { - storedLocalDraft = Local::MessageDraft((*localDraft)->msgId, (*localDraft)->textWithTags, (*localDraft)->previewCancelled); + storedLocalDraft = Storage::MessageDraft{ + (*localDraft)->msgId, + (*localDraft)->textWithTags, + (*localDraft)->previewCancelled + }; } } else { - storedLocalDraft = Local::MessageDraft(_replyToId, _field->getTextWithTags(), _previewCancelled); + storedLocalDraft = Storage::MessageDraft{ + _replyToId, + _field->getTextWithTags(), + _previewCancelled + }; } if (editDraft) { if (*editDraft) { - storedEditDraft = Local::MessageDraft((*editDraft)->msgId, (*editDraft)->textWithTags, (*editDraft)->previewCancelled); + storedEditDraft = Storage::MessageDraft{ + (*editDraft)->msgId, + (*editDraft)->textWithTags, + (*editDraft)->previewCancelled + }; } } else if (_editMsgId) { - storedEditDraft = Local::MessageDraft(_editMsgId, _field->getTextWithTags(), _previewCancelled); + storedEditDraft = Storage::MessageDraft{ + _editMsgId, + _field->getTextWithTags(), + _previewCancelled + }; } - Local::writeDrafts(_peer->id, storedLocalDraft, storedEditDraft); + session().local().writeDrafts(_peer->id, storedLocalDraft, storedEditDraft); if (_migrated) { - Local::writeDrafts(_migrated->peer->id, Local::MessageDraft(), Local::MessageDraft()); + session().local().writeDrafts(_migrated->peer->id, {}, {}); } } _saveDraftText = false; @@ -1269,9 +1285,9 @@ void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraf } else if (_editMsgId) { editCursor = MessageCursor(_field); } - Local::writeDraftCursors(_peer->id, localCursor, editCursor); + session().local().writeDraftCursors(_peer->id, localCursor, editCursor); if (_migrated) { - Local::writeDraftCursors(_migrated->peer->id, MessageCursor(), MessageCursor()); + session().local().writeDraftCursors(_migrated->peer->id, {}, {}); } } @@ -1869,9 +1885,9 @@ void HistoryWidget::showHistory( handlePeerUpdate(); - Local::readDraftsWithCursors(_history); + session().local().readDraftsWithCursors(_history); if (_migrated) { - Local::readDraftsWithCursors(_migrated); + session().local().readDraftsWithCursors(_migrated); _migrated->clearEditDraft(); _history->takeLocalDraft(_migrated); } @@ -4283,10 +4299,10 @@ void HistoryWidget::onCheckFieldAutocomplete() { if (autocomplete.query[0] == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) { - Local::readRecentHashtagsAndBots(); + session().local().readRecentHashtagsAndBots(); } else if (autocomplete.query[0] == '@' && cRecentInlineBots().isEmpty()) { - Local::readRecentHashtagsAndBots(); + session().local().readRecentHashtagsAndBots(); } else if (autocomplete.query[0] == '/' && _peer->isUser() && !_peer->asUser()->isBot()) { @@ -5764,7 +5780,7 @@ void HistoryWidget::sendInlineResult( bots.resize(RecentInlineBotsLimit - 1); } bots.push_front(bot); - Local::writeRecentHashtagsAndBots(); + session().local().writeRecentHashtagsAndBots(); } hideSelectorControlsAnimated(); @@ -5823,7 +5839,7 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() { pinnedId = 0; } else { Global::RefHiddenPinnedMessages().remove(_peer->id); - Local::writeUserSettings(); + session().local().writeSettings(); } } } @@ -6151,7 +6167,7 @@ void HistoryWidget::hidePinnedMessage() { pinnedId)); } else { Global::RefHiddenPinnedMessages().insert(_peer->id, pinnedId); - Local::writeUserSettings(); + session().local().writeSettings(); if (pinnedMsgVisibilityUpdated()) { updateControlsGeometry(); update(); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 04b25058f7..55d6435fbe 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -32,9 +32,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_utilities.h" #include "main/main_session.h" #include "data/data_session.h" +#include "data/data_user.h" #include "data/data_scheduled_messages.h" #include "storage/storage_media_prepare.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "inline_bots/inline_bot_result.h" #include "platform/platform_specific.h" #include "lang/lang_keys.h" @@ -605,7 +606,7 @@ void ScheduledWidget::sendInlineResult( bots.resize(RecentInlineBotsLimit - 1); } bots.push_front(bot); - Local::writeRecentHashtagsAndBots(); + bot->session().local().writeRecentHashtagsAndBots(); } _composeControls->hidePanelsAnimated(); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 67d41ba8eb..f485b0285c 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_instance.h" #include "history/history_location_manager.h" #include "history/view/history_view_cursor_state.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "ui/image/image.h" #include "main/main_session.h" #include "apiwrap.h" @@ -134,7 +134,7 @@ void DeleteSavedGifClickHandler::onClickImpl() const { const auto index = _data->owner().stickers().savedGifs().indexOf(_data); if (index >= 0) { _data->owner().stickers().savedGifsRef().remove(index); - Local::writeSavedGifs(); + _data->session().local().writeSavedGifs(); } _data->owner().stickers().notifySavedGifsUpdated(); } diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp index 4cf917ce6c..fa07001b0c 100644 --- a/Telegram/SourceFiles/intro/intro_step.cpp +++ b/Telegram/SourceFiles/intro/intro_step.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "intro/intro_widget.h" #include "storage/localstorage.h" +#include "storage/storage_account.h" #include "lang/lang_keys.h" #include "lang/lang_cloud_manager.h" #include "main/main_account.h" @@ -42,7 +43,7 @@ void PrepareSupportMode(not_null session) { Global::SetSoundNotify(false); Global::SetFlashBounceNotify(false); session->settings().autoDownload() = Full::FullDisabled(); - Local::writeUserSettings(); + session->local().writeSettings(); } } // namespace @@ -141,7 +142,9 @@ void Step::finish(const MTPUser &user, QImage &&photo) { const auto account = _account; const auto weak = base::make_weak(account.get()); account->createSession(user); - Local::writeMtpData(); + if (weak) { + account->local().writeMtpData(); + } App::wnd()->controller().setupMain(); // "this" is already deleted here by creating the main widget. diff --git a/Telegram/SourceFiles/main/main_account.cpp b/Telegram/SourceFiles/main/main_account.cpp index 73ad80d971..6d4ad940da 100644 --- a/Telegram/SourceFiles/main/main_account.cpp +++ b/Telegram/SourceFiles/main/main_account.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "core/launcher.h" #include "core/shortcuts.h" +#include "storage/storage_account.h" #include "storage/serialize_common.h" #include "storage/localstorage.h" #include "data/data_session.h" @@ -25,10 +26,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Main { -Account::Account(const QString &dataName) { +Account::Account(const QString &dataName) +: _local(std::make_unique(this, dataName)) +, _appConfig(std::make_unique(this)) { watchProxyChanges(); watchSessionChanges(); - _appConfig = std::make_unique(this); } Account::~Account() = default; @@ -128,7 +130,7 @@ void Account::createSession( if (!serialized.isEmpty()) { // For now it depends on Auth() which depends on _sessionValue. - Local::readSelf(serialized, streamVersion); + local().readSelf(serialized, streamVersion); } } @@ -337,6 +339,10 @@ void Account::startMtp() { MTP::Instance::Mode::Normal, std::move(config)); _mtp->setUserPhone(cLoggedPhoneNumber()); + _mtp->writeKeysRequests( + ) | rpl::start_with_next([=] { + local().writeMtpData(); + }, _mtp->lifetime()); _mtpConfig.mainDcId = _mtp->mainDcId(); _mtp->setUpdatesHandler(::rpcDone([=]( @@ -376,7 +382,7 @@ void Account::startMtp() { _storedSettings = nullptr; if (sessionExists()) { - // Skip all pending self updates so that we won't Local::writeSelf. + // Skip all pending self updates so that we won't local().writeSelf. Notify::peerUpdatedSendDelayed(); } @@ -439,6 +445,7 @@ void Account::loggedOut() { session().data().clearLocalStorage(); } destroySession(); + local().reset(); Local::reset(); cSetOtherOnline(0); @@ -450,7 +457,7 @@ void Account::destroyMtpKeys(MTP::AuthKeysList &&keys) { } if (_mtpForKeysDestroy) { _mtpForKeysDestroy->addKeysForDestroy(std::move(keys)); - Local::writeMtpData(); + local().writeMtpData(); return; } auto destroyConfig = MTP::Instance::Config(); @@ -462,14 +469,18 @@ void Account::destroyMtpKeys(MTP::AuthKeysList &&keys) { Core::App().dcOptions(), MTP::Instance::Mode::KeysDestroyer, std::move(destroyConfig)); + _mtpForKeysDestroy->writeKeysRequests( + ) | rpl::start_with_next([=] { + local().writeMtpData(); + }, _mtpForKeysDestroy->lifetime()); _mtpForKeysDestroy->allKeysDestroyed( ) | rpl::start_with_next([=] { LOG(("MTP Info: all keys scheduled for destroy are destroyed.")); crl::on_main(this, [=] { _mtpForKeysDestroy = nullptr; - Local::writeMtpData(); + local().writeMtpData(); }); - }, _lifetime); + }, _mtpForKeysDestroy->lifetime()); } void Account::suggestMainDcId(MTP::DcId mainDcId) { @@ -508,7 +519,7 @@ void Account::resetAuthorizationKeys() { _mtpValue = nullptr; _mtp = nullptr; startMtp(); - Local::writeMtpData(); + local().writeMtpData(); } void Account::clearMtp() { diff --git a/Telegram/SourceFiles/main/main_account.h b/Telegram/SourceFiles/main/main_account.h index d4f9b58e80..ef37669d1f 100644 --- a/Telegram/SourceFiles/main/main_account.h +++ b/Telegram/SourceFiles/main/main_account.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtp_instance.h" #include "base/weak_ptr.h" +namespace Storage { +class Account; +} // namespace Storage + namespace Main { class Session; @@ -36,9 +40,12 @@ public: void logOut(); void forcedLogOut(); - [[nodiscard]] AppConfig &appConfig() { + [[nodiscard]] AppConfig &appConfig() const { return *_appConfig; } + [[nodiscard]] Storage::Account &local() const { + return *_local; + } [[nodiscard]] bool sessionExists() const; [[nodiscard]] Session &session(); @@ -91,6 +98,8 @@ private: void loggedOut(); + const std::unique_ptr _local; + std::unique_ptr _mtp; rpl::variable _mtpValue; std::unique_ptr _mtpForKeysDestroy; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 23a3798f09..5de2b51c77 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -16,8 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/file_download.h" #include "storage/download_manager_mtproto.h" #include "storage/file_upload.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "storage/storage_facade.h" +#include "storage/storage_account.h" #include "data/data_session.h" #include "data/data_user.h" #include "window/notifications_manager.h" @@ -45,7 +46,7 @@ Session::Session( Settings &&settings) : _account(account) , _settings(std::move(settings)) -, _saveSettingsTimer([=] { Local::writeUserSettings(); }) +, _saveSettingsTimer([=] { local().writeSettings(); }) , _api(std::make_unique(this)) , _calls(std::make_unique(this)) , _downloader(std::make_unique(_api.get())) @@ -82,7 +83,7 @@ Session::Session( events, [=](const Notify::PeerUpdate &update) { if (update.peer == _user) { - Local::writeSelf(); + local().writeSelf(); } })); @@ -112,6 +113,10 @@ Main::Account &Session::account() const { return *_account; } +Storage::Account &Session::local() const { + return _account->local(); +} + bool Session::Exists() { return Core::IsAppLaunched() && Core::App().activeAccount().sessionExists(); @@ -174,7 +179,7 @@ Support::Templates& Session::supportTemplates() const { void Session::saveSettingsNowIfNeeded() { if (_saveSettingsTimer.isActive()) { _saveSettingsTimer.cancel(); - Local::writeUserSettings(); + local().writeSettings(); } } diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 7d68d27c28..30f09b36c4 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -32,6 +32,7 @@ namespace Storage { class DownloadManagerMtproto; class Uploader; class Facade; +class Account; } // namespace Storage namespace Window { @@ -73,6 +74,7 @@ public: [[nodiscard]] static bool Exists(); [[nodiscard]] Main::Account &account() const; + [[nodiscard]] Storage::Account &local() const; [[nodiscard]] UserId userId() const; [[nodiscard]] PeerId userPeerId() const; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 054adc3eb3..c41bb73831 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -68,7 +68,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "boxes/download_path_box.h" #include "boxes/connection_box.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "media/audio/media_audio.h" #include "media/player/media_player_panel.h" #include "media/player/media_player_widget.h" @@ -1089,7 +1089,9 @@ void MainWidget::closeBothPlayers() { void MainWidget::createPlayer() { if (!_player) { - _player.create(this, object_ptr(this)); + _player.create( + this, + object_ptr(this, &session())); rpl::merge( _player->heightValue() | rpl::map([] { return true; }), _player->shownValue() @@ -2507,7 +2509,7 @@ void MainWidget::ensureFirstColumnResizeAreaCreated() { session().settings().setDialogsWidthRatio( float64(_dialogsWidth) / width()); } - Local::writeUserSettings(); + session().local().writeSettings(); }; createResizeArea( _firstColumnResizeArea, @@ -2531,7 +2533,7 @@ void MainWidget::ensureThirdColumnResizeAreaCreated() { session().settings().thirdColumnWidth(), st::columnMinimalWidthThird, st::columnMaximalWidthThird)); - Local::writeUserSettings(); + session().local().writeSettings(); }; createResizeArea( _thirdColumnResizeArea, @@ -3193,9 +3195,10 @@ void MainWidget::sendPing() { } void MainWidget::start() { - session().api().requestNotifySettings(MTP_inputNotifyUsers()); - session().api().requestNotifySettings(MTP_inputNotifyChats()); - session().api().requestNotifySettings(MTP_inputNotifyBroadcasts()); + auto &api = session().api(); + api.requestNotifySettings(MTP_inputNotifyUsers()); + api.requestNotifySettings(MTP_inputNotifyChats()); + api.requestNotifySettings(MTP_inputNotifyBroadcasts()); cSetOtherOnline(0); session().user()->loadUserpic(); @@ -3204,16 +3207,19 @@ void MainWidget::start() { update(); _started = true; - Local::readInstalledStickers(); - Local::readFeaturedStickers(); - Local::readRecentStickers(); - Local::readFavedStickers(); - Local::readSavedGifs(); - if (const auto availableAt = Local::ReadExportSettings().availableAt) { - session().data().suggestStartExport(availableAt); + auto &local = session().local(); + local.readInstalledStickers(); + local.readFeaturedStickers(); + local.readRecentStickers(); + local.readFavedStickers(); + local.readSavedGifs(); + auto &data = session().data(); + if (const auto availableAt = local.readExportSettings().availableAt) { + data.suggestStartExport(availableAt); } - session().data().stickers().notifyUpdated(); - session().data().stickers().notifySavedGifsUpdated(); + auto &stickers = data.stickers(); + stickers.notifyUpdated(); + stickers.notifySavedGifsUpdated(); _history->start(); @@ -3441,7 +3447,7 @@ void MainWidget::incrementSticker(DocumentData *sticker) { } if (writeOldRecent) { - Local::writeUserSettings(); + session().local().writeSettings(); } // Remove that sticker from custom stickers, now it is in cloud recent stickers. @@ -3460,10 +3466,10 @@ void MainWidget::incrementSticker(DocumentData *sticker) { } if (writeInstalledStickers) { - Local::writeInstalledStickers(); + session().local().writeInstalledStickers(); } if (writeRecentStickers) { - Local::writeRecentStickers(); + session().local().writeRecentStickers(); } _controller->tabbedSelector()->refreshStickers(); } @@ -3623,30 +3629,32 @@ void MainWidget::applyCloudDraft(History *history) { } void MainWidget::writeDrafts(History *history) { - Local::MessageDraft storedLocalDraft, storedEditDraft; + Storage::MessageDraft storedLocalDraft, storedEditDraft; MessageCursor localCursor, editCursor; if (const auto localDraft = history->localDraft()) { if (session().supportMode() || !Data::draftsAreEqual(localDraft, history->cloudDraft())) { - storedLocalDraft = Local::MessageDraft( + storedLocalDraft = Storage::MessageDraft{ localDraft->msgId, localDraft->textWithTags, - localDraft->previewCancelled); + localDraft->previewCancelled + }; localCursor = localDraft->cursor; } } if (const auto editDraft = history->editDraft()) { - storedEditDraft = Local::MessageDraft( + storedEditDraft = Storage::MessageDraft{ editDraft->msgId, editDraft->textWithTags, - editDraft->previewCancelled); + editDraft->previewCancelled + }; editCursor = editDraft->cursor; } - Local::writeDrafts( + session().local().writeDrafts( history->peer->id, storedLocalDraft, storedEditDraft); - Local::writeDraftCursors(history->peer->id, localCursor, editCursor); + session().local().writeDraftCursors(history->peer->id, localCursor, editCursor); } void MainWidget::checkIdleFinish() { @@ -4543,7 +4551,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { session().api().updateStickers(); } else { session().data().stickers().setsOrderRef() = std::move(result); - Local::writeInstalledStickers(); + session().local().writeInstalledStickers(); session().data().stickers().notifyUpdated(); } } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index ae582e0ad2..ac55e20f73 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/add_contact_box.h" #include "boxes/connection_box.h" #include "observer_peer.h" +#include "storage/storage_account.h" #include "storage/localstorage.h" #include "apiwrap.h" #include "settings/settings_intro.h" @@ -791,7 +792,7 @@ void MainWindow::toggleDisplayNotifyFromTray() { Global::SetRestoreFlashBounceNotifyFromTray(false); } } - Local::writeUserSettings(); + account().local().writeSettings(); using Change = Window::Notifications::ChangeType; auto &changes = account().session().notifications().settingsChanged(); changes.notify(Change::DesktopEnabled); @@ -850,13 +851,6 @@ MainWindow::TempDirState MainWindow::tempDirState() { return QDir(cTempDir()).exists() ? TempDirExists : TempDirEmpty; } -MainWindow::TempDirState MainWindow::localStorageState() { - if (_clearManager && _clearManager->hasTask(Local::ClearManagerStorage)) { - return TempDirRemoving; - } - return TempDirEmpty; -} - void MainWindow::tempDirDelete(int task) { if (_clearManager) { if (_clearManager->addTask(task)) { diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index dc85489340..2f52c21071 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -79,7 +79,6 @@ public: TempDirEmpty, }; TempDirState tempDirState(); - TempDirState localStorageState(); void tempDirDelete(int task); void sendPaths(); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 2121b33af3..68a8baf4c1 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_media_player.h" #include "styles/style_media_view.h" #include "history/history_item.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "layout.h" #include "main/main_session.h" #include "facades.h" @@ -80,7 +80,9 @@ QPoint Widget::PlayButton::prepareRippleStartPosition() const { return QPoint(mapFromGlobal(QCursor::pos()) - st::mediaPlayerButton.rippleAreaPosition); } -Widget::Widget(QWidget *parent) : RpWidget(parent) +Widget::Widget(QWidget *parent, not_null session) +: RpWidget(parent) +, _session(session) , _nameLabel(this, st::mediaPlayerName) , _timeLabel(this, st::mediaPlayerTime) , _playPause(this) @@ -141,7 +143,7 @@ Widget::Widget(QWidget *parent) : RpWidget(parent) Global::SetVoiceMsgPlaybackDoubled(doubled); instance()->updateVoicePlaybackSpeed(); updatePlaybackSpeedIcon(); - Local::writeUserSettings(); + _session->local().writeSettings(); }); subscribe(instance()->repeatChangedNotifier(), [this](AudioMsgId::Type type) { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 1b01a518cf..7e5dc8b8d1 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -26,6 +26,10 @@ class PlaybackProgress; } // namespace Clip } // namespace Media +namespace Main { +class Session; +} // namespace Main + namespace Media { namespace Player { @@ -35,7 +39,7 @@ struct TrackState; class Widget : public Ui::RpWidget, private base::Subscriber { public: - Widget(QWidget *parent); + Widget(QWidget *parent, not_null session); void setCloseCallback(Fn callback); void stopAndClose(); @@ -85,6 +89,8 @@ private: void updateTimeText(const TrackState &state); void updateTimeLabel(); + const not_null _session; + crl::time _seekPositionMs = -1; crl::time _lastDurationMs = 0; QString _time; diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 411d70c45e..e2211c6403 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -64,6 +64,8 @@ public: void setMainDcId(DcId mainDcId); [[nodiscard]] DcId mainDcId() const; + [[nodiscard]] rpl::producer<> writeKeysRequests() const; + void dcPersistentKeyChanged(DcId dcId, const AuthKeyPtr &persistentKey); void dcTemporaryKeyChanged(DcId dcId); [[nodiscard]] rpl::producer dcTemporaryKeyChanged() const; @@ -159,6 +161,8 @@ public: void prepareToDestroy(); + [[nodiscard]] rpl::lifetime &lifetime(); + private: bool hasAuthorization(); void importDone(const MTPauth_Authorization &result, mtpRequestId requestId); @@ -224,6 +228,7 @@ private: base::flat_map _keysForWrite; base::flat_map _logoutGuestRequestIds; + rpl::event_stream<> _writeKeysRequests; rpl::event_stream<> _allKeysDestroyed; // holds dcWithShift for request to this dc or -dc for request to main dc @@ -253,6 +258,8 @@ private: base::Timer _checkDelayedTimer; + rpl::lifetime _lifetime; + }; Instance::Private::Private( @@ -404,7 +411,7 @@ void Instance::Private::setMainDcId(DcId mainDcId) { if (oldMainDcId != _mainDcId) { killSession(oldMainDcId); } - Local::writeMtpData(); + _writeKeysRequests.fire({}); } DcId Instance::Private::mainDcId() const { @@ -724,8 +731,9 @@ void Instance::Private::dcPersistentKeyChanged( } else { _keysForWrite.emplace(dcId, persistentKey); } - DEBUG_LOG(("AuthKey Info: writing auth keys, called by dc %1").arg(dcId)); - Local::writeMtpData(); + DEBUG_LOG(("AuthKey Info: writing auth keys, called by dc %1" + ).arg(dcId)); + _writeKeysRequests.fire({}); } void Instance::Private::dcTemporaryKeyChanged(DcId dcId) { @@ -769,6 +777,10 @@ rpl::producer<> Instance::Private::allKeysDestroyed() const { return _allKeysDestroyed.events(); } +rpl::producer<> Instance::Private::writeKeysRequests() const { + return _writeKeysRequests.events(); +} + not_null Instance::Private::dcOptions() { return _dcOptions; } @@ -1419,6 +1431,10 @@ not_null Instance::Private::getSession( return startSession(shiftedDcId); } +rpl::lifetime &Instance::Private::lifetime() { + return _lifetime; +} + Session *Instance::Private::findSession(ShiftedDcId shiftedDcId) { const auto i = _sessions.find(shiftedDcId); return (i != _sessions.end()) ? i->second.get() : nullptr; @@ -1666,6 +1682,10 @@ QString Instance::langPackName() const { return Lang::Current().langPackName(); } +rpl::producer<> Instance::writeKeysRequests() const { + return _private->writeKeysRequests(); +} + rpl::producer<> Instance::allKeysDestroyed() const { return _private->allKeysDestroyed(); } @@ -1862,6 +1882,10 @@ void Instance::sendAnything(ShiftedDcId shiftedDcId, crl::time msCanWait) { _private->getSession(shiftedDcId)->sendAnything(msCanWait); } +rpl::lifetime &Instance::lifetime() { + return _private->lifetime(); +} + Instance::~Instance() { _private->prepareToDestroy(); } diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.h b/Telegram/SourceFiles/mtproto/mtp_instance.h index ed37973073..868a0b1ab5 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.h +++ b/Telegram/SourceFiles/mtproto/mtp_instance.h @@ -60,6 +60,7 @@ public: [[nodiscard]] QString cloudLangCode() const; [[nodiscard]] QString langPackName() const; + [[nodiscard]] rpl::producer<> writeKeysRequests() const; [[nodiscard]] rpl::producer<> allKeysDestroyed() const; // Thread-safe. @@ -195,6 +196,8 @@ public: afterRequestId); } + [[nodiscard]] rpl::lifetime &lifetime(); + signals: void proxyDomainResolved( QString host, diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index 65ddac56fb..01dcc27c39 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -207,7 +207,7 @@ bool GetNative( QString path = dialog.directory().absolutePath(); if (path != cDialogLastPath()) { cSetDialogLastPath(path); - Local::writeUserSettings(); + Local::writeSettings(); } if (res == QDialog::Accepted) { diff --git a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp index 25113c27f4..fe1dde5306 100644 --- a/Telegram/SourceFiles/platform/win/file_utilities_win.cpp +++ b/Telegram/SourceFiles/platform/win/file_utilities_win.cpp @@ -417,7 +417,7 @@ bool Get( const auto path = dialog.directory().absolutePath(); if (path != cDialogLastPath()) { cSetDialogLastPath(path); - Local::writeUserSettings(); + Local::writeSettings(); } } diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp index 56acbb9938..a05bc8266b 100644 --- a/Telegram/SourceFiles/settings/settings_calls.cpp +++ b/Telegram/SourceFiles/settings/settings_calls.cpp @@ -17,8 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/single_choice_box.h" #include "boxes/confirm_box.h" #include "platform/platform_specific.h" +#include "main/main_session.h" #include "lang/lang_keys.h" -#include "storage/localstorage.h" #include "layout.h" #include "styles/style_settings.h" #include "ui/widgets/continuous_sliders.h" @@ -43,13 +43,14 @@ namespace Settings { Calls::Calls( QWidget *parent, not_null controller) -: Section(parent) { - setupContent(controller); +: Section(parent) +, _controller(controller) { + setupContent(); } Calls::~Calls() { if (_needWriteSettings) { - Local::writeUserSettings(); + _controller->session().saveSettingsDelayed(); } } @@ -60,7 +61,7 @@ void Calls::sectionSaveChanges(FnMut done) { done(); } -void Calls::setupContent(not_null controller) { +void Calls::setupContent() { using VoIP = tgvoip::VoIPController; const auto content = Ui::CreateChild(this); @@ -129,8 +130,8 @@ void Calls::setupContent(not_null controller) { ? devices[option - 1].id : "default"; Global::SetCallOutputDeviceID(QString::fromStdString(deviceId)); - Local::writeUserSettings(); - if (const auto call = controller->session().calls().currentCall()) { + _controller->session().saveSettingsDelayed(); + if (const auto call = _controller->session().calls().currentCall()) { call->setCurrentAudioDevice(false, deviceId); } }); @@ -160,7 +161,7 @@ void Calls::setupContent(not_null controller) { _needWriteSettings = true; updateOutputLabel(value); Global::SetCallOutputVolume(value); - if (const auto call = controller->session().calls().currentCall()) { + if (const auto call = _controller->session().calls().currentCall()) { call->setAudioVolume(false, value / 100.0f); } }; @@ -204,11 +205,11 @@ void Calls::setupContent(not_null controller) { ? devices[option - 1].id : "default"; Global::SetCallInputDeviceID(QString::fromStdString(deviceId)); - Local::writeUserSettings(); + _controller->session().saveSettingsDelayed(); if (_micTester) { stopTestingMicrophone(); } - if (const auto call = controller->session().calls().currentCall()) { + if (const auto call = _controller->session().calls().currentCall()) { call->setCurrentAudioDevice(true, deviceId); } }); @@ -238,7 +239,7 @@ void Calls::setupContent(not_null controller) { _needWriteSettings = true; updateInputLabel(value); Global::SetCallInputVolume(value); - if (const auto call = controller->session().calls().currentCall()) { + if (const auto call = _controller->session().calls().currentCall()) { call->setAudioVolume(true, value / 100.0f); } }; @@ -292,8 +293,8 @@ void Calls::setupContent(not_null controller) { return (enabled != Global::CallAudioDuckingEnabled()); }) | rpl::start_with_next([=](bool enabled) { Global::SetCallAudioDuckingEnabled(enabled); - Local::writeUserSettings(); - if (const auto call = controller->session().calls().currentCall()) { + _controller->session().saveSettingsDelayed(); + if (const auto call = _controller->session().calls().currentCall()) { call->setAudioDuckingEnabled(enabled); } }, content->lifetime()); diff --git a/Telegram/SourceFiles/settings/settings_calls.h b/Telegram/SourceFiles/settings/settings_calls.h index 9fc0984007..15edb1e5a7 100644 --- a/Telegram/SourceFiles/settings/settings_calls.h +++ b/Telegram/SourceFiles/settings/settings_calls.h @@ -32,11 +32,12 @@ public: void sectionSaveChanges(FnMut done) override; private: - void setupContent(not_null controller); + void setupContent(); void requestPermissionAndStartTestingMicrophone(); void startTestingMicrophone(); void stopTestingMicrophone(); + const not_null _controller; rpl::event_stream _outputNameStream; rpl::event_stream _inputNameStream; rpl::event_stream _micTestTextStream; diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 33abb39480..e835390553 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -791,7 +791,7 @@ void SetupMessages( if (App::main()) { App::main()->ctrlEnterSubmitUpdated(); } - Local::writeUserSettings(); + controller->session().saveSettingsDelayed(); }); AddSkip(inner, st::settingsCheckboxesSkip); @@ -863,8 +863,8 @@ void SetupDataStorage( std::move(pathtext), st::settingsButton, tr::lng_download_path()); - path->entity()->addClickHandler([] { - Ui::show(Box()); + path->entity()->addClickHandler([=] { + Ui::show(Box(controller)); }); path->toggleOn(ask->toggledValue() | rpl::map(!_1)); #endif // OS_WIN_STORE @@ -874,7 +874,7 @@ void SetupDataStorage( return (checked != Global::AskDownloadPath()); }) | rpl::start_with_next([=](bool checked) { Global::SetAskDownloadPath(checked); - Local::writeUserSettings(); + controller->session().saveSettingsDelayed(); #ifndef OS_WIN_STORE showpath->fire_copy(!checked); @@ -979,10 +979,10 @@ void SetupChatBackground( })); adaptive->entity()->checkedChanges( - ) | rpl::start_with_next([](bool checked) { + ) | rpl::start_with_next([=](bool checked) { Global::SetAdaptiveForWide(checked); Adaptive::Changed().notify(); - Local::writeUserSettings(); + controller->session().saveSettingsDelayed(); }, adaptive->lifetime()); } @@ -1285,7 +1285,7 @@ void SetupSupportSwitchSettings( add(SwitchType::Previous, "Send and switch to previous"); group->setChangedCallback([=](SwitchType value) { controller->session().settings().setSupportSwitch(value); - Local::writeUserSettings(); + controller->session().saveSettingsDelayed(); }); } @@ -1325,7 +1325,7 @@ void SetupSupportChatsLimitSlice( group->setChangedCallback([=](int days) { controller->session().settings().setSupportChatsTimeSlice( days * kDayDuration); - Local::writeUserSettings(); + controller->session().saveSettingsDelayed(); }); } @@ -1362,7 +1362,7 @@ void SetupSupport( ) | rpl::start_with_next([=](bool checked) { controller->session().settings().setSupportTemplatesAutocomplete( checked); - Local::writeUserSettings(); + controller->session().saveSettingsDelayed(); }, inner->lifetime()); AddSkip(inner, st::settingsCheckboxesSkip); diff --git a/Telegram/SourceFiles/settings/settings_codes.cpp b/Telegram/SourceFiles/settings/settings_codes.cpp index c4f20a7b1e..e00b98c363 100644 --- a/Telegram/SourceFiles/settings/settings_codes.cpp +++ b/Telegram/SourceFiles/settings/settings_codes.cpp @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "data/data_session.h" -#include "storage/localstorage.h" +#include "main/main_session.h" #include "boxes/confirm_box.h" #include "lang/lang_cloud_manager.h" #include "lang/lang_instance.h" @@ -76,10 +76,13 @@ auto GenerateCodes() { Unexpected("Crashed in Settings!"); }); codes.emplace(qsl("moderate"), [](SessionController *window) { + if (!window) { + return; + } auto text = Global::ModerateModeEnabled() ? qsl("Disable moderate mode?") : qsl("Enable moderate mode?"); - Ui::show(Box(text, [] { + Ui::show(Box(text, [=] { Global::SetModerateModeEnabled(!Global::ModerateModeEnabled()); - Local::writeUserSettings(); + window->session().saveSettingsDelayed(); Ui::hideLayer(); })); }); @@ -96,10 +99,13 @@ auto GenerateCodes() { }); }); codes.emplace(qsl("videoplayer"), [](SessionController *window) { + if (!window) { + return; + } auto text = cUseExternalVideoPlayer() ? qsl("Use internal video player?") : qsl("Use external video player?"); - Ui::show(Box(text, [] { + Ui::show(Box(text, [=] { cSetUseExternalVideoPlayer(!cUseExternalVideoPlayer()); - Local::writeUserSettings(); + window->session().saveSettingsDelayed(); Ui::hideLayer(); })); }); @@ -168,7 +174,7 @@ auto GenerateCodes() { window->session().settings().setSoundOverride( key, result.paths.front()); - Local::writeUserSettings(); + window->session().saveSettingsDelayed(); } } })); @@ -177,7 +183,7 @@ auto GenerateCodes() { codes.emplace(qsl("sounds_reset"), [](SessionController *window) { if (window) { window->session().settings().clearSoundOverrides(); - Local::writeUserSettings(); + window->session().saveSettingsDelayed(); Ui::show(Box("All sound overrides were reset.")); } }); diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 5f30627163..71ec0eabde 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/discrete_sliders.h" #include "lang/lang_keys.h" -#include "storage/localstorage.h" #include "window/notifications_manager.h" #include "window/window_session_controller.h" #include "platform/platform_notifications_manager.h" @@ -187,7 +186,7 @@ void NotificationsCount::setCount(int count) { Global::SetNotificationsCount(count); _controller->session().notifications().settingsChanged().notify( ChangeType::MaxCount); - Local::writeUserSettings(); + _controller->session().saveSettingsDelayed(); } } @@ -414,7 +413,7 @@ void NotificationsCount::mouseReleaseEvent(QMouseEvent *e) { Global::SetNotificationsCorner(_chosenCorner); _controller->session().notifications().settingsChanged().notify( ChangeType::Corner); - Local::writeUserSettings(); + _controller->session().saveSettingsDelayed(); } } } @@ -679,7 +678,7 @@ void SetupNotificationsContent( using Change = Window::Notifications::ChangeType; const auto changed = [=](Change change) { - Local::writeUserSettings(); + session->saveSettingsDelayed(); session->notifications().settingsChanged().notify(change); }; desktop->checkedChanges( @@ -776,8 +775,7 @@ void SetupNotificationsContent( return (checked != Global::NativeNotifications()); }) | rpl::start_with_next([=](bool checked) { Global::SetNativeNotifications(checked); - Local::writeUserSettings(); - + session->saveSettingsDelayed(); session->notifications().createManager(); if (advancedSlide) { diff --git a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp index affba64ce8..a1f42e9759 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_controllers.cpp @@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "observer_peer.h" #include "mainwidget.h" #include "main/main_session.h" -#include "storage/localstorage.h" #include "data/data_user.h" #include "data/data_session.h" #include "history/admin_log/history_admin_log_item.h" @@ -515,7 +514,7 @@ void LastSeenPrivacyController::confirmSave( } saveCallback(); session->settings().setLastSeenWarningSeen(true); - Local::writeUserSettings(); + session->saveSettingsDelayed(); }; auto box = Box( tr::lng_edit_privacy_lastseen_warning(tr::now), diff --git a/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp b/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp new file mode 100644 index 0000000000..a3e3bcc7c3 --- /dev/null +++ b/Telegram/SourceFiles/storage/details/storage_file_utilities.cpp @@ -0,0 +1,477 @@ +/* +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/details/storage_file_utilities.h" + +#include "mtproto/mtproto_auth_key.h" +#include "base/platform/base_platform_file_utilities.h" +#include "base/openssl_help.h" + +#include +#include + +namespace Storage { +namespace details { +namespace { + +constexpr char TdfMagic[] = { 'T', 'D', 'F', '$' }; +constexpr auto TdfMagicLen = int(sizeof(TdfMagic)); + +} // namespace + +QString ToFilePart(FileKey val) { + QString result; + result.reserve(0x10); + for (int32 i = 0; i < 0x10; ++i) { + uchar v = (val & 0x0F); + result.push_back((v < 0x0A) ? ('0' + v) : ('A' + (v - 0x0A))); + val >>= 4; + } + return result; +} + +bool KeyAlreadyUsed(QString &name) { + name += '0'; + if (QFileInfo(name).exists()) { + return true; + } + name[name.size() - 1] = '1'; + if (QFileInfo(name).exists()) { + return true; + } + name[name.size() - 1] = 's'; + if (QFileInfo(name).exists()) { + return true; + } + return false; +} + +FileKey GenerateKey(const QString &basePath) { + FileKey result; + QString path; + path.reserve(basePath.size() + 0x11); + path += basePath; + do { + result = rand_value(); + path.resize(basePath.size()); + path += ToFilePart(result); + } while (!result || KeyAlreadyUsed(path)); + + return result; +} + +void ClearKey(const FileKey &key, const QString &basePath) { + QString name; + name.reserve(basePath.size() + 0x11); + name.append(basePath).append(ToFilePart(key)).append('0'); + QFile::remove(name); + name[name.size() - 1] = '1'; + QFile::remove(name); + name[name.size() - 1] = 's'; + QFile::remove(name); +} + +bool CheckStreamStatus(QDataStream &stream) { + if (stream.status() != QDataStream::Ok) { + LOG(("Bad data stream status: %1").arg(stream.status())); + return false; + } + return true; +} + +MTP::AuthKeyPtr CreateLocalKey( + const QByteArray &passcode, + const QByteArray &salt) { + auto key = MTP::AuthKey::Data{ { gsl::byte{} } }; + const auto iterationsCount = passcode.isEmpty() + ? LocalEncryptNoPwdIterCount // Don't slow down for no password. + : LocalEncryptIterCount; + + PKCS5_PBKDF2_HMAC_SHA1( + passcode.constData(), + passcode.size(), + (uchar*)salt.data(), + salt.size(), + iterationsCount, + key.size(), + (uchar*)key.data()); + + return std::make_shared(key); +} + +FileReadDescriptor::~FileReadDescriptor() { + if (version) { + stream.setDevice(nullptr); + if (buffer.isOpen()) { + buffer.close(); + } + buffer.setBuffer(nullptr); + } +} + +EncryptedDescriptor::EncryptedDescriptor() { +} + +EncryptedDescriptor::EncryptedDescriptor(uint32 size) { + uint32 fullSize = sizeof(uint32) + size; + if (fullSize & 0x0F) fullSize += 0x10 - (fullSize & 0x0F); + data.reserve(fullSize); + + data.resize(sizeof(uint32)); + buffer.setBuffer(&data); + buffer.open(QIODevice::WriteOnly); + buffer.seek(sizeof(uint32)); + stream.setDevice(&buffer); + stream.setVersion(QDataStream::Qt_5_1); +} + +EncryptedDescriptor::~EncryptedDescriptor() { + finish(); +} + +void EncryptedDescriptor::finish() { + if (stream.device()) stream.setDevice(nullptr); + if (buffer.isOpen()) buffer.close(); + buffer.setBuffer(nullptr); +} + +FileWriteDescriptor::FileWriteDescriptor( + const FileKey &key, + const QString &basePath) +: FileWriteDescriptor(ToFilePart(key), basePath) { +} + +FileWriteDescriptor::FileWriteDescriptor( + const QString &name, + const QString &basePath) +: _basePath(basePath) { + init(name); +} + +FileWriteDescriptor::~FileWriteDescriptor() { + finish(); +} + +QString FileWriteDescriptor::path(char postfix) const { + return _base + postfix; +} + +template +bool FileWriteDescriptor::open(File &file, char postfix) { + const auto name = path(postfix); + file.setFileName(name); + if (!writeHeader(file)) { + LOG(("Storage Error: Could not open '%1' for writing.").arg(name)); + return false; + } + return true; +} + +bool FileWriteDescriptor::writeHeader(QFileDevice &file) { + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + file.write(TdfMagic, TdfMagicLen); + const auto version = qint32(AppVersion); + file.write((const char*)&version, sizeof(version)); + return true; +} + +void FileWriteDescriptor::writeFooter(QFileDevice &file) { + file.write((const char*)_md5.result(), 0x10); +} + +void FileWriteDescriptor::init(const QString &name) { + _base = _basePath + name; + _buffer.setBuffer(&_safeData); + const auto opened = _buffer.open(QIODevice::WriteOnly); + Assert(opened); + _stream.setDevice(&_buffer); +} + +void FileWriteDescriptor::writeData(const QByteArray &data) { + if (!_stream.device()) { + return; + } + _stream << data; + quint32 len = data.isNull() ? 0xffffffff : data.size(); + if (QSysInfo::ByteOrder != QSysInfo::BigEndian) { + len = qbswap(len); + } + _md5.feed(&len, sizeof(len)); + _md5.feed(data.constData(), data.size()); + _fullSize += sizeof(len) + data.size(); +} + +void FileWriteDescriptor::writeEncrypted( + EncryptedDescriptor &data, + const MTP::AuthKeyPtr &key) { + writeData(PrepareEncrypted(data, key)); +} + +void FileWriteDescriptor::finish() { + if (!_stream.device()) { + return; + } + + _stream.setDevice(nullptr); + _md5.feed(&_fullSize, sizeof(_fullSize)); + qint32 version = AppVersion; + _md5.feed(&version, sizeof(version)); + _md5.feed(TdfMagic, TdfMagicLen); + + _buffer.close(); + + const auto safe = path('s'); + const auto simple = path('0'); + const auto backup = path('1'); + QSaveFile save; + if (open(save, 's')) { + save.write(_safeData); + writeFooter(save); + if (save.commit()) { + QFile::remove(simple); + QFile::remove(backup); + return; + } + LOG(("Storage Error: Could not commit '%1'.").arg(safe)); + } + QFile plain; + if (open(plain, '0')) { + plain.write(_safeData); + writeFooter(plain); + base::Platform::FlushFileData(plain); + plain.close(); + + QFile::remove(backup); + if (base::Platform::RenameWithOverwrite(simple, safe)) { + return; + } + QFile::remove(safe); + LOG(("Storage Error: Could not rename '%1' to '%2', removing." + ).arg(simple + ).arg(safe)); + } +} + +[[nodiscard]] QByteArray PrepareEncrypted( + EncryptedDescriptor &data, + const MTP::AuthKeyPtr &key) { + data.finish(); + QByteArray &toEncrypt(data.data); + + // prepare for encryption + uint32 size = toEncrypt.size(), fullSize = size; + if (fullSize & 0x0F) { + fullSize += 0x10 - (fullSize & 0x0F); + toEncrypt.resize(fullSize); + memset_rand(toEncrypt.data() + size, fullSize - size); + } + *(uint32*)toEncrypt.data() = size; + QByteArray encrypted(0x10 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data + hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data()); + MTP::aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 0x10, fullSize, key, encrypted.constData()); + + return encrypted; +} + +bool ReadFile( + FileReadDescriptor &result, + const QString &name, + const QString &basePath) { + const auto base = basePath + name; + + // detect order of read attempts + QString toTry[2]; + const auto modern = base + 's'; + if (QFileInfo(modern).exists()) { + toTry[0] = modern; + } else { + // Legacy way. + toTry[0] = base + '0'; + QFileInfo toTry0(toTry[0]); + if (toTry0.exists()) { + toTry[1] = basePath + name + '1'; + QFileInfo toTry1(toTry[1]); + if (toTry1.exists()) { + QDateTime mod0 = toTry0.lastModified(); + QDateTime mod1 = toTry1.lastModified(); + if (mod0 < mod1) { + qSwap(toTry[0], toTry[1]); + } + } else { + toTry[1] = QString(); + } + } else { + toTry[0][toTry[0].size() - 1] = '1'; + } + } + for (int32 i = 0; i < 2; ++i) { + QString fname(toTry[i]); + if (fname.isEmpty()) break; + + QFile f(fname); + if (!f.open(QIODevice::ReadOnly)) { + DEBUG_LOG(("App Info: failed to open '%1' for reading" + ).arg(name)); + continue; + } + + // check magic + char magic[TdfMagicLen]; + if (f.read(magic, TdfMagicLen) != TdfMagicLen) { + DEBUG_LOG(("App Info: failed to read magic from '%1'" + ).arg(name)); + continue; + } + if (memcmp(magic, TdfMagic, TdfMagicLen)) { + DEBUG_LOG(("App Info: bad magic %1 in '%2'" + ).arg(Logs::mb(magic, TdfMagicLen).str() + ).arg(name)); + continue; + } + + // read app version + qint32 version; + if (f.read((char*)&version, sizeof(version)) != sizeof(version)) { + DEBUG_LOG(("App Info: failed to read version from '%1'" + ).arg(name)); + continue; + } + if (version > AppVersion) { + DEBUG_LOG(("App Info: version too big %1 for '%2', my version %3" + ).arg(version + ).arg(name + ).arg(AppVersion)); + continue; + } + + // read data + QByteArray bytes = f.read(f.size()); + int32 dataSize = bytes.size() - 16; + if (dataSize < 0) { + DEBUG_LOG(("App Info: bad file '%1', could not read sign part" + ).arg(name)); + continue; + } + + // check signature + HashMd5 md5; + md5.feed(bytes.constData(), dataSize); + md5.feed(&dataSize, sizeof(dataSize)); + md5.feed(&version, sizeof(version)); + md5.feed(magic, TdfMagicLen); + if (memcmp(md5.result(), bytes.constData() + dataSize, 16)) { + DEBUG_LOG(("App Info: bad file '%1', signature did not match" + ).arg(name)); + continue; + } + + bytes.resize(dataSize); + result.data = bytes; + bytes = QByteArray(); + + result.version = version; + result.buffer.setBuffer(&result.data); + result.buffer.open(QIODevice::ReadOnly); + result.stream.setDevice(&result.buffer); + result.stream.setVersion(QDataStream::Qt_5_1); + + if ((i == 0 && !toTry[1].isEmpty()) || i == 1) { + QFile::remove(toTry[1 - i]); + } + + return true; + } + return false; +} + +bool DecryptLocal( + EncryptedDescriptor &result, + const QByteArray &encrypted, + const MTP::AuthKeyPtr &key) { + if (encrypted.size() <= 16 || (encrypted.size() & 0x0F)) { + LOG(("App Error: bad encrypted part size: %1").arg(encrypted.size())); + return false; + } + uint32 fullLen = encrypted.size() - 16; + + QByteArray decrypted; + decrypted.resize(fullLen); + const char *encryptedKey = encrypted.constData(), *encryptedData = encrypted.constData() + 16; + aesDecryptLocal(encryptedData, decrypted.data(), fullLen, key, encryptedKey); + uchar sha1Buffer[20]; + if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), encryptedKey, 16)) { + LOG(("App Info: bad decrypt key, data not decrypted - incorrect password?")); + return false; + } + + uint32 dataLen = *(const uint32*)decrypted.constData(); + if (dataLen > uint32(decrypted.size()) || dataLen <= fullLen - 16 || dataLen < sizeof(uint32)) { + LOG(("App Error: bad decrypted part size: %1, fullLen: %2, decrypted size: %3").arg(dataLen).arg(fullLen).arg(decrypted.size())); + return false; + } + + decrypted.resize(dataLen); + result.data = decrypted; + decrypted = QByteArray(); + + result.buffer.setBuffer(&result.data); + result.buffer.open(QIODevice::ReadOnly); + result.buffer.seek(sizeof(uint32)); // skip len + result.stream.setDevice(&result.buffer); + result.stream.setVersion(QDataStream::Qt_5_1); + + return true; +} + +bool ReadEncryptedFile( + FileReadDescriptor &result, + const QString &name, + const QString &basePath, + const MTP::AuthKeyPtr &key) { + if (!ReadFile(result, name, basePath)) { + return false; + } + QByteArray encrypted; + result.stream >> encrypted; + + EncryptedDescriptor data; + if (!DecryptLocal(data, encrypted, key)) { + result.stream.setDevice(nullptr); + if (result.buffer.isOpen()) result.buffer.close(); + result.buffer.setBuffer(nullptr); + result.data = QByteArray(); + result.version = 0; + return false; + } + + result.stream.setDevice(0); + if (result.buffer.isOpen()) { + result.buffer.close(); + } + result.buffer.setBuffer(0); + result.data = data.data; + result.buffer.setBuffer(&result.data); + result.buffer.open(QIODevice::ReadOnly); + result.buffer.seek(data.buffer.pos()); + result.stream.setDevice(&result.buffer); + result.stream.setVersion(QDataStream::Qt_5_1); + + return true; +} + +bool ReadEncryptedFile( + FileReadDescriptor &result, + const FileKey &fkey, + const QString &basePath, + const MTP::AuthKeyPtr &key) { + return ReadEncryptedFile(result, ToFilePart(fkey), basePath, key); +} + +} // namespace details +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/details/storage_file_utilities.h b/Telegram/SourceFiles/storage/details/storage_file_utilities.h new file mode 100644 index 0000000000..34f47cb974 --- /dev/null +++ b/Telegram/SourceFiles/storage/details/storage_file_utilities.h @@ -0,0 +1,109 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "storage/storage_account.h" + +#include + +namespace Storage { +namespace details { + +[[nodiscard]] QString ToFilePart(FileKey val); +[[nodiscard]] bool KeyAlreadyUsed(QString &name); +[[nodiscard]] FileKey GenerateKey(const QString &basePath); +void ClearKey(const FileKey &key, const QString &basePath); + +[[nodiscard]] bool CheckStreamStatus(QDataStream &stream); +[[nodiscard]] MTP::AuthKeyPtr CreateLocalKey( + const QByteArray &passcode, + const QByteArray &salt); + +struct FileReadDescriptor final { + ~FileReadDescriptor(); + + int32 version = 0; + QByteArray data; + QBuffer buffer; + QDataStream stream; +}; + +struct EncryptedDescriptor final { + EncryptedDescriptor(); + explicit EncryptedDescriptor(uint32 size); + ~EncryptedDescriptor(); + + void finish(); + + QByteArray data; + QBuffer buffer; + QDataStream stream; +}; + +[[nodiscard]] QByteArray PrepareEncrypted( + EncryptedDescriptor &data, + const MTP::AuthKeyPtr &key); + +class FileWriteDescriptor final { +public: + FileWriteDescriptor( + const FileKey &key, + const QString &basePath); + FileWriteDescriptor( + const QString &name, + const QString &basePath); + ~FileWriteDescriptor(); + + void writeData(const QByteArray &data); + void writeEncrypted( + EncryptedDescriptor &data, + const MTP::AuthKeyPtr &key); + +private: + void init(const QString &name); + [[nodiscard]] QString path(char postfix) const; + template + [[nodiscard]] bool open(File &file, char postfix); + [[nodiscard]] bool writeHeader(QFileDevice &file); + void writeFooter(QFileDevice &file); + void finish(); + + const QString _basePath; + QBuffer _buffer; + QDataStream _stream; + QByteArray _safeData; + QString _base; + HashMd5 _md5; + int _fullSize = 0; + +}; + +bool ReadFile( + FileReadDescriptor &result, + const QString &name, + const QString &basePath); + +bool DecryptLocal( + EncryptedDescriptor &result, + const QByteArray &encrypted, + const MTP::AuthKeyPtr &key); + +bool ReadEncryptedFile( + FileReadDescriptor &result, + const QString &name, + const QString &basePath, + const MTP::AuthKeyPtr &key); + +bool ReadEncryptedFile( + FileReadDescriptor &result, + const FileKey &fkey, + const QString &basePath, + const MTP::AuthKeyPtr &key); + +} // namespace details +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp new file mode 100644 index 0000000000..98bd475e36 --- /dev/null +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.cpp @@ -0,0 +1,1013 @@ +/* +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/details/storage_settings_scheme.h" + +#include "storage/details/storage_file_utilities.h" +#include "storage/cache/storage_cache_database.h" +#include "core/application.h" +#include "main/main_account.h" +#include "storage/serialize_common.h" +#include "ui/effects/animation_value.h" +#include "ui/widgets/input_fields.h" +#include "window/themes/window_theme.h" +#include "core/update_checker.h" +#include "platform/platform_specific.h" +#include "boxes/send_files_box.h" +#include "facades.h" + +namespace Storage { +namespace details { +namespace { + +using Cache::Database; + +[[nodiscard]] bool NoTimeLimit(qint32 storedLimitValue) { + // This is a workaround for a bug in storing the cache time limit. + // See https://github.com/telegramdesktop/tdesktop/issues/5611 + return !storedLimitValue + || (storedLimitValue == qint32(std::numeric_limits::max())) + || (storedLimitValue == qint32(std::numeric_limits::max())); +} + +} // namespace + +bool ReadSetting( + quint32 blockId, + QDataStream &stream, + int version, + ReadSettingsContext &context) { + switch (blockId) { + case dbiDcOptionOldOld: { + quint32 dcId, port; + QString host, ip; + stream >> dcId >> host >> ip >> port; + if (!CheckStreamStatus(stream)) return false; + + context.dcOptions.constructAddOne( + dcId, + 0, + ip.toStdString(), + port, + {}); + } break; + + case dbiDcOptionOld: { + quint32 dcIdWithShift, port; + qint32 flags; + QString ip; + stream >> dcIdWithShift >> flags >> ip >> port; + if (!CheckStreamStatus(stream)) return false; + + context.dcOptions.constructAddOne( + dcIdWithShift, + MTPDdcOption::Flags::from_raw(flags), + ip.toStdString(), + port, + {}); + } break; + + case dbiDcOptions: { + auto serialized = QByteArray(); + stream >> serialized; + if (!CheckStreamStatus(stream)) return false; + + context.dcOptions.constructFromSerialized(serialized); + } break; + + case dbiApplicationSettings: { + auto serialized = QByteArray(); + stream >> serialized; + if (!CheckStreamStatus(stream)) return false; + + Core::App().settings().constructFromSerialized(serialized); + } break; + + case dbiChatSizeMax: { + qint32 maxSize; + stream >> maxSize; + if (!CheckStreamStatus(stream)) return false; + + Global::SetChatSizeMax(maxSize); + } break; + + case dbiSavedGifsLimit: { + qint32 limit; + stream >> limit; + if (!CheckStreamStatus(stream)) return false; + + Global::SetSavedGifsLimit(limit); + } break; + + case dbiStickersRecentLimit: { + qint32 limit; + stream >> limit; + if (!CheckStreamStatus(stream)) return false; + + Global::SetStickersRecentLimit(limit); + } break; + + case dbiStickersFavedLimit: { + qint32 limit; + stream >> limit; + if (!CheckStreamStatus(stream)) return false; + + Global::SetStickersFavedLimit(limit); + } break; + + case dbiMegagroupSizeMax: { + qint32 maxSize; + stream >> maxSize; + if (!CheckStreamStatus(stream)) return false; + + Global::SetMegagroupSizeMax(maxSize); + } break; + + case dbiUser: { + quint32 dcId; + qint32 userId; + stream >> userId >> dcId; + if (!CheckStreamStatus(stream)) return false; + + DEBUG_LOG(("MTP Info: user found, dc %1, uid %2").arg(dcId).arg(userId)); + Core::App().activeAccount().setMtpMainDcId(dcId); + Core::App().activeAccount().setSessionUserId(userId); + } break; + + case dbiKey: { + qint32 dcId; + stream >> dcId; + auto key = Serialize::read(stream); + if (!CheckStreamStatus(stream)) return false; + + Core::App().activeAccount().setMtpKey(dcId, key); + } break; + + case dbiMtpAuthorization: { + auto serialized = QByteArray(); + stream >> serialized; + if (!CheckStreamStatus(stream)) return false; + + Core::App().activeAccount().setMtpAuthorization(serialized); + } break; + + case dbiAutoStart: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetAutoStart(v == 1); + } break; + + case dbiStartMinimized: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetStartMinimized(v == 1); + } break; + + case dbiSendToMenu: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetSendToMenu(v == 1); + } break; + + case dbiUseExternalVideoPlayer: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetUseExternalVideoPlayer(v == 1); + } break; + + case dbiCacheSettingsOld: { + qint64 size; + qint32 time; + stream >> size >> time; + if (!CheckStreamStatus(stream) + || size <= Database::Settings().maxDataSize + || (!NoTimeLimit(time) && time < 0)) { + return false; + } + context.cacheTotalSizeLimit = size; + context.cacheTotalTimeLimit = NoTimeLimit(time) ? 0 : time; + context.cacheBigFileTotalSizeLimit = size; + context.cacheBigFileTotalTimeLimit = NoTimeLimit(time) ? 0 : time; + } break; + + case dbiCacheSettings: { + qint64 size, sizeBig; + qint32 time, timeBig; + stream >> size >> time >> sizeBig >> timeBig; + if (!CheckStreamStatus(stream) + || size <= Database::Settings().maxDataSize + || sizeBig <= Database::Settings().maxDataSize + || (!NoTimeLimit(time) && time < 0) + || (!NoTimeLimit(timeBig) && timeBig < 0)) { + return false; + } + + context.cacheTotalSizeLimit = size; + context.cacheTotalTimeLimit = NoTimeLimit(time) ? 0 : time; + context.cacheBigFileTotalSizeLimit = sizeBig; + context.cacheBigFileTotalTimeLimit = NoTimeLimit(timeBig) ? 0 : timeBig; + } break; + + case dbiAnimationsDisabled: { + qint32 disabled; + stream >> disabled; + if (!CheckStreamStatus(stream)) { + return false; + } + + anim::SetDisabled(disabled == 1); + } break; + + case dbiSoundFlashBounceNotify: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetSoundNotify((v & 0x01) == 0x01); + Global::SetFlashBounceNotify((v & 0x02) == 0x00); + } break; + + case dbiAutoDownloadOld: { + qint32 photo, audio, gif; + stream >> photo >> audio >> gif; + if (!CheckStreamStatus(stream)) return false; + + using namespace Data::AutoDownload; + auto &settings = context.sessionSettings().autoDownload(); + const auto disabled = [](qint32 value, qint32 mask) { + return (value & mask) != 0; + }; + const auto set = [&](Type type, qint32 value) { + constexpr auto kNoPrivate = qint32(0x01); + constexpr auto kNoGroups = qint32(0x02); + if (disabled(value, kNoPrivate)) { + settings.setBytesLimit(Source::User, type, 0); + } + if (disabled(value, kNoGroups)) { + settings.setBytesLimit(Source::Group, type, 0); + settings.setBytesLimit(Source::Channel, type, 0); + } + }; + set(Type::Photo, photo); + set(Type::VoiceMessage, audio); + set(Type::AutoPlayGIF, gif); + set(Type::AutoPlayVideoMessage, gif); + } break; + + case dbiAutoPlayOld: { + qint32 gif; + stream >> gif; + if (!CheckStreamStatus(stream)) return false; + + if (!gif) { + using namespace Data::AutoDownload; + auto &settings = context.sessionSettings().autoDownload(); + const auto types = { + Type::AutoPlayGIF, + Type::AutoPlayVideo, + Type::AutoPlayVideoMessage, + }; + const auto sources = { + Source::User, + Source::Group, + Source::Channel + }; + for (const auto source : sources) { + for (const auto type : types) { + settings.setBytesLimit(source, type, 0); + } + } + } + } break; + + case dbiDialogsModeOld: { + qint32 enabled, modeInt; + stream >> enabled >> modeInt; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiDialogsFilters: { + qint32 enabled; + stream >> enabled; + if (!CheckStreamStatus(stream)) return false; + + Global::SetDialogsFiltersEnabled(enabled == 1); + } break; + + case dbiModerateMode: { + qint32 enabled; + stream >> enabled; + if (!CheckStreamStatus(stream)) return false; + + Global::SetModerateModeEnabled(enabled == 1); + } break; + + case dbiIncludeMutedOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().setIncludeMutedCounter(v == 1); + } break; + + case dbiShowingSavedGifsOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiDesktopNotify: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetDesktopNotify(v == 1); + } break; + + case dbiWindowsNotificationsOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiNativeNotifications: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetNativeNotifications(v == 1); + } break; + + case dbiNotificationsCount: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetNotificationsCount((v > 0 ? v : 3)); + } break; + + case dbiNotificationsCorner: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetNotificationsCorner(static_cast((v >= 0 && v < 4) ? v : 2)); + } break; + + case dbiDialogsWidthRatioOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().setDialogsWidthRatio(v / 1000000.); + } break; + + case dbiLastSeenWarningSeenOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().setLastSeenWarningSeen(v == 1); + } break; + + case dbiSessionSettings: { + QByteArray v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().constructFromSerialized(v); + } break; + + case dbiWorkMode: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + auto newMode = [v] { + switch (v) { + case dbiwmTrayOnly: return dbiwmTrayOnly; + case dbiwmWindowOnly: return dbiwmWindowOnly; + }; + return dbiwmWindowAndTray; + }; + Global::RefWorkMode().set(newMode()); + } break; + + case dbiTxtDomainStringOld: { + QString v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiTxtDomainString: { + QString v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetTxtDomainString(v); + } break; + + case dbiConnectionTypeOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + MTP::ProxyData proxy; + switch (v) { + case dbictHttpProxy: + case dbictTcpProxy: { + qint32 port; + stream >> proxy.host >> port >> proxy.user >> proxy.password; + if (!CheckStreamStatus(stream)) return false; + + proxy.port = uint32(port); + proxy.type = (v == dbictTcpProxy) + ? MTP::ProxyData::Type::Socks5 + : MTP::ProxyData::Type::Http; + } break; + }; + Global::SetSelectedProxy(proxy ? proxy : MTP::ProxyData()); + Global::SetProxySettings(proxy + ? MTP::ProxyData::Settings::Enabled + : MTP::ProxyData::Settings::System); + if (proxy) { + Global::SetProxiesList({ 1, proxy }); + } else { + Global::SetProxiesList({}); + } + Core::App().refreshGlobalProxy(); + } break; + + case dbiConnectionType: { + qint32 connectionType; + stream >> connectionType; + if (!CheckStreamStatus(stream)) { + return false; + } + + const auto readProxy = [&] { + qint32 proxyType, port; + MTP::ProxyData proxy; + stream >> proxyType >> proxy.host >> port >> proxy.user >> proxy.password; + proxy.port = port; + proxy.type = (proxyType == dbictTcpProxy) + ? MTP::ProxyData::Type::Socks5 + : (proxyType == dbictHttpProxy) + ? MTP::ProxyData::Type::Http + : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Socks5)) + ? MTP::ProxyData::Type::Socks5 + : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Http)) + ? MTP::ProxyData::Type::Http + : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Mtproto)) + ? MTP::ProxyData::Type::Mtproto + : MTP::ProxyData::Type::None; + return proxy; + }; + if (connectionType == dbictProxiesListOld + || connectionType == dbictProxiesList) { + qint32 count = 0, index = 0; + stream >> count >> index; + qint32 settings = 0, calls = 0; + if (connectionType == dbictProxiesList) { + stream >> settings >> calls; + } else if (std::abs(index) > count) { + calls = 1; + index -= (index > 0 ? count : -count); + } + + auto list = std::vector(); + for (auto i = 0; i < count; ++i) { + const auto proxy = readProxy(); + if (proxy) { + list.push_back(proxy); + } else if (index < -list.size()) { + ++index; + } else if (index > list.size()) { + --index; + } + } + if (!CheckStreamStatus(stream)) { + return false; + } + Global::SetProxiesList(list); + if (connectionType == dbictProxiesListOld) { + settings = static_cast( + (index > 0 && index <= list.size() + ? MTP::ProxyData::Settings::Enabled + : MTP::ProxyData::Settings::System)); + index = std::abs(index); + } + if (index > 0 && index <= list.size()) { + Global::SetSelectedProxy(list[index - 1]); + } else { + Global::SetSelectedProxy(MTP::ProxyData()); + } + + const auto unchecked = static_cast(settings); + switch (unchecked) { + case MTP::ProxyData::Settings::Enabled: + Global::SetProxySettings(Global::SelectedProxy() + ? MTP::ProxyData::Settings::Enabled + : MTP::ProxyData::Settings::System); + break; + case MTP::ProxyData::Settings::Disabled: + case MTP::ProxyData::Settings::System: + Global::SetProxySettings(unchecked); + break; + default: + Global::SetProxySettings(MTP::ProxyData::Settings::System); + break; + } + Global::SetUseProxyForCalls(calls == 1); + } else { + const auto proxy = readProxy(); + if (!CheckStreamStatus(stream)) { + return false; + } + if (proxy) { + Global::SetProxiesList({ 1, proxy }); + Global::SetSelectedProxy(proxy); + if (connectionType == dbictTcpProxy + || connectionType == dbictHttpProxy) { + Global::SetProxySettings(MTP::ProxyData::Settings::Enabled); + } else { + Global::SetProxySettings(MTP::ProxyData::Settings::System); + } + } else { + Global::SetProxiesList({}); + Global::SetSelectedProxy(MTP::ProxyData()); + Global::SetProxySettings(MTP::ProxyData::Settings::System); + } + } + Core::App().refreshGlobalProxy(); + } break; + + case dbiThemeKeyOld: { + quint64 key = 0; + stream >> key; + if (!CheckStreamStatus(stream)) return false; + + context.themeKeyLegacy = key; + } break; + + case dbiThemeKey: { + quint64 keyDay = 0, keyNight = 0; + quint32 nightMode = 0; + stream >> keyDay >> keyNight >> nightMode; + if (!CheckStreamStatus(stream)) return false; + + context.themeKeyDay = keyDay; + context.themeKeyNight = keyNight; + Window::Theme::SetNightModeValue(nightMode == 1); + } break; + + case dbiLangPackKey: { + quint64 langPackKey = 0; + stream >> langPackKey; + if (!CheckStreamStatus(stream)) return false; + + context.langPackKey = langPackKey; + } break; + + case dbiLanguagesKey: { + quint64 languagesKey = 0; + stream >> languagesKey; + if (!CheckStreamStatus(stream)) return false; + + context.languagesKey = languagesKey; + } break; + + case dbiTryIPv6: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetTryIPv6(v == 1); + } break; + + case dbiSeenTrayTooltip: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetSeenTrayTooltip(v == 1); + } break; + + case dbiAutoUpdate: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetAutoUpdate(v == 1); + if (!Core::UpdaterDisabled() && !cAutoUpdate()) { + Core::UpdateChecker().stop(); + } + } break; + + case dbiLastUpdateCheck: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetLastUpdateCheck(v); + } break; + + case dbiScaleOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + SetScaleChecked([&] { + constexpr auto kAuto = 0; + constexpr auto kOne = 1; + constexpr auto kOneAndQuarter = 2; + constexpr auto kOneAndHalf = 3; + constexpr auto kTwo = 4; + switch (v) { + case kAuto: return style::kScaleAuto; + case kOne: return 100; + case kOneAndQuarter: return 125; + case kOneAndHalf: return 150; + case kTwo: return 200; + } + return cConfigScale(); + }()); + } break; + + case dbiScalePercent: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + // If cConfigScale() has value then it was set via command line. + if (cConfigScale() == style::kScaleAuto) { + SetScaleChecked(v); + } + } break; + + case dbiLangOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiLangFileOld: { + QString v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiWindowPosition: { + auto position = TWindowPos(); + stream >> position.x >> position.y >> position.w >> position.h; + stream >> position.moncrc >> position.maximized; + if (!CheckStreamStatus(stream)) return false; + + DEBUG_LOG(("Window Pos: Read from storage %1, %2, %3, %4 (maximized %5)").arg(position.x).arg(position.y).arg(position.w).arg(position.h).arg(Logs::b(position.maximized))); + cSetWindowPos(position); + } break; + + case dbiLoggedPhoneNumber: { + QString v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetLoggedPhoneNumber(v); + } break; + + case dbiMutePeer: { // deprecated + quint64 peerId; + stream >> peerId; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiMutedPeers: { // deprecated + quint32 count; + stream >> count; + if (!CheckStreamStatus(stream)) return false; + + for (uint32 i = 0; i < count; ++i) { + quint64 peerId; + stream >> peerId; + } + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiSendKeyOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + using SendSettings = Ui::InputSubmitSettings; + const auto unchecked = static_cast(v); + + if (unchecked != SendSettings::Enter + && unchecked != SendSettings::CtrlEnter) { + return false; + } + context.sessionSettings().setSendSubmitWay(unchecked); + } break; + + case dbiCatsAndDogs: { // deprecated + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiTileBackgroundOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + bool tile = (version < 8005 && !context.hasCustomDayBackground) + ? false + : (v == 1); + if (Window::Theme::IsNightMode()) { + Window::Theme::Background()->setTileNightValue(tile); + } else { + Window::Theme::Background()->setTileDayValue(tile); + } + } break; + + case dbiTileBackground: { + qint32 tileDay, tileNight; + stream >> tileDay >> tileNight; + if (!CheckStreamStatus(stream)) return false; + + Window::Theme::Background()->setTileDayValue(tileDay == 1); + Window::Theme::Background()->setTileNightValue(tileNight == 1); + } break; + + case dbiAdaptiveForWide: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetAdaptiveForWide(v == 1); + } break; + + case dbiAutoLock: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetAutoLock(v); + Global::RefLocalPasscodeChanged().notify(); + } break; + + case dbiReplaceEmojiOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().setReplaceEmoji(v == 1); + } break; + + case dbiSuggestEmojiOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().setSuggestEmoji(v == 1); + } break; + + case dbiSuggestStickersByEmojiOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().setSuggestStickersByEmoji(v == 1); + } break; + + case dbiDefaultAttach: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + } break; + + case dbiNotifyView: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + switch (v) { + case dbinvShowNothing: Global::SetNotifyView(dbinvShowNothing); break; + case dbinvShowName: Global::SetNotifyView(dbinvShowName); break; + default: Global::SetNotifyView(dbinvShowPreview); break; + } + } break; + + case dbiAskDownloadPath: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetAskDownloadPath(v == 1); + } break; + + case dbiDownloadPathOld: { + QString v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; +#ifndef OS_WIN_STORE + if (!v.isEmpty() && v != qstr("tmp") && !v.endsWith('/')) v += '/'; + Global::SetDownloadPath(v); + Global::SetDownloadPathBookmark(QByteArray()); + Global::RefDownloadPathChanged().notify(); +#endif // OS_WIN_STORE + } break; + + case dbiDownloadPath: { + QString v; + QByteArray bookmark; + stream >> v >> bookmark; + if (!CheckStreamStatus(stream)) return false; + +#ifndef OS_WIN_STORE + if (!v.isEmpty() && v != qstr("tmp") && !v.endsWith('/')) v += '/'; + Global::SetDownloadPath(v); + Global::SetDownloadPathBookmark(bookmark); + psDownloadPathEnableAccess(); + Global::RefDownloadPathChanged().notify(); +#endif // OS_WIN_STORE + } break; + + case dbiCompressPastedImage: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + context.sessionSettings().setSendFilesWay((v == 1) + ? SendFilesWay::Album + : SendFilesWay::Files); + } break; + + case dbiEmojiTabOld: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + // deprecated + } break; + + case dbiRecentEmojiOldOld: { + RecentEmojiPreloadOldOld v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + if (!v.isEmpty()) { + RecentEmojiPreload p; + p.reserve(v.size()); + for (auto &item : v) { + auto oldKey = uint64(item.first); + switch (oldKey) { + case 0xD83CDDEFLLU: oldKey = 0xD83CDDEFD83CDDF5LLU; break; + case 0xD83CDDF0LLU: oldKey = 0xD83CDDF0D83CDDF7LLU; break; + case 0xD83CDDE9LLU: oldKey = 0xD83CDDE9D83CDDEALLU; break; + case 0xD83CDDE8LLU: oldKey = 0xD83CDDE8D83CDDF3LLU; break; + case 0xD83CDDFALLU: oldKey = 0xD83CDDFAD83CDDF8LLU; break; + case 0xD83CDDEBLLU: oldKey = 0xD83CDDEBD83CDDF7LLU; break; + case 0xD83CDDEALLU: oldKey = 0xD83CDDEAD83CDDF8LLU; break; + case 0xD83CDDEELLU: oldKey = 0xD83CDDEED83CDDF9LLU; break; + case 0xD83CDDF7LLU: oldKey = 0xD83CDDF7D83CDDFALLU; break; + case 0xD83CDDECLLU: oldKey = 0xD83CDDECD83CDDE7LLU; break; + } + auto id = Ui::Emoji::IdFromOldKey(oldKey); + if (!id.isEmpty()) { + p.push_back(qMakePair(id, item.second)); + } + } + cSetRecentEmojiPreload(p); + } + } break; + + case dbiRecentEmojiOld: { + RecentEmojiPreloadOld v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + if (!v.isEmpty()) { + RecentEmojiPreload p; + p.reserve(v.size()); + for (auto &item : v) { + auto id = Ui::Emoji::IdFromOldKey(item.first); + if (!id.isEmpty()) { + p.push_back(qMakePair(id, item.second)); + } + } + cSetRecentEmojiPreload(p); + } + } break; + + case dbiRecentEmoji: { + RecentEmojiPreload v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetRecentEmojiPreload(v); + } break; + + case dbiRecentStickers: { + RecentStickerPreload v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetRecentStickersPreload(v); + } break; + + case dbiEmojiVariantsOld: { + EmojiColorVariantsOld v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + EmojiColorVariants variants; + for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) { + auto id = Ui::Emoji::IdFromOldKey(static_cast(i.key())); + if (!id.isEmpty()) { + auto index = Ui::Emoji::ColorIndexFromOldKey(i.value()); + if (index >= 0) { + variants.insert(id, index); + } + } + } + cSetEmojiVariants(variants); + } break; + + case dbiEmojiVariants: { + EmojiColorVariants v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + cSetEmojiVariants(v); + } break; + + case dbiHiddenPinnedMessages: { + Global::HiddenPinnedMessagesMap v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetHiddenPinnedMessages(v); + } break; + + case dbiDialogLastPath: { + QString path; + stream >> path; + if (!CheckStreamStatus(stream)) return false; + + cSetDialogLastPath(path); + } break; + + case dbiSongVolume: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetSongVolume(snap(v / 1e6, 0., 1.)); + } break; + + case dbiVideoVolume: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetVideoVolume(snap(v / 1e6, 0., 1.)); + } break; + + case dbiPlaybackSpeed: { + qint32 v; + stream >> v; + if (!CheckStreamStatus(stream)) return false; + + Global::SetVoiceMsgPlaybackDoubled(v == 2); + } break; + + case dbiCallSettings: { + QByteArray callSettings; + stream >> callSettings; + if (!CheckStreamStatus(stream)) return false; + + context.callSettings = callSettings; + } break; + + default: + LOG(("App Error: unknown blockId in _readSetting: %1").arg(blockId)); + return false; + } + + return true; +} + +} // namespace details +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/details/storage_settings_scheme.h b/Telegram/SourceFiles/storage/details/storage_settings_scheme.h new file mode 100644 index 0000000000..41da5f3800 --- /dev/null +++ b/Telegram/SourceFiles/storage/details/storage_settings_scheme.h @@ -0,0 +1,161 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "mtproto/dc_options.h" +#include "main/main_settings.h" +#include "storage/storage_account.h" + +namespace Storage { +namespace details { + +struct ReadSettingsContext { + [[nodiscard]] Main::Settings &sessionSettings() { + if (!sessionSettingsStorage) { + sessionSettingsStorage = std::make_unique(); + } + return *sessionSettingsStorage; + } + + // This field is read in ReadSetting. + bool hasCustomDayBackground = false; + + // Those fields are written in ReadSetting. + MTP::DcOptions dcOptions; + + qint64 cacheTotalSizeLimit = 0; + qint32 cacheTotalTimeLimit = 0; + qint64 cacheBigFileTotalSizeLimit = 0; + qint32 cacheBigFileTotalTimeLimit = 0; + + std::unique_ptr sessionSettingsStorage; + + FileKey themeKeyLegacy = 0; + FileKey themeKeyDay = 0; + FileKey themeKeyNight = 0; + FileKey langPackKey = 0; + FileKey languagesKey = 0; + + QByteArray callSettings; +}; + +[[nodiscard]] bool ReadSetting( + quint32 blockId, + QDataStream &stream, + int version, + ReadSettingsContext &context); + +enum { + dbiKey = 0x00, + dbiUser = 0x01, + dbiDcOptionOldOld = 0x02, + dbiChatSizeMax = 0x03, + dbiMutePeer = 0x04, + dbiSendKeyOld = 0x05, + dbiAutoStart = 0x06, + dbiStartMinimized = 0x07, + dbiSoundFlashBounceNotify = 0x08, + dbiWorkMode = 0x09, + dbiSeenTrayTooltip = 0x0a, + dbiDesktopNotify = 0x0b, + dbiAutoUpdate = 0x0c, + dbiLastUpdateCheck = 0x0d, + dbiWindowPosition = 0x0e, + dbiConnectionTypeOld = 0x0f, + // 0x10 reserved + dbiDefaultAttach = 0x11, + dbiCatsAndDogs = 0x12, + dbiReplaceEmojiOld = 0x13, + dbiAskDownloadPath = 0x14, + dbiDownloadPathOld = 0x15, + dbiScaleOld = 0x16, + dbiEmojiTabOld = 0x17, + dbiRecentEmojiOldOld = 0x18, + dbiLoggedPhoneNumber = 0x19, + dbiMutedPeers = 0x1a, + // 0x1b reserved + dbiNotifyView = 0x1c, + dbiSendToMenu = 0x1d, + dbiCompressPastedImage = 0x1e, + dbiLangOld = 0x1f, + dbiLangFileOld = 0x20, + dbiTileBackgroundOld = 0x21, + dbiAutoLock = 0x22, + dbiDialogLastPath = 0x23, + dbiRecentEmojiOld = 0x24, + dbiEmojiVariantsOld = 0x25, + dbiRecentStickers = 0x26, + dbiDcOptionOld = 0x27, + dbiTryIPv6 = 0x28, + dbiSongVolume = 0x29, + dbiWindowsNotificationsOld = 0x30, + dbiIncludeMutedOld = 0x31, + dbiMegagroupSizeMax = 0x32, + dbiDownloadPath = 0x33, + dbiAutoDownloadOld = 0x34, + dbiSavedGifsLimit = 0x35, + dbiShowingSavedGifsOld = 0x36, + dbiAutoPlayOld = 0x37, + dbiAdaptiveForWide = 0x38, + dbiHiddenPinnedMessages = 0x39, + dbiRecentEmoji = 0x3a, + dbiEmojiVariants = 0x3b, + dbiDialogsModeOld = 0x40, + dbiModerateMode = 0x41, + dbiVideoVolume = 0x42, + dbiStickersRecentLimit = 0x43, + dbiNativeNotifications = 0x44, + dbiNotificationsCount = 0x45, + dbiNotificationsCorner = 0x46, + dbiThemeKeyOld = 0x47, + dbiDialogsWidthRatioOld = 0x48, + dbiUseExternalVideoPlayer = 0x49, + dbiDcOptions = 0x4a, + dbiMtpAuthorization = 0x4b, + dbiLastSeenWarningSeenOld = 0x4c, + dbiSessionSettings = 0x4d, + dbiLangPackKey = 0x4e, + dbiConnectionType = 0x4f, + dbiStickersFavedLimit = 0x50, + dbiSuggestStickersByEmojiOld = 0x51, + dbiSuggestEmojiOld = 0x52, + dbiTxtDomainStringOld = 0x53, + dbiThemeKey = 0x54, + dbiTileBackground = 0x55, + dbiCacheSettingsOld = 0x56, + dbiAnimationsDisabled = 0x57, + dbiScalePercent = 0x58, + dbiPlaybackSpeed = 0x59, + dbiLanguagesKey = 0x5a, + dbiCallSettings = 0x5b, + dbiCacheSettings = 0x5c, + dbiTxtDomainString = 0x5d, + dbiApplicationSettings = 0x5e, + dbiDialogsFilters = 0x5f, + + dbiEncryptedWithSalt = 333, + dbiEncrypted = 444, + + // 500-600 reserved + + dbiVersion = 666, +}; + +enum { + dbictAuto = 0, + dbictHttpAuto = 1, // not used + dbictHttpProxy = 2, + dbictTcpProxy = 3, + dbictProxiesListOld = 4, + dbictProxiesList = 5, +}; + +inline constexpr auto kProxyTypeShift = 1024; + +} // namespace details +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index 36e6236c55..f5a056b01b 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "mainwindow.h" #include "core/application.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "storage/file_download_mtproto.h" #include "storage/file_download_web.h" #include "platform/platform_file_utilities.h" @@ -425,7 +425,9 @@ bool FileLoader::finalizeResult() { if (_localStatus == LocalStatus::NotFound) { if (const auto key = fileLocationKey()) { if (!_filename.isEmpty()) { - Local::writeFileLocation(*key, FileLocation(_filename)); + _session->local().writeFileLocation( + *key, + FileLocation(_filename)); } } const auto key = cacheKey(); diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 8c1e9c8c05..7f4ca75ab6 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -6,97 +6,46 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "storage/localstorage.h" - -#include "storage/serialize_document.h" +// #include "storage/serialize_common.h" -#include "storage/storage_encrypted_file.h" -#include "storage/storage_clear_legacy.h" -#include "data/data_drafts.h" -#include "data/data_user.h" +#include "storage/storage_account.h" +#include "storage/details/storage_file_utilities.h" +#include "storage/details/storage_settings_scheme.h" #include "data/data_session.h" +#include "data/data_document.h" #include "data/data_document_media.h" -#include "data/stickers/data_stickers.h" -#include "boxes/send_files_box.h" -#include "base/flags.h" -#include "base/platform/base_platform_file_utilities.h" #include "base/platform/base_platform_info.h" -#include "ui/widgets/input_fields.h" -#include "ui/emoji_config.h" -#include "export/export_settings.h" -#include "api/api_hash.h" -#include "core/crash_reports.h" +#include "ui/effects/animation_value.h" #include "core/update_checker.h" -#include "observer_peer.h" -#include "mainwidget.h" -#include "mainwindow.h" -#include "lang/lang_keys.h" -#include "lang/lang_cloud_manager.h" -#include "main/main_account.h" #include "media/audio/media_audio.h" #include "mtproto/dc_options.h" #include "core/application.h" -#include "apiwrap.h" #include "main/main_session.h" #include "window/themes/window_theme.h" -#include "window/window_session_controller.h" -#include "history/history.h" #include "facades.h" -#include -#include -#include #include #ifndef Q_OS_WIN #include #endif // Q_OS_WIN -extern "C" { -#include -} // extern "C" +//extern "C" { +//#include +//} // extern "C" namespace Local { namespace { constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024; constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000); -constexpr auto kDefaultStickerInstallDate = TimeId(1); -constexpr auto kProxyTypeShift = 1024; -constexpr auto kWriteMapTimeout = crl::time(1000); -constexpr auto kSavedBackgroundFormat = QImage::Format_ARGB32_Premultiplied; - -constexpr auto kWallPaperLegacySerializeTagId = int32(-111); -constexpr auto kWallPaperSerializeTagId = int32(-112); -constexpr auto kWallPaperSidesLimit = 10'000; - -constexpr auto kSinglePeerTypeUser = qint32(1); -constexpr auto kSinglePeerTypeChat = qint32(2); -constexpr auto kSinglePeerTypeChannel = qint32(3); -constexpr auto kSinglePeerTypeSelf = qint32(4); -constexpr auto kSinglePeerTypeEmpty = qint32(0); - -constexpr auto kStickersVersionTag = quint32(-1); -constexpr auto kStickersSerializeVersion = 1; -constexpr auto kMaxSavedStickerSetsCount = 1000; const auto kThemeNewPathRelativeTag = qstr("special://new_tag"); +using namespace Storage::details; +using Storage::FileKey; + using Database = Storage::Cache::Database; -using FileKey = quint64; - -constexpr char tdfMagic[] = { 'T', 'D', 'F', '$' }; -constexpr auto tdfMagicLen = int(sizeof(tdfMagic)); - -QString toFilePart(FileKey val) { - QString result; - result.reserve(0x10); - for (int32 i = 0; i < 0x10; ++i) { - uchar v = (val & 0x0F); - result.push_back((v < 0x0A) ? ('0' + v) : ('A' + (v - 0x0A))); - val >>= 4; - } - return result; -} QString _basePath, _userBasePath, _userDbPath; @@ -108,69 +57,7 @@ bool _working() { return _manager && !_basePath.isEmpty(); } -bool _userWorking() { - return _manager && !_basePath.isEmpty() && !_userBasePath.isEmpty(); -} - -enum class FileOwner { - User = (1 << 0), - Global = (1 << 1), -}; - -[[nodiscard]] bool KeyAlreadyUsed(QString &name) { - name += '0'; - if (QFileInfo(name).exists()) { - return true; - } - name[name.size() - 1] = '1'; - if (QFileInfo(name).exists()) { - return true; - } - name[name.size() - 1] = 's'; - if (QFileInfo(name).exists()) { - return true; - } - return false; -} - -[[nodiscard]] FileKey GenerateKey(FileOwner owner = FileOwner::User) { - if (owner == FileOwner::User) { - if (!_userWorking()) return 0; - } else { - if (!_working()) return 0; - } - - FileKey result; - QString base = (owner == FileOwner::User) ? _userBasePath : _basePath, path; - path.reserve(base.size() + 0x11); - path += base; - do { - result = rand_value(); - path.resize(base.size()); - path += toFilePart(result); - } while (!result || KeyAlreadyUsed(path)); - - return result; -} - -void ClearKey(const FileKey &key, FileOwner owner = FileOwner::User) { - if (owner == FileOwner::User) { - if (!_userWorking()) return; - } else { - if (!_working()) return; - } - - QString base = (owner == FileOwner::User) ? _userBasePath : _basePath, name; - name.reserve(base.size() + 0x11); - name.append(base).append(toFilePart(key)).append('0'); - QFile::remove(name); - name[name.size() - 1] = '1'; - QFile::remove(name); - name[name.size() - 1] = 's'; - QFile::remove(name); -} - -bool _checkStreamStatus(QDataStream &stream) { +bool CheckStreamStatus(QDataStream &stream) { if (stream.status() != QDataStream::Ok) { LOG(("Bad data stream status: %1").arg(stream.status())); return false; @@ -178,601 +65,10 @@ bool _checkStreamStatus(QDataStream &stream) { return true; } -QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted; - -constexpr auto kLocalKeySize = MTP::AuthKey::kSize; +QByteArray _settingsSalt; auto OldKey = MTP::AuthKeyPtr(); auto SettingsKey = MTP::AuthKeyPtr(); -auto PassKey = MTP::AuthKeyPtr(); -auto LocalKey = MTP::AuthKeyPtr(); - -void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKeyPtr *result) { - auto key = MTP::AuthKey::Data { { gsl::byte{} } }; - auto iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password - auto newSalt = QByteArray(); - if (!salt) { - newSalt.resize(LocalEncryptSaltSize); - memset_rand(newSalt.data(), newSalt.size()); - salt = &newSalt; - - cSetLocalSalt(newSalt); - } - - PKCS5_PBKDF2_HMAC_SHA1(pass.constData(), pass.size(), (uchar*)salt->data(), salt->size(), iterCount, key.size(), (uchar*)key.data()); - - *result = std::make_shared(key); -} - -struct FileReadDescriptor { - int32 version = 0; - QByteArray data; - QBuffer buffer; - QDataStream stream; - ~FileReadDescriptor() { - if (version) { - stream.setDevice(nullptr); - if (buffer.isOpen()) buffer.close(); - buffer.setBuffer(nullptr); - } - } -}; - -struct EncryptedDescriptor { - EncryptedDescriptor() { - } - EncryptedDescriptor(uint32 size) { - uint32 fullSize = sizeof(uint32) + size; - if (fullSize & 0x0F) fullSize += 0x10 - (fullSize & 0x0F); - data.reserve(fullSize); - - data.resize(sizeof(uint32)); - buffer.setBuffer(&data); - buffer.open(QIODevice::WriteOnly); - buffer.seek(sizeof(uint32)); - stream.setDevice(&buffer); - stream.setVersion(QDataStream::Qt_5_1); - } - QByteArray data; - QBuffer buffer; - QDataStream stream; - void finish() { - if (stream.device()) stream.setDevice(nullptr); - if (buffer.isOpen()) buffer.close(); - buffer.setBuffer(nullptr); - } - ~EncryptedDescriptor() { - finish(); - } -}; - -class FileWriteDescriptor final { -public: - explicit FileWriteDescriptor( - const FileKey &key, - FileOwner owner = FileOwner::User); - explicit FileWriteDescriptor( - const QString &name, - FileOwner owner = FileOwner::User); - ~FileWriteDescriptor(); - - void writeData(const QByteArray &data); - void writeEncrypted( - EncryptedDescriptor &data, - const MTP::AuthKeyPtr &key = LocalKey); - -private: - void init(const QString &name); - [[nodiscard]] QString path(char postfix) const; - template - [[nodiscard]] bool open(File &file, char postfix); - [[nodiscard]] bool writeHeader(QFileDevice &file); - void writeFooter(QFileDevice &file); - void finish(); - - const FileOwner _owner = FileOwner(); - QBuffer _buffer; - QDataStream _stream; - QByteArray _safeData; - QString _base; - HashMd5 _md5; - int _fullSize = 0; - -}; - -[[nodiscard]] QByteArray PrepareEncrypted( - EncryptedDescriptor &data, - const MTP::AuthKeyPtr &key = LocalKey) { - data.finish(); - QByteArray &toEncrypt(data.data); - - // prepare for encryption - uint32 size = toEncrypt.size(), fullSize = size; - if (fullSize & 0x0F) { - fullSize += 0x10 - (fullSize & 0x0F); - toEncrypt.resize(fullSize); - memset_rand(toEncrypt.data() + size, fullSize - size); - } - *(uint32*)toEncrypt.data() = size; - QByteArray encrypted(0x10 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data - hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data()); - MTP::aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 0x10, fullSize, key, encrypted.constData()); - - return encrypted; -} - -FileWriteDescriptor::FileWriteDescriptor( - const FileKey &key, - FileOwner owner) -: FileWriteDescriptor(toFilePart(key), owner) { -} - -FileWriteDescriptor::FileWriteDescriptor( - const QString &name, - FileOwner owner) -: _owner(owner) { - init(name); -} - -FileWriteDescriptor::~FileWriteDescriptor() { - finish(); -} - -QString FileWriteDescriptor::path(char postfix) const { - return _base + postfix; -} - -template -bool FileWriteDescriptor::open(File &file, char postfix) { - const auto name = path(postfix); - file.setFileName(name); - if (!writeHeader(file)) { - LOG(("Storage Error: Could not open '%1' for writing.").arg(name)); - return false; - } - return true; -} - -bool FileWriteDescriptor::writeHeader(QFileDevice &file) { - if (!file.open(QIODevice::WriteOnly)) { - return false; - } - file.write(tdfMagic, tdfMagicLen); - const auto version = qint32(AppVersion); - file.write((const char*)&version, sizeof(version)); - return true; -} - -void FileWriteDescriptor::writeFooter(QFileDevice &file) { - file.write((const char*)_md5.result(), 0x10); -} - -void FileWriteDescriptor::init(const QString &name) { - const auto working = (_owner == FileOwner::User) - ? _userWorking() - : _working(); - if (!working) { - return; - } - - const auto basePath = (_owner == FileOwner::User) - ? _userBasePath - : _basePath; - _base = basePath + name; - _buffer.setBuffer(&_safeData); - const auto opened = _buffer.open(QIODevice::WriteOnly); - Assert(opened); - _stream.setDevice(&_buffer); -} - -void FileWriteDescriptor::writeData(const QByteArray &data) { - if (!_stream.device()) { - return; - } - _stream << data; - quint32 len = data.isNull() ? 0xffffffff : data.size(); - if (QSysInfo::ByteOrder != QSysInfo::BigEndian) { - len = qbswap(len); - } - _md5.feed(&len, sizeof(len)); - _md5.feed(data.constData(), data.size()); - _fullSize += sizeof(len) + data.size(); -} - -void FileWriteDescriptor::writeEncrypted( - EncryptedDescriptor &data, - const MTP::AuthKeyPtr &key) { - writeData(PrepareEncrypted(data, key)); -} - -void FileWriteDescriptor::finish() { - if (!_stream.device()) { - return; - } - - _stream.setDevice(nullptr); - _md5.feed(&_fullSize, sizeof(_fullSize)); - qint32 version = AppVersion; - _md5.feed(&version, sizeof(version)); - _md5.feed(tdfMagic, tdfMagicLen); - - _buffer.close(); - - const auto safe = path('s'); - const auto simple = path('0'); - const auto backup = path('1'); - QSaveFile save; - if (open(save, 's')) { - save.write(_safeData); - writeFooter(save); - if (save.commit()) { - QFile::remove(simple); - QFile::remove(backup); - return; - } - LOG(("Storage Error: Could not commit '%1'.").arg(safe)); - } - QFile plain; - if (open(plain, '0')) { - plain.write(_safeData); - writeFooter(plain); - base::Platform::FlushFileData(plain); - plain.close(); - - QFile::remove(backup); - if (base::Platform::RenameWithOverwrite(simple, safe)) { - return; - } - QFile::remove(safe); - LOG(("Storage Error: Could not rename '%1' to '%2', removing." - ).arg(simple - ).arg(safe)); - } -} - -bool ReadFile( - FileReadDescriptor &result, - const QString &name, - FileOwner owner = FileOwner::User) { - if (owner == FileOwner::User) { - if (!_userWorking()) return false; - } else { - if (!_working()) return false; - } - - const auto base = ((owner == FileOwner::User) ? _userBasePath : _basePath) + name; - - // detect order of read attempts - QString toTry[2]; - const auto modern = base + 's'; - if (QFileInfo(modern).exists()) { - toTry[0] = modern; - } else { - // Legacy way. - toTry[0] = base + '0'; - QFileInfo toTry0(toTry[0]); - if (toTry0.exists()) { - toTry[1] = ((owner == FileOwner::User) ? _userBasePath : _basePath) + name + '1'; - QFileInfo toTry1(toTry[1]); - if (toTry1.exists()) { - QDateTime mod0 = toTry0.lastModified(), mod1 = toTry1.lastModified(); - if (mod0 < mod1) { - qSwap(toTry[0], toTry[1]); - } - } else { - toTry[1] = QString(); - } - } else { - toTry[0][toTry[0].size() - 1] = '1'; - } - } - for (int32 i = 0; i < 2; ++i) { - QString fname(toTry[i]); - if (fname.isEmpty()) break; - - QFile f(fname); - if (!f.open(QIODevice::ReadOnly)) { - DEBUG_LOG(("App Info: failed to open '%1' for reading").arg(name)); - continue; - } - - // check magic - char magic[tdfMagicLen]; - if (f.read(magic, tdfMagicLen) != tdfMagicLen) { - DEBUG_LOG(("App Info: failed to read magic from '%1'").arg(name)); - continue; - } - if (memcmp(magic, tdfMagic, tdfMagicLen)) { - DEBUG_LOG(("App Info: bad magic %1 in '%2'").arg(Logs::mb(magic, tdfMagicLen).str()).arg(name)); - continue; - } - - // read app version - qint32 version; - if (f.read((char*)&version, sizeof(version)) != sizeof(version)) { - DEBUG_LOG(("App Info: failed to read version from '%1'").arg(name)); - continue; - } - if (version > AppVersion) { - DEBUG_LOG(("App Info: version too big %1 for '%2', my version %3").arg(version).arg(name).arg(AppVersion)); - continue; - } - - // read data - QByteArray bytes = f.read(f.size()); - int32 dataSize = bytes.size() - 16; - if (dataSize < 0) { - DEBUG_LOG(("App Info: bad file '%1', could not read sign part").arg(name)); - continue; - } - - // check signature - HashMd5 md5; - md5.feed(bytes.constData(), dataSize); - md5.feed(&dataSize, sizeof(dataSize)); - md5.feed(&version, sizeof(version)); - md5.feed(magic, tdfMagicLen); - if (memcmp(md5.result(), bytes.constData() + dataSize, 16)) { - DEBUG_LOG(("App Info: bad file '%1', signature did not match").arg(name)); - continue; - } - - bytes.resize(dataSize); - result.data = bytes; - bytes = QByteArray(); - - result.version = version; - result.buffer.setBuffer(&result.data); - result.buffer.open(QIODevice::ReadOnly); - result.stream.setDevice(&result.buffer); - result.stream.setVersion(QDataStream::Qt_5_1); - - if ((i == 0 && !toTry[1].isEmpty()) || i == 1) { - QFile::remove(toTry[1 - i]); - } - - return true; - } - return false; -} - -bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, const MTP::AuthKeyPtr &key = LocalKey) { - if (encrypted.size() <= 16 || (encrypted.size() & 0x0F)) { - LOG(("App Error: bad encrypted part size: %1").arg(encrypted.size())); - return false; - } - uint32 fullLen = encrypted.size() - 16; - - QByteArray decrypted; - decrypted.resize(fullLen); - const char *encryptedKey = encrypted.constData(), *encryptedData = encrypted.constData() + 16; - aesDecryptLocal(encryptedData, decrypted.data(), fullLen, key, encryptedKey); - uchar sha1Buffer[20]; - if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), encryptedKey, 16)) { - LOG(("App Info: bad decrypt key, data not decrypted - incorrect password?")); - return false; - } - - uint32 dataLen = *(const uint32*)decrypted.constData(); - if (dataLen > uint32(decrypted.size()) || dataLen <= fullLen - 16 || dataLen < sizeof(uint32)) { - LOG(("App Error: bad decrypted part size: %1, fullLen: %2, decrypted size: %3").arg(dataLen).arg(fullLen).arg(decrypted.size())); - return false; - } - - decrypted.resize(dataLen); - result.data = decrypted; - decrypted = QByteArray(); - - result.buffer.setBuffer(&result.data); - result.buffer.open(QIODevice::ReadOnly); - result.buffer.seek(sizeof(uint32)); // skip len - result.stream.setDevice(&result.buffer); - result.stream.setVersion(QDataStream::Qt_5_1); - - return true; -} - -bool ReadEncryptedFile( - FileReadDescriptor &result, - const QString &name, - FileOwner owner = FileOwner::User, - const MTP::AuthKeyPtr &key = LocalKey) { - if (!ReadFile(result, name, owner)) { - return false; - } - QByteArray encrypted; - result.stream >> encrypted; - - EncryptedDescriptor data; - if (!decryptLocal(data, encrypted, key)) { - result.stream.setDevice(nullptr); - if (result.buffer.isOpen()) result.buffer.close(); - result.buffer.setBuffer(nullptr); - result.data = QByteArray(); - result.version = 0; - return false; - } - - result.stream.setDevice(0); - if (result.buffer.isOpen()) result.buffer.close(); - result.buffer.setBuffer(0); - result.data = data.data; - result.buffer.setBuffer(&result.data); - result.buffer.open(QIODevice::ReadOnly); - result.buffer.seek(data.buffer.pos()); - result.stream.setDevice(&result.buffer); - result.stream.setVersion(QDataStream::Qt_5_1); - - return true; -} - -bool ReadEncryptedFile( - FileReadDescriptor &result, - const FileKey &fkey, - FileOwner owner = FileOwner::User, - const MTP::AuthKeyPtr &key = LocalKey) { - return ReadEncryptedFile(result, toFilePart(fkey), owner, key); -} - -FileKey _dataNameKey = 0; - -enum { // Local Storage Keys - lskUserMap = 0x00, - lskDraft = 0x01, // data: PeerId peer - lskDraftPosition = 0x02, // data: PeerId peer - lskLegacyImages = 0x03, // legacy - lskLocations = 0x04, // no data - lskLegacyStickerImages = 0x05, // legacy - lskLegacyAudios = 0x06, // legacy - lskRecentStickersOld = 0x07, // no data - lskBackgroundOld = 0x08, // no data - lskUserSettings = 0x09, // no data - lskRecentHashtagsAndBots = 0x0a, // no data - lskStickersOld = 0x0b, // no data - lskSavedPeersOld = 0x0c, // no data - lskReportSpamStatusesOld = 0x0d, // no data - lskSavedGifsOld = 0x0e, // no data - lskSavedGifs = 0x0f, // no data - lskStickersKeys = 0x10, // no data - lskTrustedBots = 0x11, // no data - lskFavedStickers = 0x12, // no data - lskExportSettings = 0x13, // no data - lskBackground = 0x14, // no data - lskSelfSerialized = 0x15, // serialized self -}; - -enum { - dbiKey = 0x00, - dbiUser = 0x01, - dbiDcOptionOldOld = 0x02, - dbiChatSizeMax = 0x03, - dbiMutePeer = 0x04, - dbiSendKeyOld = 0x05, - dbiAutoStart = 0x06, - dbiStartMinimized = 0x07, - dbiSoundFlashBounceNotify = 0x08, - dbiWorkMode = 0x09, - dbiSeenTrayTooltip = 0x0a, - dbiDesktopNotify = 0x0b, - dbiAutoUpdate = 0x0c, - dbiLastUpdateCheck = 0x0d, - dbiWindowPosition = 0x0e, - dbiConnectionTypeOld = 0x0f, - // 0x10 reserved - dbiDefaultAttach = 0x11, - dbiCatsAndDogs = 0x12, - dbiReplaceEmojiOld = 0x13, - dbiAskDownloadPath = 0x14, - dbiDownloadPathOld = 0x15, - dbiScaleOld = 0x16, - dbiEmojiTabOld = 0x17, - dbiRecentEmojiOldOld = 0x18, - dbiLoggedPhoneNumber = 0x19, - dbiMutedPeers = 0x1a, - // 0x1b reserved - dbiNotifyView = 0x1c, - dbiSendToMenu = 0x1d, - dbiCompressPastedImage = 0x1e, - dbiLangOld = 0x1f, - dbiLangFileOld = 0x20, - dbiTileBackgroundOld = 0x21, - dbiAutoLock = 0x22, - dbiDialogLastPath = 0x23, - dbiRecentEmojiOld = 0x24, - dbiEmojiVariantsOld = 0x25, - dbiRecentStickers = 0x26, - dbiDcOptionOld = 0x27, - dbiTryIPv6 = 0x28, - dbiSongVolume = 0x29, - dbiWindowsNotificationsOld = 0x30, - dbiIncludeMutedOld = 0x31, - dbiMegagroupSizeMax = 0x32, - dbiDownloadPath = 0x33, - dbiAutoDownloadOld = 0x34, - dbiSavedGifsLimit = 0x35, - dbiShowingSavedGifsOld = 0x36, - dbiAutoPlayOld = 0x37, - dbiAdaptiveForWide = 0x38, - dbiHiddenPinnedMessages = 0x39, - dbiRecentEmoji = 0x3a, - dbiEmojiVariants = 0x3b, - dbiDialogsModeOld = 0x40, - dbiModerateMode = 0x41, - dbiVideoVolume = 0x42, - dbiStickersRecentLimit = 0x43, - dbiNativeNotifications = 0x44, - dbiNotificationsCount = 0x45, - dbiNotificationsCorner = 0x46, - dbiThemeKeyOld = 0x47, - dbiDialogsWidthRatioOld = 0x48, - dbiUseExternalVideoPlayer = 0x49, - dbiDcOptions = 0x4a, - dbiMtpAuthorization = 0x4b, - dbiLastSeenWarningSeenOld = 0x4c, - dbiSessionSettings = 0x4d, - dbiLangPackKey = 0x4e, - dbiConnectionType = 0x4f, - dbiStickersFavedLimit = 0x50, - dbiSuggestStickersByEmojiOld = 0x51, - dbiSuggestEmojiOld = 0x52, - dbiTxtDomainStringOld = 0x53, - dbiThemeKey = 0x54, - dbiTileBackground = 0x55, - dbiCacheSettingsOld = 0x56, - dbiAnimationsDisabled = 0x57, - dbiScalePercent = 0x58, - dbiPlaybackSpeed = 0x59, - dbiLanguagesKey = 0x5a, - dbiCallSettings = 0x5b, - dbiCacheSettings = 0x5c, - dbiTxtDomainString = 0x5d, - dbiApplicationSettings = 0x5e, - dbiDialogsFilters = 0x5f, - - dbiEncryptedWithSalt = 333, - dbiEncrypted = 444, - - // 500-600 reserved - - dbiVersion = 666, -}; - -enum { - dbictAuto = 0, - dbictHttpAuto = 1, // not used - dbictHttpProxy = 2, - dbictTcpProxy = 3, - dbictProxiesListOld = 4, - dbictProxiesList = 5, -}; - -typedef QMap DraftsMap; -DraftsMap _draftsMap, _draftCursorsMap; -typedef QMap DraftsNotReadMap; -DraftsNotReadMap _draftsNotReadMap; - -typedef QPair FileDesc; // file, size - -typedef QMultiMap FileLocations; -FileLocations _fileLocations; -typedef QPair FileLocationPair; -typedef QMap FileLocationPairs; -FileLocationPairs _fileLocationPairs; -typedef QMap FileLocationAliases; -FileLocationAliases _fileLocationAliases; -FileKey _locationsKey = 0, _trustedBotsKey = 0; - -using TrustedBots = OrderedSet; -TrustedBots _trustedBots; -bool _trustedBotsRead = false; - -FileKey _recentStickersKeyOld = 0; -FileKey _installedStickersKey = 0, _featuredStickersKey = 0, _recentStickersKey = 0, _favedStickersKey = 0, _archivedStickersKey = 0; -FileKey _savedGifsKey = 0; - -FileKey _backgroundKeyDay = 0; -FileKey _backgroundKeyNight = 0; -bool _backgroundCanWrite = true; FileKey _themeKeyDay = 0; FileKey _themeKeyNight = 0; @@ -781,31 +77,10 @@ FileKey _themeKeyNight = 0; // But it should be moved to keyDay or keyNight inside InitialLoadTheme() // and never used after. FileKey _themeKeyLegacy = 0; - -bool _readingUserSettings = false; -FileKey _userSettingsKey = 0; -FileKey _recentHashtagsAndBotsKey = 0; -bool _recentHashtagsAndBotsWereRead = false; -qint64 _cacheTotalSizeLimit = Database::Settings().totalSizeLimit; -qint32 _cacheTotalTimeLimit = Database::Settings().totalTimeLimit; -qint64 _cacheBigFileTotalSizeLimit = Database::Settings().totalSizeLimit; -qint32 _cacheBigFileTotalTimeLimit = Database::Settings().totalTimeLimit; - -bool NoTimeLimit(qint32 storedLimitValue) { - // This is a workaround for a bug in storing the cache time limit. - // See https://github.com/telegramdesktop/tdesktop/issues/5611 - return !storedLimitValue - || (storedLimitValue == qint32(std::numeric_limits::max())) - || (storedLimitValue == qint32(std::numeric_limits::max())); -} - -FileKey _exportSettingsKey = 0; - FileKey _langPackKey = 0; FileKey _languagesKey = 0; -bool _mapChanged = false; -int32 _oldMapVersion = 0, _oldSettingsVersion = 0; +int32 _oldSettingsVersion = 0; enum class WriteMapWhen { Now, @@ -821,1152 +96,14 @@ Main::Settings &GetStoredSessionSettings() { return *StoredSessionSettings; } -void _writeMap(WriteMapWhen when = WriteMapWhen::Soon); - -void _writeLocations(WriteMapWhen when = WriteMapWhen::Soon) { - Expects(_manager != nullptr); - - if (when != WriteMapWhen::Now) { - _manager->writeLocations(when == WriteMapWhen::Fast); - return; - } - if (!_working()) return; - - _manager->writingLocations(); - if (_fileLocations.isEmpty()) { - if (_locationsKey) { - ClearKey(_locationsKey); - _locationsKey = 0; - _mapChanged = true; - _writeMap(); - } - } else { - if (!_locationsKey) { - _locationsKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - quint32 size = 0; - for (FileLocations::const_iterator i = _fileLocations.cbegin(), e = _fileLocations.cend(); i != e; ++i) { - // location + type + namelen + name - size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(i.value().name()); - if (AppVersion > 9013) { - // bookmark - size += Serialize::bytearraySize(i.value().bookmark()); - } - // date + size - size += Serialize::dateTimeSize() + sizeof(quint32); - } - - //end mark - size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(QString()); - if (AppVersion > 9013) { - size += Serialize::bytearraySize(QByteArray()); - } - size += Serialize::dateTimeSize() + sizeof(quint32); - - size += sizeof(quint32); // aliases count - for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) { - // alias + location - size += sizeof(quint64) * 2 + sizeof(quint64) * 2; - } - - EncryptedDescriptor data(size); - auto legacyTypeField = 0; - for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) { - data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(legacyTypeField) << i.value().name(); - if (AppVersion > 9013) { - data.stream << i.value().bookmark(); - } - data.stream << i.value().modified << quint32(i.value().size); - } - - data.stream << quint64(0) << quint64(0) << quint32(0) << QString(); - if (AppVersion > 9013) { - data.stream << QByteArray(); - } - data.stream << QDateTime::currentDateTime() << quint32(0); - - data.stream << quint32(_fileLocationAliases.size()); - for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) { - data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second); - } - - FileWriteDescriptor file(_locationsKey); - file.writeEncrypted(data); - } -} - -void _readLocations() { - FileReadDescriptor locations; - if (!ReadEncryptedFile(locations, _locationsKey)) { - ClearKey(_locationsKey); - _locationsKey = 0; - _writeMap(); - return; - } - - bool endMarkFound = false; - while (!locations.stream.atEnd()) { - quint64 first, second; - QByteArray bookmark; - FileLocation loc; - quint32 legacyTypeField = 0; - locations.stream >> first >> second >> legacyTypeField >> loc.fname; - if (locations.version > 9013) { - locations.stream >> bookmark; - } - locations.stream >> loc.modified >> loc.size; - loc.setBookmark(bookmark); - - if (!first && !second && !legacyTypeField && loc.fname.isEmpty() && !loc.size) { // end mark - endMarkFound = true; - break; - } - - MediaKey key(first, second); - - _fileLocations.insert(key, loc); - if (!loc.inMediaCache()) { - _fileLocationPairs.insert(loc.fname, FileLocationPair(key, loc)); - } - } - - if (endMarkFound) { - quint32 cnt; - locations.stream >> cnt; - for (quint32 i = 0; i < cnt; ++i) { - quint64 kfirst, ksecond, vfirst, vsecond; - locations.stream >> kfirst >> ksecond >> vfirst >> vsecond; - _fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond)); - } - - if (!locations.stream.atEnd()) { - quint32 webLocationsCount; - locations.stream >> webLocationsCount; - for (quint32 i = 0; i < webLocationsCount; ++i) { - QString url; - quint64 key; - qint32 size; - locations.stream >> url >> key >> size; - ClearKey(key, FileOwner::User); - } - } - } -} - -struct ReadSettingsContext { - MTP::DcOptions dcOptions; -}; - void applyReadContext(ReadSettingsContext &&context) { Core::App().dcOptions()->addFromOther(std::move(context.dcOptions)); -} -QByteArray serializeCallSettings(){ - QByteArray result=QByteArray(); - uint32 size = 3*sizeof(qint32) + Serialize::stringSize(Global::CallOutputDeviceID()) + Serialize::stringSize(Global::CallInputDeviceID()); - result.reserve(size); - QDataStream stream(&result, QIODevice::WriteOnly); - stream.setVersion(QDataStream::Qt_5_1); - stream << Global::CallOutputDeviceID(); - stream << qint32(Global::CallOutputVolume()); - stream << Global::CallInputDeviceID(); - stream << qint32(Global::CallInputVolume()); - stream << qint32(Global::CallAudioDuckingEnabled() ? 1 : 0); - return result; -} - -void deserializeCallSettings(QByteArray& settings){ - QDataStream stream(&settings, QIODevice::ReadOnly); - stream.setVersion(QDataStream::Qt_5_1); - QString outputDeviceID; - QString inputDeviceID; - qint32 outputVolume; - qint32 inputVolume; - qint32 duckingEnabled; - - stream >> outputDeviceID; - stream >> outputVolume; - stream >> inputDeviceID; - stream >> inputVolume; - stream >> duckingEnabled; - if(_checkStreamStatus(stream)){ - Global::SetCallOutputDeviceID(outputDeviceID); - Global::SetCallOutputVolume(outputVolume); - Global::SetCallInputDeviceID(inputDeviceID); - Global::SetCallInputVolume(inputVolume); - Global::SetCallAudioDuckingEnabled(duckingEnabled); - } -} - -bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSettingsContext &context) { - switch (blockId) { - case dbiDcOptionOldOld: { - quint32 dcId, port; - QString host, ip; - stream >> dcId >> host >> ip >> port; - if (!_checkStreamStatus(stream)) return false; - - context.dcOptions.constructAddOne( - dcId, - 0, - ip.toStdString(), - port, - {}); - } break; - - case dbiDcOptionOld: { - quint32 dcIdWithShift, port; - qint32 flags; - QString ip; - stream >> dcIdWithShift >> flags >> ip >> port; - if (!_checkStreamStatus(stream)) return false; - - context.dcOptions.constructAddOne( - dcIdWithShift, - MTPDdcOption::Flags::from_raw(flags), - ip.toStdString(), - port, - {}); - } break; - - case dbiDcOptions: { - auto serialized = QByteArray(); - stream >> serialized; - if (!_checkStreamStatus(stream)) return false; - - context.dcOptions.constructFromSerialized(serialized); - } break; - - case dbiApplicationSettings: { - auto serialized = QByteArray(); - stream >> serialized; - if (!_checkStreamStatus(stream)) return false; - - Core::App().settings().constructFromSerialized(serialized); - } break; - - case dbiChatSizeMax: { - qint32 maxSize; - stream >> maxSize; - if (!_checkStreamStatus(stream)) return false; - - Global::SetChatSizeMax(maxSize); - } break; - - case dbiSavedGifsLimit: { - qint32 limit; - stream >> limit; - if (!_checkStreamStatus(stream)) return false; - - Global::SetSavedGifsLimit(limit); - } break; - - case dbiStickersRecentLimit: { - qint32 limit; - stream >> limit; - if (!_checkStreamStatus(stream)) return false; - - Global::SetStickersRecentLimit(limit); - } break; - - case dbiStickersFavedLimit: { - qint32 limit; - stream >> limit; - if (!_checkStreamStatus(stream)) return false; - - Global::SetStickersFavedLimit(limit); - } break; - - case dbiMegagroupSizeMax: { - qint32 maxSize; - stream >> maxSize; - if (!_checkStreamStatus(stream)) return false; - - Global::SetMegagroupSizeMax(maxSize); - } break; - - case dbiUser: { - quint32 dcId; - qint32 userId; - stream >> userId >> dcId; - if (!_checkStreamStatus(stream)) return false; - - DEBUG_LOG(("MTP Info: user found, dc %1, uid %2").arg(dcId).arg(userId)); - Core::App().activeAccount().setMtpMainDcId(dcId); - Core::App().activeAccount().setSessionUserId(userId); - } break; - - case dbiKey: { - qint32 dcId; - stream >> dcId; - auto key = Serialize::read(stream); - if (!_checkStreamStatus(stream)) return false; - - Core::App().activeAccount().setMtpKey(dcId, key); - } break; - - case dbiMtpAuthorization: { - auto serialized = QByteArray(); - stream >> serialized; - if (!_checkStreamStatus(stream)) return false; - - Core::App().activeAccount().setMtpAuthorization(serialized); - } break; - - case dbiAutoStart: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetAutoStart(v == 1); - } break; - - case dbiStartMinimized: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetStartMinimized(v == 1); - } break; - - case dbiSendToMenu: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetSendToMenu(v == 1); - } break; - - case dbiUseExternalVideoPlayer: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetUseExternalVideoPlayer(v == 1); - } break; - - case dbiCacheSettingsOld: { - qint64 size; - qint32 time; - stream >> size >> time; - if (!_checkStreamStatus(stream) - || size <= Database::Settings().maxDataSize - || (!NoTimeLimit(time) && time < 0)) { - return false; - } - _cacheTotalSizeLimit = size; - _cacheTotalTimeLimit = NoTimeLimit(time) ? 0 : time; - _cacheBigFileTotalSizeLimit = size; - _cacheBigFileTotalTimeLimit = NoTimeLimit(time) ? 0 : time; - } break; - - case dbiCacheSettings: { - qint64 size, sizeBig; - qint32 time, timeBig; - stream >> size >> time >> sizeBig >> timeBig; - if (!_checkStreamStatus(stream) - || size <= Database::Settings().maxDataSize - || sizeBig <= Database::Settings().maxDataSize - || (!NoTimeLimit(time) && time < 0) - || (!NoTimeLimit(timeBig) && timeBig < 0)) { - return false; - } - - _cacheTotalSizeLimit = size; - _cacheTotalTimeLimit = NoTimeLimit(time) ? 0 : time; - _cacheBigFileTotalSizeLimit = sizeBig; - _cacheBigFileTotalTimeLimit = NoTimeLimit(timeBig) ? 0 : timeBig; - } break; - - case dbiAnimationsDisabled: { - qint32 disabled; - stream >> disabled; - if (!_checkStreamStatus(stream)) { - return false; - } - - anim::SetDisabled(disabled == 1); - } break; - - case dbiSoundFlashBounceNotify: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetSoundNotify((v & 0x01) == 0x01); - Global::SetFlashBounceNotify((v & 0x02) == 0x00); - } break; - - case dbiAutoDownloadOld: { - qint32 photo, audio, gif; - stream >> photo >> audio >> gif; - if (!_checkStreamStatus(stream)) return false; - - using namespace Data::AutoDownload; - auto &settings = GetStoredSessionSettings().autoDownload(); - const auto disabled = [](qint32 value, qint32 mask) { - return (value & mask) != 0; - }; - const auto set = [&](Type type, qint32 value) { - constexpr auto kNoPrivate = qint32(0x01); - constexpr auto kNoGroups = qint32(0x02); - if (disabled(value, kNoPrivate)) { - settings.setBytesLimit(Source::User, type, 0); - } - if (disabled(value, kNoGroups)) { - settings.setBytesLimit(Source::Group, type, 0); - settings.setBytesLimit(Source::Channel, type, 0); - } - }; - set(Type::Photo, photo); - set(Type::VoiceMessage, audio); - set(Type::AutoPlayGIF, gif); - set(Type::AutoPlayVideoMessage, gif); - } break; - - case dbiAutoPlayOld: { - qint32 gif; - stream >> gif; - if (!_checkStreamStatus(stream)) return false; - - if (!gif) { - using namespace Data::AutoDownload; - auto &settings = GetStoredSessionSettings().autoDownload(); - const auto types = { - Type::AutoPlayGIF, - Type::AutoPlayVideo, - Type::AutoPlayVideoMessage, - }; - const auto sources = { - Source::User, - Source::Group, - Source::Channel - }; - for (const auto source : sources) { - for (const auto type : types) { - settings.setBytesLimit(source, type, 0); - } - } - } - } break; - - case dbiDialogsModeOld: { - qint32 enabled, modeInt; - stream >> enabled >> modeInt; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiDialogsFilters: { - qint32 enabled; - stream >> enabled; - if (!_checkStreamStatus(stream)) return false; - - Global::SetDialogsFiltersEnabled(enabled == 1); - } break; - - case dbiModerateMode: { - qint32 enabled; - stream >> enabled; - if (!_checkStreamStatus(stream)) return false; - - Global::SetModerateModeEnabled(enabled == 1); - } break; - - case dbiIncludeMutedOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().setIncludeMutedCounter(v == 1); - } break; - - case dbiShowingSavedGifsOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiDesktopNotify: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetDesktopNotify(v == 1); - if (App::wnd()) App::wnd()->updateTrayMenu(); - } break; - - case dbiWindowsNotificationsOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiNativeNotifications: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetNativeNotifications(v == 1); - } break; - - case dbiNotificationsCount: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetNotificationsCount((v > 0 ? v : 3)); - } break; - - case dbiNotificationsCorner: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetNotificationsCorner(static_cast((v >= 0 && v < 4) ? v : 2)); - } break; - - case dbiDialogsWidthRatioOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().setDialogsWidthRatio(v / 1000000.); - } break; - - case dbiLastSeenWarningSeenOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().setLastSeenWarningSeen(v == 1); - } break; - - case dbiSessionSettings: { - QByteArray v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().constructFromSerialized(v); - } break; - - case dbiWorkMode: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - auto newMode = [v] { - switch (v) { - case dbiwmTrayOnly: return dbiwmTrayOnly; - case dbiwmWindowOnly: return dbiwmWindowOnly; - }; - return dbiwmWindowAndTray; - }; - Global::RefWorkMode().set(newMode()); - } break; - - case dbiTxtDomainStringOld: { - QString v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiTxtDomainString: { - QString v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetTxtDomainString(v); - } break; - - case dbiConnectionTypeOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - MTP::ProxyData proxy; - switch (v) { - case dbictHttpProxy: - case dbictTcpProxy: { - qint32 port; - stream >> proxy.host >> port >> proxy.user >> proxy.password; - if (!_checkStreamStatus(stream)) return false; - - proxy.port = uint32(port); - proxy.type = (v == dbictTcpProxy) - ? MTP::ProxyData::Type::Socks5 - : MTP::ProxyData::Type::Http; - } break; - }; - Global::SetSelectedProxy(proxy ? proxy : MTP::ProxyData()); - Global::SetProxySettings(proxy - ? MTP::ProxyData::Settings::Enabled - : MTP::ProxyData::Settings::System); - if (proxy) { - Global::SetProxiesList({ 1, proxy }); - } else { - Global::SetProxiesList({}); - } - Core::App().refreshGlobalProxy(); - } break; - - case dbiConnectionType: { - qint32 connectionType; - stream >> connectionType; - if (!_checkStreamStatus(stream)) { - return false; - } - - const auto readProxy = [&] { - qint32 proxyType, port; - MTP::ProxyData proxy; - stream >> proxyType >> proxy.host >> port >> proxy.user >> proxy.password; - proxy.port = port; - proxy.type = (proxyType == dbictTcpProxy) - ? MTP::ProxyData::Type::Socks5 - : (proxyType == dbictHttpProxy) - ? MTP::ProxyData::Type::Http - : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Socks5)) - ? MTP::ProxyData::Type::Socks5 - : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Http)) - ? MTP::ProxyData::Type::Http - : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Mtproto)) - ? MTP::ProxyData::Type::Mtproto - : MTP::ProxyData::Type::None; - return proxy; - }; - if (connectionType == dbictProxiesListOld - || connectionType == dbictProxiesList) { - qint32 count = 0, index = 0; - stream >> count >> index; - qint32 settings = 0, calls = 0; - if (connectionType == dbictProxiesList) { - stream >> settings >> calls; - } else if (std::abs(index) > count) { - calls = 1; - index -= (index > 0 ? count : -count); - } - - auto list = std::vector(); - for (auto i = 0; i < count; ++i) { - const auto proxy = readProxy(); - if (proxy) { - list.push_back(proxy); - } else if (index < -list.size()) { - ++index; - } else if (index > list.size()) { - --index; - } - } - if (!_checkStreamStatus(stream)) { - return false; - } - Global::SetProxiesList(list); - if (connectionType == dbictProxiesListOld) { - settings = static_cast( - (index > 0 && index <= list.size() - ? MTP::ProxyData::Settings::Enabled - : MTP::ProxyData::Settings::System)); - index = std::abs(index); - } - if (index > 0 && index <= list.size()) { - Global::SetSelectedProxy(list[index - 1]); - } else { - Global::SetSelectedProxy(MTP::ProxyData()); - } - - const auto unchecked = static_cast(settings); - switch (unchecked) { - case MTP::ProxyData::Settings::Enabled: - Global::SetProxySettings(Global::SelectedProxy() - ? MTP::ProxyData::Settings::Enabled - : MTP::ProxyData::Settings::System); - break; - case MTP::ProxyData::Settings::Disabled: - case MTP::ProxyData::Settings::System: - Global::SetProxySettings(unchecked); - break; - default: - Global::SetProxySettings(MTP::ProxyData::Settings::System); - break; - } - Global::SetUseProxyForCalls(calls == 1); - } else { - const auto proxy = readProxy(); - if (!_checkStreamStatus(stream)) { - return false; - } - if (proxy) { - Global::SetProxiesList({ 1, proxy }); - Global::SetSelectedProxy(proxy); - if (connectionType == dbictTcpProxy - || connectionType == dbictHttpProxy) { - Global::SetProxySettings(MTP::ProxyData::Settings::Enabled); - } else { - Global::SetProxySettings(MTP::ProxyData::Settings::System); - } - } else { - Global::SetProxiesList({}); - Global::SetSelectedProxy(MTP::ProxyData()); - Global::SetProxySettings(MTP::ProxyData::Settings::System); - } - } - Core::App().refreshGlobalProxy(); - } break; - - case dbiThemeKeyOld: { - quint64 key = 0; - stream >> key; - if (!_checkStreamStatus(stream)) return false; - - _themeKeyLegacy = key; - } break; - - case dbiThemeKey: { - quint64 keyDay = 0, keyNight = 0; - quint32 nightMode = 0; - stream >> keyDay >> keyNight >> nightMode; - if (!_checkStreamStatus(stream)) return false; - - _themeKeyDay = keyDay; - _themeKeyNight = keyNight; - Window::Theme::SetNightModeValue(nightMode == 1); - } break; - - case dbiLangPackKey: { - quint64 langPackKey = 0; - stream >> langPackKey; - if (!_checkStreamStatus(stream)) return false; - - _langPackKey = langPackKey; - } break; - - case dbiLanguagesKey: { - quint64 languagesKey = 0; - stream >> languagesKey; - if (!_checkStreamStatus(stream)) return false; - - _languagesKey = languagesKey; - } break; - - case dbiTryIPv6: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetTryIPv6(v == 1); - } break; - - case dbiSeenTrayTooltip: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetSeenTrayTooltip(v == 1); - } break; - - case dbiAutoUpdate: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetAutoUpdate(v == 1); - if (!Core::UpdaterDisabled() && !cAutoUpdate()) { - Core::UpdateChecker().stop(); - } - } break; - - case dbiLastUpdateCheck: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetLastUpdateCheck(v); - } break; - - case dbiScaleOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - SetScaleChecked([&] { - constexpr auto kAuto = 0; - constexpr auto kOne = 1; - constexpr auto kOneAndQuarter = 2; - constexpr auto kOneAndHalf = 3; - constexpr auto kTwo = 4; - switch (v) { - case kAuto: return style::kScaleAuto; - case kOne: return 100; - case kOneAndQuarter: return 125; - case kOneAndHalf: return 150; - case kTwo: return 200; - } - return cConfigScale(); - }()); - } break; - - case dbiScalePercent: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - // If cConfigScale() has value then it was set via command line. - if (cConfigScale() == style::kScaleAuto) { - SetScaleChecked(v); - } - } break; - - case dbiLangOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiLangFileOld: { - QString v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiWindowPosition: { - auto position = TWindowPos(); - stream >> position.x >> position.y >> position.w >> position.h; - stream >> position.moncrc >> position.maximized; - if (!_checkStreamStatus(stream)) return false; - - DEBUG_LOG(("Window Pos: Read from storage %1, %2, %3, %4 (maximized %5)").arg(position.x).arg(position.y).arg(position.w).arg(position.h).arg(Logs::b(position.maximized))); - cSetWindowPos(position); - } break; - - case dbiLoggedPhoneNumber: { - QString v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetLoggedPhoneNumber(v); - } break; - - case dbiMutePeer: { // deprecated - quint64 peerId; - stream >> peerId; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiMutedPeers: { // deprecated - quint32 count; - stream >> count; - if (!_checkStreamStatus(stream)) return false; - - for (uint32 i = 0; i < count; ++i) { - quint64 peerId; - stream >> peerId; - } - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiSendKeyOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - using SendSettings = Ui::InputSubmitSettings; - const auto unchecked = static_cast(v); - - if (unchecked != SendSettings::Enter - && unchecked != SendSettings::CtrlEnter) { - return false; - } - GetStoredSessionSettings().setSendSubmitWay(unchecked); - } break; - - case dbiCatsAndDogs: { // deprecated - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiTileBackgroundOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - bool tile = (version < 8005 && !_backgroundKeyDay) - ? false - : (v == 1); - if (Window::Theme::IsNightMode()) { - Window::Theme::Background()->setTileNightValue(tile); - } else { - Window::Theme::Background()->setTileDayValue(tile); - } - } break; - - case dbiTileBackground: { - qint32 tileDay, tileNight; - stream >> tileDay >> tileNight; - if (!_checkStreamStatus(stream)) return false; - - Window::Theme::Background()->setTileDayValue(tileDay == 1); - Window::Theme::Background()->setTileNightValue(tileNight == 1); - } break; - - case dbiAdaptiveForWide: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetAdaptiveForWide(v == 1); - } break; - - case dbiAutoLock: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetAutoLock(v); - Global::RefLocalPasscodeChanged().notify(); - } break; - - case dbiReplaceEmojiOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().setReplaceEmoji(v == 1); - } break; - - case dbiSuggestEmojiOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().setSuggestEmoji(v == 1); - } break; - - case dbiSuggestStickersByEmojiOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().setSuggestStickersByEmoji(v == 1); - } break; - - case dbiDefaultAttach: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - } break; - - case dbiNotifyView: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - switch (v) { - case dbinvShowNothing: Global::SetNotifyView(dbinvShowNothing); break; - case dbinvShowName: Global::SetNotifyView(dbinvShowName); break; - default: Global::SetNotifyView(dbinvShowPreview); break; - } - } break; - - case dbiAskDownloadPath: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetAskDownloadPath(v == 1); - } break; - - case dbiDownloadPathOld: { - QString v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; -#ifndef OS_WIN_STORE - if (!v.isEmpty() && v != qstr("tmp") && !v.endsWith('/')) v += '/'; - Global::SetDownloadPath(v); - Global::SetDownloadPathBookmark(QByteArray()); - Global::RefDownloadPathChanged().notify(); -#endif // OS_WIN_STORE - } break; - - case dbiDownloadPath: { - QString v; - QByteArray bookmark; - stream >> v >> bookmark; - if (!_checkStreamStatus(stream)) return false; -#ifndef OS_WIN_STORE - if (!v.isEmpty() && v != qstr("tmp") && !v.endsWith('/')) v += '/'; - Global::SetDownloadPath(v); - Global::SetDownloadPathBookmark(bookmark); - psDownloadPathEnableAccess(); - Global::RefDownloadPathChanged().notify(); -#endif // OS_WIN_STORE - } break; - - case dbiCompressPastedImage: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - GetStoredSessionSettings().setSendFilesWay((v == 1) - ? SendFilesWay::Album - : SendFilesWay::Files); - } break; - - case dbiEmojiTabOld: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - // deprecated - } break; - - case dbiRecentEmojiOldOld: { - RecentEmojiPreloadOldOld v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - if (!v.isEmpty()) { - RecentEmojiPreload p; - p.reserve(v.size()); - for (auto &item : v) { - auto oldKey = uint64(item.first); - switch (oldKey) { - case 0xD83CDDEFLLU: oldKey = 0xD83CDDEFD83CDDF5LLU; break; - case 0xD83CDDF0LLU: oldKey = 0xD83CDDF0D83CDDF7LLU; break; - case 0xD83CDDE9LLU: oldKey = 0xD83CDDE9D83CDDEALLU; break; - case 0xD83CDDE8LLU: oldKey = 0xD83CDDE8D83CDDF3LLU; break; - case 0xD83CDDFALLU: oldKey = 0xD83CDDFAD83CDDF8LLU; break; - case 0xD83CDDEBLLU: oldKey = 0xD83CDDEBD83CDDF7LLU; break; - case 0xD83CDDEALLU: oldKey = 0xD83CDDEAD83CDDF8LLU; break; - case 0xD83CDDEELLU: oldKey = 0xD83CDDEED83CDDF9LLU; break; - case 0xD83CDDF7LLU: oldKey = 0xD83CDDF7D83CDDFALLU; break; - case 0xD83CDDECLLU: oldKey = 0xD83CDDECD83CDDE7LLU; break; - } - auto id = Ui::Emoji::IdFromOldKey(oldKey); - if (!id.isEmpty()) { - p.push_back(qMakePair(id, item.second)); - } - } - cSetRecentEmojiPreload(p); - } - } break; - - case dbiRecentEmojiOld: { - RecentEmojiPreloadOld v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - if (!v.isEmpty()) { - RecentEmojiPreload p; - p.reserve(v.size()); - for (auto &item : v) { - auto id = Ui::Emoji::IdFromOldKey(item.first); - if (!id.isEmpty()) { - p.push_back(qMakePair(id, item.second)); - } - } - cSetRecentEmojiPreload(p); - } - } break; - - case dbiRecentEmoji: { - RecentEmojiPreload v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetRecentEmojiPreload(v); - } break; - - case dbiRecentStickers: { - RecentStickerPreload v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetRecentStickersPreload(v); - } break; - - case dbiEmojiVariantsOld: { - EmojiColorVariantsOld v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - EmojiColorVariants variants; - for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) { - auto id = Ui::Emoji::IdFromOldKey(static_cast(i.key())); - if (!id.isEmpty()) { - auto index = Ui::Emoji::ColorIndexFromOldKey(i.value()); - if (index >= 0) { - variants.insert(id, index); - } - } - } - cSetEmojiVariants(variants); - } break; - - case dbiEmojiVariants: { - EmojiColorVariants v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - cSetEmojiVariants(v); - } break; - - case dbiHiddenPinnedMessages: { - Global::HiddenPinnedMessagesMap v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetHiddenPinnedMessages(v); - } break; - - case dbiDialogLastPath: { - QString path; - stream >> path; - if (!_checkStreamStatus(stream)) return false; - - cSetDialogLastPath(path); - } break; - - case dbiSongVolume: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetSongVolume(snap(v / 1e6, 0., 1.)); - } break; - - case dbiVideoVolume: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetVideoVolume(snap(v / 1e6, 0., 1.)); - } break; - - case dbiPlaybackSpeed: { - qint32 v; - stream >> v; - if (!_checkStreamStatus(stream)) return false; - - Global::SetVoiceMsgPlaybackDoubled(v == 2); - } break; - - case dbiCallSettings: { - QByteArray callSettings; - stream >> callSettings; - if(!_checkStreamStatus(stream)) return false; - - deserializeCallSettings(callSettings); - } break; - - default: - LOG(("App Error: unknown blockId in _readSetting: %1").arg(blockId)); - return false; - } - - return true; + _themeKeyLegacy = context.themeKeyLegacy; + _themeKeyDay = context.themeKeyDay; + _themeKeyNight = context.themeKeyNight; + _langPackKey = context.langPackKey; + _languagesKey = context.languagesKey; } bool _readOldSettings(bool remove, ReadSettingsContext &context) { @@ -1981,14 +118,14 @@ bool _readOldSettings(bool remove, ReadSettingsContext &context) { while (!stream.atEnd()) { quint32 blockId; stream >> blockId; - if (!_checkStreamStatus(stream)) break; + if (!CheckStreamStatus(stream)) break; if (blockId == dbiVersion) { stream >> version; - if (!_checkStreamStatus(stream)) break; + if (!CheckStreamStatus(stream)) break; if (version > AppVersion) break; - } else if (!_readSetting(blockId, stream, version, context)) { + } else if (!ReadSetting(blockId, stream, version, context)) { break; } } @@ -1999,20 +136,23 @@ bool _readOldSettings(bool remove, ReadSettingsContext &context) { return result; } -void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettingsContext &context) { +void _readOldUserSettingsFields( + QIODevice *device, + qint32 &version, + ReadSettingsContext &context) { QDataStream stream(device); stream.setVersion(QDataStream::Qt_5_1); while (!stream.atEnd()) { quint32 blockId; stream >> blockId; - if (!_checkStreamStatus(stream)) { + if (!CheckStreamStatus(stream)) { break; } if (blockId == dbiVersion) { stream >> version; - if (!_checkStreamStatus(stream)) { + if (!CheckStreamStatus(stream)) { break; } @@ -2020,7 +160,7 @@ void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettings } else if (blockId == dbiEncryptedWithSalt) { QByteArray salt, data, decrypted; stream >> salt >> data; - if (!_checkStreamStatus(stream)) { + if (!CheckStreamStatus(stream)) { break; } @@ -2029,7 +169,7 @@ void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettings continue; } - createLocalKey(QByteArray(), &salt, &OldKey); + OldKey = CreateLocalKey(QByteArray(), salt); if (data.size() <= 16 || (data.size() & 0x0F)) { LOG(("App Error: bad encrypted part size in old user config: %1").arg(data.size())); @@ -2056,7 +196,7 @@ void _readOldUserSettingsFields(QIODevice *device, qint32 &version, ReadSettings LOG(("App Info: reading encrypted old user config...")); _readOldUserSettingsFields(&decryptedStream, version, context); - } else if (!_readSetting(blockId, stream, version, context)) { + } else if (!ReadSetting(blockId, stream, version, context)) { return; } } @@ -2076,20 +216,23 @@ bool _readOldUserSettings(bool remove, ReadSettingsContext &context) { return result; } -void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsContext &context) { +void _readOldMtpDataFields( + QIODevice *device, + qint32 &version, + ReadSettingsContext &context) { QDataStream stream(device); stream.setVersion(QDataStream::Qt_5_1); while (!stream.atEnd()) { quint32 blockId; stream >> blockId; - if (!_checkStreamStatus(stream)) { + if (!CheckStreamStatus(stream)) { break; } if (blockId == dbiVersion) { stream >> version; - if (!_checkStreamStatus(stream)) { + if (!CheckStreamStatus(stream)) { break; } @@ -2097,7 +240,7 @@ void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsConte } else if (blockId == dbiEncrypted) { QByteArray data, decrypted; stream >> data; - if (!_checkStreamStatus(stream)) { + if (!CheckStreamStatus(stream)) { break; } @@ -2131,7 +274,7 @@ void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsConte LOG(("App Info: reading encrypted old keys...")); _readOldMtpDataFields(&decryptedStream, version, context); - } else if (!_readSetting(blockId, stream, version, context)) { + } else if (!ReadSetting(blockId, stream, version, context)) { return; } } @@ -2139,7 +282,8 @@ void _readOldMtpDataFields(QIODevice *device, qint32 &version, ReadSettingsConte bool _readOldMtpData(bool remove, ReadSettingsContext &context) { bool result = false; - QFile file(cWorkingDir() + cDataFile() + (cTestMode() ? qsl("_test") : QString())); + const auto testPostfix = (cTestMode() ? qsl("_test") : QString()); + QFile file(cWorkingDir() + cDataFile() + testPostfix); if (file.open(QIODevice::ReadOnly)) { LOG(("App Info: reading old keys...")); qint32 version = 0; @@ -2151,524 +295,10 @@ bool _readOldMtpData(bool remove, ReadSettingsContext &context) { return result; } -void _writeUserSettings() { - if (!_userWorking()) { - LOG(("App Error: attempt to write user settings too early!")); - return; - } else if (_readingUserSettings) { - LOG(("App Error: attempt to write settings while reading them!")); - return; - } - LOG(("App Info: writing encrypted user settings...")); - - if (!_userSettingsKey) { - _userSettingsKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - - auto recentEmojiPreloadData = cRecentEmojiPreload(); - if (recentEmojiPreloadData.isEmpty()) { - recentEmojiPreloadData.reserve(GetRecentEmoji().size()); - for (auto &item : GetRecentEmoji()) { - recentEmojiPreloadData.push_back(qMakePair(item.first->id(), item.second)); - } - } - auto userDataInstance = StoredSessionSettings - ? StoredSessionSettings.get() - : Core::App().activeAccount().getSessionSettings(); - auto userData = userDataInstance - ? userDataInstance->serialize() - : QByteArray(); - auto callSettings = serializeCallSettings(); - - auto recentStickers = cRecentStickersPreload(); - if (recentStickers.isEmpty() && Main::Session::Exists()) { - recentStickers.reserve(Auth().data().stickers().getRecentPack().size()); - for (const auto &pair : std::as_const(Auth().data().stickers().getRecentPack())) { - recentStickers.push_back(qMakePair(pair.first->id, pair.second)); - } - } - - uint32 size = 24 * (sizeof(quint32) + sizeof(qint32)); - size += sizeof(quint32) + Serialize::stringSize(Global::AskDownloadPath() ? QString() : Global::DownloadPath()) + Serialize::bytearraySize(Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark()); - - size += sizeof(quint32) + sizeof(qint32); - for (auto &item : recentEmojiPreloadData) { - size += Serialize::stringSize(item.first) + sizeof(item.second); - } - - size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64)); - size += sizeof(quint32) + sizeof(qint32) + recentStickers.size() * (sizeof(uint64) + sizeof(ushort)); - size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath()); - size += sizeof(quint32) + 3 * sizeof(qint32); - size += sizeof(quint32) + 2 * sizeof(qint32); - size += sizeof(quint32) + sizeof(qint64) + sizeof(qint32); - if (!Global::HiddenPinnedMessages().isEmpty()) { - size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId)); - } - if (!userData.isEmpty()) { - size += sizeof(quint32) + Serialize::bytearraySize(userData); - } - size += sizeof(quint32) + Serialize::bytearraySize(callSettings); - - const auto soundFlashBounce = (Global::SoundNotify() ? 0x01 : 0x00) - | (Global::FlashBounceNotify() ? 0x00 : 0x02); - - EncryptedDescriptor data(size); - data.stream - << quint32(dbiTileBackground) - << qint32(Window::Theme::Background()->tileDay() ? 1 : 0) - << qint32(Window::Theme::Background()->tileNight() ? 1 : 0); - data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0); - data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock()); - data.stream << quint32(dbiSoundFlashBounceNotify) << qint32(soundFlashBounce); - data.stream << quint32(dbiDesktopNotify) << qint32(Global::DesktopNotify()); - data.stream << quint32(dbiNotifyView) << qint32(Global::NotifyView()); - data.stream << quint32(dbiNativeNotifications) << qint32(Global::NativeNotifications()); - data.stream << quint32(dbiNotificationsCount) << qint32(Global::NotificationsCount()); - data.stream << quint32(dbiNotificationsCorner) << qint32(Global::NotificationsCorner()); - data.stream << quint32(dbiAskDownloadPath) << qint32(Global::AskDownloadPath()); - data.stream << quint32(dbiDownloadPath) << (Global::AskDownloadPath() ? QString() : Global::DownloadPath()) << (Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark()); - data.stream << quint32(dbiDialogLastPath) << cDialogLastPath(); - data.stream << quint32(dbiSongVolume) << qint32(qRound(Global::SongVolume() * 1e6)); - data.stream << quint32(dbiVideoVolume) << qint32(qRound(Global::VideoVolume() * 1e6)); - data.stream << quint32(dbiDialogsFilters) << qint32(Global::DialogsFiltersEnabled() ? 1 : 0); - data.stream << quint32(dbiModerateMode) << qint32(Global::ModerateModeEnabled() ? 1 : 0); - data.stream << quint32(dbiUseExternalVideoPlayer) << qint32(cUseExternalVideoPlayer()); - data.stream << quint32(dbiCacheSettings) << qint64(_cacheTotalSizeLimit) << qint32(_cacheTotalTimeLimit) << qint64(_cacheBigFileTotalSizeLimit) << qint32(_cacheBigFileTotalTimeLimit); - if (!userData.isEmpty()) { - data.stream << quint32(dbiSessionSettings) << userData; - } - data.stream << quint32(dbiPlaybackSpeed) << qint32(Global::VoiceMsgPlaybackDoubled() ? 2 : 1); - data.stream << quint32(dbiRecentEmoji) << recentEmojiPreloadData; - data.stream << quint32(dbiEmojiVariants) << cEmojiVariants(); - data.stream << quint32(dbiRecentStickers) << recentStickers; - if (!Global::HiddenPinnedMessages().isEmpty()) { - data.stream << quint32(dbiHiddenPinnedMessages) << Global::HiddenPinnedMessages(); - } - data.stream << qint32(dbiCallSettings) << callSettings; - - FileWriteDescriptor file(_userSettingsKey); - file.writeEncrypted(data); -} - -void _readUserSettings() { - ReadSettingsContext context; - FileReadDescriptor userSettings; - if (!ReadEncryptedFile(userSettings, _userSettingsKey)) { - LOG(("App Info: could not read encrypted user settings...")); - - _readOldUserSettings(true, context); - applyReadContext(std::move(context)); - - return _writeUserSettings(); - } - - LOG(("App Info: reading encrypted user settings...")); - _readingUserSettings = true; - while (!userSettings.stream.atEnd()) { - quint32 blockId; - userSettings.stream >> blockId; - if (!_checkStreamStatus(userSettings.stream)) { - _readingUserSettings = false; - return _writeUserSettings(); - } - - if (!_readSetting(blockId, userSettings.stream, userSettings.version, context)) { - _readingUserSettings = false; - return _writeUserSettings(); - } - } - _readingUserSettings = false; - LOG(("App Info: encrypted user settings read.")); - - applyReadContext(std::move(context)); -} - -void _writeMtpData() { - FileWriteDescriptor mtp(toFilePart(_dataNameKey), FileOwner::Global); - if (!LocalKey) { - LOG(("App Error: localkey not created in _writeMtpData()")); - return; - } - - auto mtpAuthorizationSerialized = Core::App().activeAccount().serializeMtpAuthorization(); - - quint32 size = sizeof(quint32) + Serialize::bytearraySize(mtpAuthorizationSerialized); - - EncryptedDescriptor data(size); - data.stream << quint32(dbiMtpAuthorization) << mtpAuthorizationSerialized; - mtp.writeEncrypted(data); -} - -void _readMtpData() { - ReadSettingsContext context; - FileReadDescriptor mtp; - if (!ReadEncryptedFile(mtp, toFilePart(_dataNameKey), FileOwner::Global)) { - if (LocalKey) { - _readOldMtpData(true, context); - applyReadContext(std::move(context)); - - _writeMtpData(); - } - return; - } - - LOG(("App Info: reading encrypted mtp data...")); - while (!mtp.stream.atEnd()) { - quint32 blockId; - mtp.stream >> blockId; - if (!_checkStreamStatus(mtp.stream)) { - return _writeMtpData(); - } - - if (!_readSetting(blockId, mtp.stream, mtp.version, context)) { - return _writeMtpData(); - } - } - applyReadContext(std::move(context)); -} - -ReadMapState _readMap(const QByteArray &pass) { - auto ms = crl::now(); - QByteArray dataNameUtf8 = (cDataFile() + (cTestMode() ? qsl(":/test/") : QString())).toUtf8(); - FileKey dataNameHash[2]; - hashMd5(dataNameUtf8.constData(), dataNameUtf8.size(), dataNameHash); - _dataNameKey = dataNameHash[0]; - _userBasePath = _basePath + toFilePart(_dataNameKey) + QChar('/'); - _userDbPath = _basePath - + "user_" + cDataFile() - + (cTestMode() ? "[test]" : "") - + '/'; - - FileReadDescriptor mapData; - if (!ReadFile(mapData, qsl("map"))) { - return ReadMapFailed; - } - LOG(("App Info: reading map...")); - - QByteArray salt, keyEncrypted, mapEncrypted; - mapData.stream >> salt >> keyEncrypted >> mapEncrypted; - if (!_checkStreamStatus(mapData.stream)) { - return ReadMapFailed; - } - - if (salt.size() != LocalEncryptSaltSize) { - LOG(("App Error: bad salt in map file, size: %1").arg(salt.size())); - return ReadMapFailed; - } - createLocalKey(pass, &salt, &PassKey); - - EncryptedDescriptor keyData, map; - if (!decryptLocal(keyData, keyEncrypted, PassKey)) { - LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password...")); - return ReadMapPassNeeded; - } - auto key = Serialize::read(keyData.stream); - if (keyData.stream.status() != QDataStream::Ok || !keyData.stream.atEnd()) { - LOG(("App Error: could not read pass-protected key from map file")); - return ReadMapFailed; - } - LocalKey = std::make_shared(key); - - _passKeyEncrypted = keyEncrypted; - _passKeySalt = salt; - - if (!decryptLocal(map, mapEncrypted)) { - LOG(("App Error: could not decrypt map.")); - return ReadMapFailed; - } - LOG(("App Info: reading encrypted map...")); - - QByteArray selfSerialized; - DraftsMap draftsMap, draftCursorsMap; - DraftsNotReadMap draftsNotReadMap; - quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedBotsKey = 0; - quint64 recentStickersKeyOld = 0; - quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0; - quint64 savedGifsKey = 0; - quint64 backgroundKeyDay = 0, backgroundKeyNight = 0; - quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0; - while (!map.stream.atEnd()) { - quint32 keyType; - map.stream >> keyType; - switch (keyType) { - case lskDraft: { - quint32 count = 0; - map.stream >> count; - for (quint32 i = 0; i < count; ++i) { - FileKey key; - quint64 p; - map.stream >> key >> p; - draftsMap.insert(p, key); - draftsNotReadMap.insert(p, true); - } - } break; - case lskSelfSerialized: { - map.stream >> selfSerialized; - } break; - case lskDraftPosition: { - quint32 count = 0; - map.stream >> count; - for (quint32 i = 0; i < count; ++i) { - FileKey key; - quint64 p; - map.stream >> key >> p; - draftCursorsMap.insert(p, key); - } - } break; - case lskLegacyImages: - case lskLegacyStickerImages: - case lskLegacyAudios: { - quint32 count = 0; - map.stream >> count; - for (quint32 i = 0; i < count; ++i) { - FileKey key; - quint64 first, second; - qint32 size; - map.stream >> key >> first >> second >> size; - // Just ignore the key, it will be removed as a leaked one. - } - } break; - case lskLocations: { - map.stream >> locationsKey; - } break; - case lskReportSpamStatusesOld: { - map.stream >> reportSpamStatusesKey; - ClearKey(reportSpamStatusesKey); - } break; - case lskTrustedBots: { - map.stream >> trustedBotsKey; - } break; - case lskRecentStickersOld: { - map.stream >> recentStickersKeyOld; - } break; - case lskBackgroundOld: { - map.stream >> (Window::Theme::IsNightMode() - ? backgroundKeyNight - : backgroundKeyDay); - } break; - case lskBackground: { - map.stream >> backgroundKeyDay >> backgroundKeyNight; - } break; - case lskUserSettings: { - map.stream >> userSettingsKey; - } break; - case lskRecentHashtagsAndBots: { - map.stream >> recentHashtagsAndBotsKey; - } break; - case lskStickersOld: { - map.stream >> installedStickersKey; - } break; - case lskStickersKeys: { - map.stream >> installedStickersKey >> featuredStickersKey >> recentStickersKey >> archivedStickersKey; - } break; - case lskFavedStickers: { - map.stream >> favedStickersKey; - } break; - case lskSavedGifsOld: { - quint64 key; - map.stream >> key; - } break; - case lskSavedGifs: { - map.stream >> savedGifsKey; - } break; - case lskSavedPeersOld: { - quint64 key; - map.stream >> key; - } break; - case lskExportSettings: { - map.stream >> exportSettingsKey; - } break; - default: - LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType)); - return ReadMapFailed; - } - if (!_checkStreamStatus(map.stream)) { - return ReadMapFailed; - } - } - - _draftsMap = draftsMap; - _draftCursorsMap = draftCursorsMap; - _draftsNotReadMap = draftsNotReadMap; - - _locationsKey = locationsKey; - _trustedBotsKey = trustedBotsKey; - _recentStickersKeyOld = recentStickersKeyOld; - _installedStickersKey = installedStickersKey; - _featuredStickersKey = featuredStickersKey; - _recentStickersKey = recentStickersKey; - _favedStickersKey = favedStickersKey; - _archivedStickersKey = archivedStickersKey; - _savedGifsKey = savedGifsKey; - _backgroundKeyDay = backgroundKeyDay; - _backgroundKeyNight = backgroundKeyNight; - _userSettingsKey = userSettingsKey; - _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey; - _exportSettingsKey = exportSettingsKey; - _oldMapVersion = mapData.version; - if (_oldMapVersion < AppVersion) { - _mapChanged = true; - _writeMap(); - } else { - _mapChanged = false; - } - - if (_locationsKey) { - _readLocations(); - } - - _readUserSettings(); - _readMtpData(); - - DEBUG_LOG(("selfSerialized set: %1").arg(selfSerialized.size())); - Core::App().activeAccount().setSessionFromStorage( - std::move(StoredSessionSettings), - std::move(selfSerialized), - _oldMapVersion); - - LOG(("Map read time: %1").arg(crl::now() - ms)); - if (_oldSettingsVersion < AppVersion) { - writeSettings(); - } - return ReadMapDone; -} - -void _writeMap(WriteMapWhen when) { - Expects(_manager != nullptr); - - if (when != WriteMapWhen::Now) { - _manager->writeMap(when == WriteMapWhen::Fast); - return; - } - _manager->writingMap(); - if (!_mapChanged) return; - if (_userBasePath.isEmpty()) { - LOG(("App Error: _userBasePath is empty in writeMap()")); - return; - } - - if (!QDir().exists(_userBasePath)) QDir().mkpath(_userBasePath); - - FileWriteDescriptor map(qsl("map")); - if (_passKeySalt.isEmpty() || _passKeyEncrypted.isEmpty()) { - QByteArray pass(kLocalKeySize, Qt::Uninitialized), salt(LocalEncryptSaltSize, Qt::Uninitialized); - memset_rand(pass.data(), pass.size()); - memset_rand(salt.data(), salt.size()); - createLocalKey(pass, &salt, &LocalKey); - - _passKeySalt.resize(LocalEncryptSaltSize); - memset_rand(_passKeySalt.data(), _passKeySalt.size()); - createLocalKey(QByteArray(), &_passKeySalt, &PassKey); - - EncryptedDescriptor passKeyData(kLocalKeySize); - LocalKey->write(passKeyData.stream); - _passKeyEncrypted = PrepareEncrypted(passKeyData, PassKey); - } - map.writeData(_passKeySalt); - map.writeData(_passKeyEncrypted); - - uint32 mapSize = 0; - const auto self = [] { - if (!Main::Session::Exists()) { - DEBUG_LOG(("AuthSelf Warning: Session does not exist.")); - return QByteArray(); - } - const auto self = Auth().user(); - if (self->phone().isEmpty()) { - DEBUG_LOG(("AuthSelf Error: Phone is empty.")); - return QByteArray(); - } - auto result = QByteArray(); - result.reserve(Serialize::peerSize(self) - + Serialize::stringSize(self->about())); - { - QBuffer buffer(&result); - buffer.open(QIODevice::WriteOnly); - QDataStream stream(&buffer); - Serialize::writePeer(stream, self); - stream << self->about(); - } - return result; - }(); - if (!self.isEmpty()) mapSize += sizeof(quint32) + Serialize::bytearraySize(self); - if (!_draftsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2; - if (!_draftCursorsMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _draftCursorsMap.size() * sizeof(quint64) * 2; - if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64); - if (_trustedBotsKey) mapSize += sizeof(quint32) + sizeof(quint64); - if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64); - if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) { - mapSize += sizeof(quint32) + 4 * sizeof(quint64); - } - if (_favedStickersKey) mapSize += sizeof(quint32) + sizeof(quint64); - if (_savedGifsKey) mapSize += sizeof(quint32) + sizeof(quint64); - if (_backgroundKeyDay || _backgroundKeyNight) mapSize += sizeof(quint32) + sizeof(quint64) + sizeof(quint64); - if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); - if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64); - if (_exportSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); - - EncryptedDescriptor mapData(mapSize); - if (!self.isEmpty()) { - mapData.stream << quint32(lskSelfSerialized) << self; - } - if (!_draftsMap.isEmpty()) { - mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size()); - for (DraftsMap::const_iterator i = _draftsMap.cbegin(), e = _draftsMap.cend(); i != e; ++i) { - mapData.stream << quint64(i.value()) << quint64(i.key()); - } - } - if (!_draftCursorsMap.isEmpty()) { - mapData.stream << quint32(lskDraftPosition) << quint32(_draftCursorsMap.size()); - for (DraftsMap::const_iterator i = _draftCursorsMap.cbegin(), e = _draftCursorsMap.cend(); i != e; ++i) { - mapData.stream << quint64(i.value()) << quint64(i.key()); - } - } - if (_locationsKey) { - mapData.stream << quint32(lskLocations) << quint64(_locationsKey); - } - if (_trustedBotsKey) { - mapData.stream << quint32(lskTrustedBots) << quint64(_trustedBotsKey); - } - if (_recentStickersKeyOld) { - mapData.stream << quint32(lskRecentStickersOld) << quint64(_recentStickersKeyOld); - } - if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) { - mapData.stream << quint32(lskStickersKeys); - mapData.stream << quint64(_installedStickersKey) << quint64(_featuredStickersKey) << quint64(_recentStickersKey) << quint64(_archivedStickersKey); - } - if (_favedStickersKey) { - mapData.stream << quint32(lskFavedStickers) << quint64(_favedStickersKey); - } - if (_savedGifsKey) { - mapData.stream << quint32(lskSavedGifs) << quint64(_savedGifsKey); - } - if (_backgroundKeyDay || _backgroundKeyNight) { - mapData.stream - << quint32(lskBackground) - << quint64(_backgroundKeyDay) - << quint64(_backgroundKeyNight); - } - if (_userSettingsKey) { - mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey); - } - if (_recentHashtagsAndBotsKey) { - mapData.stream << quint32(lskRecentHashtagsAndBots) << quint64(_recentHashtagsAndBotsKey); - } - if (_exportSettingsKey) { - mapData.stream << quint32(lskExportSettings) << quint64(_exportSettingsKey); - } - map.writeEncrypted(mapData); - - _mapChanged = false; -} - } // namespace void finish() { if (_manager) { - _writeMap(WriteMapWhen::Now); _manager->finish(); _manager->deleteLater(); _manager = nullptr; @@ -2691,7 +321,7 @@ void start() { ReadSettingsContext context; FileReadDescriptor settingsData; - if (!ReadFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), FileOwner::Global)) { + if (!ReadFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), _basePath)) { _readOldSettings(true, context); _readOldUserSettings(false, context); // needed further in _readUserSettings _readOldMtpData(false, context); // needed further in _readMtpData @@ -2706,7 +336,7 @@ void start() { QByteArray salt, settingsEncrypted; settingsData.stream >> salt >> settingsEncrypted; - if (!_checkStreamStatus(settingsData.stream)) { + if (!CheckStreamStatus(settingsData.stream)) { return writeSettings(); } @@ -2714,10 +344,10 @@ void start() { LOG(("App Error: bad salt in settings file, size: %1").arg(salt.size())); return writeSettings(); } - createLocalKey(QByteArray(), &salt, &SettingsKey); + SettingsKey = CreateLocalKey(QByteArray(), salt); EncryptedDescriptor settings; - if (!decryptLocal(settings, settingsEncrypted, SettingsKey)) { + if (!DecryptLocal(settings, settingsEncrypted, SettingsKey)) { LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode...")); return writeSettings(); } @@ -2726,11 +356,11 @@ void start() { while (!settings.stream.atEnd()) { quint32 blockId; settings.stream >> blockId; - if (!_checkStreamStatus(settings.stream)) { + if (!CheckStreamStatus(settings.stream)) { return writeSettings(); } - if (!_readSetting(blockId, settings.stream, settingsData.version, context)) { + if (!ReadSetting(blockId, settings.stream, settingsData.version, context)) { return writeSettings(); } } @@ -2754,11 +384,11 @@ void writeSettings() { FileWriteDescriptor settings( cTestMode() ? qsl("settings_test") : qsl("settings"), - FileOwner::Global); + _basePath); if (_settingsSalt.isEmpty() || !SettingsKey) { _settingsSalt.resize(LocalEncryptSaltSize); memset_rand(_settingsSalt.data(), _settingsSalt.size()); - createLocalKey(QByteArray(), &_settingsSalt, &SettingsKey); + SettingsKey = CreateLocalKey(QByteArray(), _settingsSalt); } settings.writeData(_settingsSalt); @@ -2770,6 +400,7 @@ void writeSettings() { size += sizeof(quint32) + Serialize::bytearraySize(applicationSettings); size += sizeof(quint32) + Serialize::stringSize(cLoggedPhoneNumber()); size += sizeof(quint32) + Serialize::stringSize(Global::TxtDomainString()); + size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath()); auto &proxies = Global::RefProxiesList(); const auto &proxy = Global::SelectedProxy(); @@ -2809,6 +440,7 @@ void writeSettings() { data.stream << quint32(dbiApplicationSettings) << applicationSettings; data.stream << quint32(dbiLoggedPhoneNumber) << cLoggedPhoneNumber(); data.stream << quint32(dbiTxtDomainString) << Global::TxtDomainString(); + data.stream << quint32(dbiDialogLastPath) << cDialogLastPath(); data.stream << quint32(dbiAnimationsDisabled) << qint32(anim::Disabled() ? 1 : 0); data.stream << quint32(dbiConnectionType) << qint32(dbictProxiesList); @@ -2843,14 +475,6 @@ void writeSettings() { settings.writeEncrypted(data, SettingsKey); } -void writeUserSettings() { - _writeUserSettings(); -} - -void writeMtpData() { - _writeMtpData(); -} - const QString &AutoupdatePrefix(const QString &replaceWith = {}) { Expects(!Core::UpdaterDisabled()); @@ -2916,443 +540,15 @@ void reset() { _localLoader->stop(); } - _passKeySalt.clear(); // reset passcode, local key - _draftsMap.clear(); - _draftCursorsMap.clear(); - _fileLocations.clear(); - _fileLocationPairs.clear(); - _fileLocationAliases.clear(); - _draftsNotReadMap.clear(); - _locationsKey = _trustedBotsKey = 0; - _recentStickersKeyOld = 0; - _installedStickersKey = _featuredStickersKey = _recentStickersKey = _favedStickersKey = _archivedStickersKey = 0; - _savedGifsKey = 0; - _backgroundKeyDay = _backgroundKeyNight = 0; Window::Theme::Background()->reset(); - _userSettingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0; - _oldMapVersion = _oldSettingsVersion = 0; - _cacheTotalSizeLimit = Database::Settings().totalSizeLimit; - _cacheTotalTimeLimit = Database::Settings().totalTimeLimit; - _cacheBigFileTotalSizeLimit = Database::Settings().totalSizeLimit; - _cacheBigFileTotalTimeLimit = Database::Settings().totalTimeLimit; + _oldSettingsVersion = 0; StoredSessionSettings.reset(); - _mapChanged = true; - _writeMap(WriteMapWhen::Now); - - _writeMtpData(); -} - -bool checkPasscode(const QByteArray &passcode) { - auto checkKey = MTP::AuthKeyPtr(); - createLocalKey(passcode, &_passKeySalt, &checkKey); - return checkKey->equals(PassKey); -} - -void setPasscode(const QByteArray &passcode) { - createLocalKey(passcode, &_passKeySalt, &PassKey); - - EncryptedDescriptor passKeyData(kLocalKeySize); - LocalKey->write(passKeyData.stream); - _passKeyEncrypted = PrepareEncrypted(passKeyData, PassKey); - - _mapChanged = true; - _writeMap(WriteMapWhen::Now); - - Global::SetLocalPasscode(!passcode.isEmpty()); - Global::RefLocalPasscodeChanged().notify(); -} - -base::flat_set CollectGoodNames() { - const auto keys = { - _locationsKey, - _userSettingsKey, - _installedStickersKey, - _featuredStickersKey, - _recentStickersKey, - _favedStickersKey, - _archivedStickersKey, - _recentStickersKeyOld, - _savedGifsKey, - _backgroundKeyNight, - _backgroundKeyDay, - _recentHashtagsAndBotsKey, - _exportSettingsKey, - _trustedBotsKey - }; - auto result = base::flat_set{ "map0", "map1", "maps" }; - const auto push = [&](FileKey key) { - if (!key) { - return; - } - auto name = toFilePart(key) + '0'; - result.emplace(name); - name[name.size() - 1] = '1'; - result.emplace(name); - name[name.size() - 1] = 's'; - result.emplace(name); - }; - for (const auto &value : _draftsMap) { - push(value); - } - for (const auto &value : _draftCursorsMap) { - push(value); - } - for (const auto &value : keys) { - push(value); - } - return result; -} - -void FilterLegacyFiles(FnMut&&)> then) { - crl::on_main([then = std::move(then)]() mutable { - then(CollectGoodNames()); - }); -} - -ReadMapState readMap(const QByteArray &pass) { - ReadMapState result = _readMap(pass); - if (result == ReadMapFailed) { - _mapChanged = true; - _writeMap(WriteMapWhen::Now); - } - if (result != ReadMapPassNeeded) { - Storage::ClearLegacyFiles(_userBasePath, FilterLegacyFiles); - } - return result; -} - -int32 oldMapVersion() { - return _oldMapVersion; } int32 oldSettingsVersion() { return _oldSettingsVersion; } -void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const MessageDraft &editDraft) { - if (!_working()) return; - - if (localDraft.msgId <= 0 && localDraft.textWithTags.text.isEmpty() && editDraft.msgId <= 0) { - auto i = _draftsMap.find(peer); - if (i != _draftsMap.cend()) { - ClearKey(i.value()); - _draftsMap.erase(i); - _mapChanged = true; - _writeMap(); - } - - _draftsNotReadMap.remove(peer); - } else { - auto i = _draftsMap.constFind(peer); - if (i == _draftsMap.cend()) { - i = _draftsMap.insert(peer, GenerateKey()); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - - auto msgTags = TextUtilities::SerializeTags( - localDraft.textWithTags.tags); - auto editTags = TextUtilities::SerializeTags( - editDraft.textWithTags.tags); - - int size = sizeof(quint64); - size += Serialize::stringSize(localDraft.textWithTags.text) + Serialize::bytearraySize(msgTags) + 2 * sizeof(qint32); - size += Serialize::stringSize(editDraft.textWithTags.text) + Serialize::bytearraySize(editTags) + 2 * sizeof(qint32); - - EncryptedDescriptor data(size); - data.stream << quint64(peer); - data.stream << localDraft.textWithTags.text << msgTags; - data.stream << qint32(localDraft.msgId) << qint32(localDraft.previewCancelled ? 1 : 0); - data.stream << editDraft.textWithTags.text << editTags; - data.stream << qint32(editDraft.msgId) << qint32(editDraft.previewCancelled ? 1 : 0); - - FileWriteDescriptor file(i.value()); - file.writeEncrypted(data); - - _draftsNotReadMap.remove(peer); - } -} - -void clearDraftCursors(const PeerId &peer) { - DraftsMap::iterator i = _draftCursorsMap.find(peer); - if (i != _draftCursorsMap.cend()) { - ClearKey(i.value()); - _draftCursorsMap.erase(i); - _mapChanged = true; - _writeMap(); - } -} - -void _readDraftCursors(const PeerId &peer, MessageCursor &localCursor, MessageCursor &editCursor) { - DraftsMap::iterator j = _draftCursorsMap.find(peer); - if (j == _draftCursorsMap.cend()) { - return; - } - - FileReadDescriptor draft; - if (!ReadEncryptedFile(draft, j.value())) { - clearDraftCursors(peer); - return; - } - quint64 draftPeer; - qint32 localPosition = 0, localAnchor = 0, localScroll = QFIXED_MAX; - qint32 editPosition = 0, editAnchor = 0, editScroll = QFIXED_MAX; - draft.stream >> draftPeer >> localPosition >> localAnchor >> localScroll; - if (!draft.stream.atEnd()) { - draft.stream >> editPosition >> editAnchor >> editScroll; - } - - if (draftPeer != peer) { - clearDraftCursors(peer); - return; - } - - localCursor = MessageCursor(localPosition, localAnchor, localScroll); - editCursor = MessageCursor(editPosition, editAnchor, editScroll); -} - -void readDraftsWithCursors(History *h) { - PeerId peer = h->peer->id; - if (!_draftsNotReadMap.remove(peer)) { - clearDraftCursors(peer); - return; - } - - DraftsMap::iterator j = _draftsMap.find(peer); - if (j == _draftsMap.cend()) { - clearDraftCursors(peer); - return; - } - FileReadDescriptor draft; - if (!ReadEncryptedFile(draft, j.value())) { - ClearKey(j.value()); - _draftsMap.erase(j); - clearDraftCursors(peer); - return; - } - - quint64 draftPeer = 0; - TextWithTags msgData, editData; - QByteArray msgTagsSerialized, editTagsSerialized; - qint32 msgReplyTo = 0, msgPreviewCancelled = 0, editMsgId = 0, editPreviewCancelled = 0; - draft.stream >> draftPeer >> msgData.text; - if (draft.version >= 9048) { - draft.stream >> msgTagsSerialized; - } - if (draft.version >= 7021) { - draft.stream >> msgReplyTo; - if (draft.version >= 8001) { - draft.stream >> msgPreviewCancelled; - if (!draft.stream.atEnd()) { - draft.stream >> editData.text; - if (draft.version >= 9048) { - draft.stream >> editTagsSerialized; - } - draft.stream >> editMsgId >> editPreviewCancelled; - } - } - } - if (draftPeer != peer) { - ClearKey(j.value()); - _draftsMap.erase(j); - clearDraftCursors(peer); - return; - } - - msgData.tags = TextUtilities::DeserializeTags( - msgTagsSerialized, - msgData.text.size()); - editData.tags = TextUtilities::DeserializeTags( - editTagsSerialized, - editData.text.size()); - - MessageCursor msgCursor, editCursor; - _readDraftCursors(peer, msgCursor, editCursor); - - if (!h->localDraft()) { - if (msgData.text.isEmpty() && !msgReplyTo) { - h->clearLocalDraft(); - } else { - h->setLocalDraft(std::make_unique( - msgData, - msgReplyTo, - msgCursor, - msgPreviewCancelled)); - } - } - if (!editMsgId) { - h->clearEditDraft(); - } else { - h->setEditDraft(std::make_unique( - editData, - editMsgId, - editCursor, - editPreviewCancelled)); - } -} - -void writeDraftCursors(const PeerId &peer, const MessageCursor &msgCursor, const MessageCursor &editCursor) { - if (!_working()) return; - - if (msgCursor == MessageCursor() && editCursor == MessageCursor()) { - clearDraftCursors(peer); - } else { - DraftsMap::const_iterator i = _draftCursorsMap.constFind(peer); - if (i == _draftCursorsMap.cend()) { - i = _draftCursorsMap.insert(peer, GenerateKey()); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - - EncryptedDescriptor data(sizeof(quint64) + sizeof(qint32) * 3); - data.stream << quint64(peer) << qint32(msgCursor.position) << qint32(msgCursor.anchor) << qint32(msgCursor.scroll); - data.stream << qint32(editCursor.position) << qint32(editCursor.anchor) << qint32(editCursor.scroll); - - FileWriteDescriptor file(i.value()); - file.writeEncrypted(data); - } -} - -bool hasDraftCursors(const PeerId &peer) { - return _draftCursorsMap.contains(peer); -} - -bool hasDraft(const PeerId &peer) { - return _draftsMap.contains(peer); -} - -void writeFileLocation(MediaKey location, const FileLocation &local) { - if (local.fname.isEmpty()) { - return; - } - if (!local.inMediaCache()) { - FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location); - if (aliasIt != _fileLocationAliases.cend()) { - location = aliasIt.value(); - } - - FileLocationPairs::iterator i = _fileLocationPairs.find(local.fname); - if (i != _fileLocationPairs.cend()) { - if (i.value().second == local) { - if (i.value().first != location) { - _fileLocationAliases.insert(location, i.value().first); - _writeLocations(WriteMapWhen::Fast); - } - return; - } - if (i.value().first != location) { - for (FileLocations::iterator j = _fileLocations.find(i.value().first), e = _fileLocations.end(); (j != e) && (j.key() == i.value().first); ++j) { - if (j.value() == i.value().second) { - _fileLocations.erase(j); - break; - } - } - _fileLocationPairs.erase(i); - } - } - _fileLocationPairs.insert(local.fname, FileLocationPair(location, local)); - } else { - for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { - if (i.value().inMediaCache() || i.value().check()) { - return; - } - i = _fileLocations.erase(i); - } - } - _fileLocations.insert(location, local); - _writeLocations(WriteMapWhen::Fast); -} - -void removeFileLocation(MediaKey location) { - FileLocations::iterator i = _fileLocations.find(location); - if (i == _fileLocations.end()) { - return; - } - while (i != _fileLocations.end() && (i.key() == location)) { - i = _fileLocations.erase(i); - } - _writeLocations(WriteMapWhen::Fast); -} - -FileLocation readFileLocation(MediaKey location) { - FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location); - if (aliasIt != _fileLocationAliases.cend()) { - location = aliasIt.value(); - } - - for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { - if (!i.value().inMediaCache() && !i.value().check()) { - _fileLocationPairs.remove(i.value().fname); - i = _fileLocations.erase(i); - _writeLocations(); - continue; - } - return i.value(); - } - return FileLocation(); -} - -Storage::EncryptionKey cacheKey() { - Expects(LocalKey != nullptr); - - return Storage::EncryptionKey(bytes::make_vector(LocalKey->data())); -} - -Storage::EncryptionKey cacheBigFileKey() { - return cacheKey(); -} - -QString cachePath() { - Expects(!_userDbPath.isEmpty()); - - return _userDbPath + "cache"; -} - -Storage::Cache::Database::Settings cacheSettings() { - auto result = Storage::Cache::Database::Settings(); - result.clearOnWrongKey = true; - result.totalSizeLimit = _cacheTotalSizeLimit; - result.totalTimeLimit = _cacheTotalTimeLimit; - result.maxDataSize = Storage::kMaxFileInMemory; - return result; -} - -void updateCacheSettings( - Storage::Cache::Database::SettingsUpdate &update, - Storage::Cache::Database::SettingsUpdate &updateBig) { - Expects(update.totalSizeLimit > Database::Settings().maxDataSize); - Expects(update.totalTimeLimit >= 0); - Expects(updateBig.totalSizeLimit > Database::Settings().maxDataSize); - Expects(updateBig.totalTimeLimit >= 0); - - if (_cacheTotalSizeLimit == update.totalSizeLimit - && _cacheTotalTimeLimit == update.totalTimeLimit - && _cacheBigFileTotalSizeLimit == updateBig.totalSizeLimit - && _cacheBigFileTotalTimeLimit == updateBig.totalTimeLimit) { - return; - } - _cacheTotalSizeLimit = update.totalSizeLimit; - _cacheTotalTimeLimit = update.totalTimeLimit; - _cacheBigFileTotalSizeLimit = updateBig.totalSizeLimit; - _cacheBigFileTotalTimeLimit = updateBig.totalTimeLimit; - _writeUserSettings(); -} - -QString cacheBigFilePath() { - Expects(!_userDbPath.isEmpty()); - - return _userDbPath + "media_cache"; -} - -Storage::Cache::Database::Settings cacheBigFileSettings() { - auto result = Storage::Cache::Database::Settings(); - result.clearOnWrongKey = true; - result.totalSizeLimit = _cacheBigFileTotalSizeLimit; - result.totalTimeLimit = _cacheBigFileTotalTimeLimit; - result.maxDataSize = Storage::kMaxFileInMemory; - return result; -} - class CountWaveformTask : public Task { public: CountWaveformTask(not_null media) @@ -3423,937 +619,11 @@ void cancelTask(TaskId id) { } } -void _writeStickerSet(QDataStream &stream, const Data::StickersSet &set) { - const auto writeInfo = [&](int count) { - stream - << quint64(set.id) - << quint64(set.access) - << set.title - << set.shortName - << qint32(count) - << qint32(set.hash) - << qint32(set.flags) - << qint32(set.installDate); - Serialize::writeImageLocation(stream, set.thumbnailLocation()); - }; - if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { - writeInfo(-set.count); - return; - } else if (set.stickers.isEmpty()) { - return; - } - - writeInfo(set.stickers.size()); - for (const auto &sticker : set.stickers) { - Serialize::Document::writeToStream(stream, sticker); - } - stream << qint32(set.dates.size()); - if (!set.dates.empty()) { - Assert(set.dates.size() == set.stickers.size()); - for (const auto date : set.dates) { - stream << qint32(date); - } - } - stream << qint32(set.emoji.size()); - for (auto j = set.emoji.cbegin(), e = set.emoji.cend(); j != e; ++j) { - stream << j.key()->id() << qint32(j->size()); - for (const auto sticker : *j) { - stream << quint64(sticker->id); - } - } -} - -// In generic method _writeStickerSets() we look through all the sets and call a -// callback on each set to see, if we write it, skip it or abort the whole write. -enum class StickerSetCheckResult { - Write, - Skip, - Abort, -}; - -// CheckSet is a functor on Data::StickersSet, which returns a StickerSetCheckResult. -template -void _writeStickerSets( - FileKey &stickersKey, - CheckSet checkSet, - const Data::StickersSetsOrder &order) { - if (!_working()) return; - - const auto &sets = Auth().data().stickers().sets(); - if (sets.empty()) { - if (stickersKey) { - ClearKey(stickersKey); - stickersKey = 0; - _mapChanged = true; - } - _writeMap(); - return; - } - - // versionTag + version + count - quint32 size = sizeof(quint32) + sizeof(qint32) + sizeof(qint32); - - int32 setsCount = 0; - for (const auto &[id, set] : sets) { - const auto raw = set.get(); - auto result = checkSet(*raw); - if (result == StickerSetCheckResult::Abort) { - return; - } else if (result == StickerSetCheckResult::Skip) { - continue; - } - - // id + access + title + shortName + stickersCount + hash + flags + installDate - size += sizeof(quint64) * 2 - + Serialize::stringSize(raw->title) - + Serialize::stringSize(raw->shortName) - + sizeof(qint32) * 4 - + Serialize::imageLocationSize(raw->thumbnailLocation()); - if (raw->flags & MTPDstickerSet_ClientFlag::f_not_loaded) { - continue; - } - - for (const auto sticker : raw->stickers) { - size += Serialize::Document::sizeInStream(sticker); - } - - size += sizeof(qint32); // datesCount - if (!raw->dates.empty()) { - Assert(raw->stickers.size() == raw->dates.size()); - size += raw->dates.size() * sizeof(qint32); - } - - size += sizeof(qint32); // emojiCount - for (auto j = raw->emoji.cbegin(), e = raw->emoji.cend(); j != e; ++j) { - size += Serialize::stringSize(j.key()->id()) + sizeof(qint32) + (j->size() * sizeof(quint64)); - } - - ++setsCount; - } - if (!setsCount && order.isEmpty()) { - if (stickersKey) { - ClearKey(stickersKey); - stickersKey = 0; - _mapChanged = true; - } - _writeMap(); - return; - } - size += sizeof(qint32) + (order.size() * sizeof(quint64)); - - if (!stickersKey) { - stickersKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - EncryptedDescriptor data(size); - data.stream - << quint32(kStickersVersionTag) - << qint32(kStickersSerializeVersion) - << qint32(setsCount); - for (const auto &[id, set] : sets) { - auto result = checkSet(*set); - if (result == StickerSetCheckResult::Abort) { - return; - } else if (result == StickerSetCheckResult::Skip) { - continue; - } - _writeStickerSet(data.stream, *set); - } - data.stream << order; - - FileWriteDescriptor file(stickersKey); - file.writeEncrypted(data); -} - -void _readStickerSets( - FileKey &stickersKey, - Data::StickersSetsOrder *outOrder = nullptr, - MTPDstickerSet::Flags readingFlags = 0) { - FileReadDescriptor stickers; - if (!ReadEncryptedFile(stickers, stickersKey)) { - ClearKey(stickersKey); - stickersKey = 0; - _writeMap(); - return; - } - - const auto failed = [&] { - ClearKey(stickersKey); - stickersKey = 0; - }; - - auto &sets = Auth().data().stickers().setsRef(); - if (outOrder) outOrder->clear(); - - quint32 versionTag = 0; - qint32 version = 0; - stickers.stream >> versionTag >> version; - if (versionTag != kStickersVersionTag - || version != kStickersSerializeVersion) { - // Old data, without sticker set thumbnails. - return failed(); - } - qint32 count = 0; - stickers.stream >> count; - if (!_checkStreamStatus(stickers.stream) - || (count < 0) - || (count > kMaxSavedStickerSetsCount)) { - return failed(); - } - for (auto i = 0; i != count; ++i) { - using LocationType = StorageFileLocation::Type; - - quint64 setId = 0, setAccess = 0; - QString setTitle, setShortName; - qint32 scnt = 0; - qint32 setInstallDate = 0; - qint32 setHash = 0; - MTPDstickerSet::Flags setFlags = 0; - qint32 setFlagsValue = 0; - ImageLocation setThumbnail; - - stickers.stream - >> setId - >> setAccess - >> setTitle - >> setShortName - >> scnt - >> setHash - >> setFlagsValue - >> setInstallDate; - const auto thumbnail = Serialize::readImageLocation( - stickers.version, - stickers.stream); - if (!thumbnail || !_checkStreamStatus(stickers.stream)) { - return failed(); - } else if (thumbnail->valid() && thumbnail->isLegacy()) { - setThumbnail = thumbnail->convertToModern( - LocationType::StickerSetThumb, - setId, - setAccess); - } else { - setThumbnail = *thumbnail; - } - - setFlags = MTPDstickerSet::Flags::from_raw(setFlagsValue); - if (setId == Data::Stickers::DefaultSetId) { - setTitle = tr::lng_stickers_default_set(tr::now); - setFlags |= MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special; - } else if (setId == Data::Stickers::CustomSetId) { - setTitle = qsl("Custom stickers"); - setFlags |= MTPDstickerSet_ClientFlag::f_special; - } else if (setId == Data::Stickers::CloudRecentSetId) { - setTitle = tr::lng_recent_stickers(tr::now); - setFlags |= MTPDstickerSet_ClientFlag::f_special; - } else if (setId == Data::Stickers::FavedSetId) { - setTitle = Lang::Hard::FavedSetTitle(); - setFlags |= MTPDstickerSet_ClientFlag::f_special; - } else if (!setId) { - continue; - } - - auto it = sets.find(setId); - if (it == sets.cend()) { - // We will set this flags from order lists when reading those stickers. - setFlags &= ~(MTPDstickerSet::Flag::f_installed_date | MTPDstickerSet_ClientFlag::f_featured); - it = sets.emplace(setId, std::make_unique( - &Auth().data(), - setId, - setAccess, - setTitle, - setShortName, - 0, - setHash, - MTPDstickerSet::Flags(setFlags), - setInstallDate)).first; - it->second->setThumbnail( - ImageWithLocation{ .location = setThumbnail }); - } - const auto set = it->second.get(); - auto inputSet = MTP_inputStickerSetID(MTP_long(set->id), MTP_long(set->access)); - const auto fillStickers = set->stickers.isEmpty(); - - if (scnt < 0) { // disabled not loaded set - if (!set->count || fillStickers) { - set->count = -scnt; - } - continue; - } - - if (fillStickers) { - set->stickers.reserve(scnt); - set->count = 0; - } - - Serialize::Document::StickerSetInfo info(setId, setAccess, setShortName); - base::flat_set read; - for (int32 j = 0; j < scnt; ++j) { - auto document = Serialize::Document::readStickerFromStream(stickers.version, stickers.stream, info); - if (!_checkStreamStatus(stickers.stream)) { - return failed(); - } else if (!document - || !document->sticker() - || read.contains(document->id)) { - continue; - } - read.emplace(document->id); - if (fillStickers) { - set->stickers.push_back(document); - if (!(set->flags & MTPDstickerSet_ClientFlag::f_special)) { - if (document->sticker()->set.type() != mtpc_inputStickerSetID) { - document->sticker()->set = inputSet; - } - } - ++set->count; - } - } - - qint32 datesCount = 0; - stickers.stream >> datesCount; - if (datesCount > 0) { - if (datesCount != scnt) { - return failed(); - } - const auto fillDates = (set->id == Data::Stickers::CloudRecentSetId) - && (set->stickers.size() == datesCount); - if (fillDates) { - set->dates.clear(); - set->dates.reserve(datesCount); - } - for (auto i = 0; i != datesCount; ++i) { - qint32 date = 0; - stickers.stream >> date; - if (fillDates) { - set->dates.push_back(TimeId(date)); - } - } - } - - qint32 emojiCount = 0; - stickers.stream >> emojiCount; - if (!_checkStreamStatus(stickers.stream) || emojiCount < 0) { - return failed(); - } - for (int32 j = 0; j < emojiCount; ++j) { - QString emojiString; - qint32 stickersCount; - stickers.stream >> emojiString >> stickersCount; - Data::StickersPack pack; - pack.reserve(stickersCount); - for (int32 k = 0; k < stickersCount; ++k) { - quint64 id; - stickers.stream >> id; - const auto doc = Auth().data().document(id); - if (!doc->sticker()) continue; - - pack.push_back(doc); - } - if (fillStickers) { - if (auto emoji = Ui::Emoji::Find(emojiString)) { - emoji = emoji->original(); - set->emoji.insert(emoji, pack); - } - } - } - } - - // Read orders of installed and featured stickers. - if (outOrder) { - auto outOrderCount = quint32(); - stickers.stream >> outOrderCount; - if (!_checkStreamStatus(stickers.stream) || outOrderCount > 1000) { - return failed(); - } - outOrder->reserve(outOrderCount); - for (auto i = 0; i != outOrderCount; ++i) { - auto value = uint64(); - stickers.stream >> value; - if (!_checkStreamStatus(stickers.stream)) { - outOrder->clear(); - return failed(); - } - outOrder->push_back(value); - } - } - if (!_checkStreamStatus(stickers.stream)) { - return failed(); - } - - // Set flags that we dropped above from the order. - if (readingFlags && outOrder) { - for (const auto setId : std::as_const(*outOrder)) { - auto it = sets.find(setId); - if (it != sets.cend()) { - const auto set = it->second.get(); - set->flags |= readingFlags; - if ((readingFlags == MTPDstickerSet::Flag::f_installed_date) - && !set->installDate) { - set->installDate = kDefaultStickerInstallDate; - } - } - } - } -} - -void writeInstalledStickers() { - if (!Global::started()) return; - - _writeStickerSets(_installedStickersKey, [](const Data::StickersSet &set) { - if (set.id == Data::Stickers::CloudRecentSetId || set.id == Data::Stickers::FavedSetId) { // separate files for them - return StickerSetCheckResult::Skip; - } else if (set.flags & MTPDstickerSet_ClientFlag::f_special) { - if (set.stickers.isEmpty()) { // all other special are "installed" - return StickerSetCheckResult::Skip; - } - } else if (!(set.flags & MTPDstickerSet::Flag::f_installed_date) || (set.flags & MTPDstickerSet::Flag::f_archived)) { - return StickerSetCheckResult::Skip; - } else if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { // waiting to receive - return StickerSetCheckResult::Abort; - } else if (set.stickers.isEmpty()) { - return StickerSetCheckResult::Skip; - } - return StickerSetCheckResult::Write; - }, Auth().data().stickers().setsOrder()); -} - -void writeFeaturedStickers() { - if (!Global::started()) return; - - _writeStickerSets(_featuredStickersKey, [](const Data::StickersSet &set) { - if (set.id == Data::Stickers::CloudRecentSetId - || set.id == Data::Stickers::FavedSetId) { // separate files for them - return StickerSetCheckResult::Skip; - } else if (set.flags & MTPDstickerSet_ClientFlag::f_special) { - return StickerSetCheckResult::Skip; - } else if (!(set.flags & MTPDstickerSet_ClientFlag::f_featured)) { - return StickerSetCheckResult::Skip; - } else if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { // waiting to receive - return StickerSetCheckResult::Abort; - } else if (set.stickers.isEmpty()) { - return StickerSetCheckResult::Skip; - } - return StickerSetCheckResult::Write; - }, Auth().data().stickers().featuredSetsOrder()); -} - -void writeRecentStickers() { - if (!Global::started()) return; - - _writeStickerSets(_recentStickersKey, [](const Data::StickersSet &set) { - if (set.id != Data::Stickers::CloudRecentSetId || set.stickers.isEmpty()) { - return StickerSetCheckResult::Skip; - } - return StickerSetCheckResult::Write; - }, Data::StickersSetsOrder()); -} - -void writeFavedStickers() { - if (!Global::started()) return; - - _writeStickerSets(_favedStickersKey, [](const Data::StickersSet &set) { - if (set.id != Data::Stickers::FavedSetId || set.stickers.isEmpty()) { - return StickerSetCheckResult::Skip; - } - return StickerSetCheckResult::Write; - }, Data::StickersSetsOrder()); -} - -void writeArchivedStickers() { - if (!Global::started()) return; - - _writeStickerSets(_archivedStickersKey, [](const Data::StickersSet &set) { - if (!(set.flags & MTPDstickerSet::Flag::f_archived) || set.stickers.isEmpty()) { - return StickerSetCheckResult::Skip; - } - return StickerSetCheckResult::Write; - }, Auth().data().stickers().archivedSetsOrder()); -} - -void importOldRecentStickers() { - if (!_recentStickersKeyOld) return; - - FileReadDescriptor stickers; - if (!ReadEncryptedFile(stickers, _recentStickersKeyOld)) { - ClearKey(_recentStickersKeyOld); - _recentStickersKeyOld = 0; - _writeMap(); - return; - } - - auto &sets = Auth().data().stickers().setsRef(); - sets.clear(); - - auto &order = Auth().data().stickers().setsOrderRef(); - order.clear(); - - auto &recent = cRefRecentStickers(); - recent.clear(); - - const auto def = sets.emplace( - Data::Stickers::DefaultSetId, - std::make_unique( - &Auth().data(), - Data::Stickers::DefaultSetId, - uint64(0), - tr::lng_stickers_default_set(tr::now), - QString(), - 0, // count - 0, // hash - (MTPDstickerSet::Flag::f_official - | MTPDstickerSet::Flag::f_installed_date - | MTPDstickerSet_ClientFlag::f_special), - kDefaultStickerInstallDate)).first->second.get(); - const auto custom = sets.emplace( - Data::Stickers::CustomSetId, - std::make_unique( - &Auth().data(), - Data::Stickers::CustomSetId, - uint64(0), - qsl("Custom stickers"), - QString(), - 0, // count - 0, // hash - (MTPDstickerSet::Flag::f_installed_date - | MTPDstickerSet_ClientFlag::f_special), - kDefaultStickerInstallDate)).first->second.get(); - - QMap read; - while (!stickers.stream.atEnd()) { - quint64 id, access; - QString name, mime, alt; - qint32 date, dc, size, width, height, type; - qint16 value; - stickers.stream >> id >> value >> access >> date >> name >> mime >> dc >> size >> width >> height >> type; - if (stickers.version >= 7021) { - stickers.stream >> alt; - } - if (!value || read.contains(id)) continue; - read.insert(id, true); - - QVector attributes; - if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name))); - if (type == AnimatedDocument) { - attributes.push_back(MTP_documentAttributeAnimated()); - } else if (type == StickerDocument) { - attributes.push_back(MTP_documentAttributeSticker(MTP_flags(0), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords())); - } - if (width > 0 && height > 0) { - attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); - } - - const auto doc = Auth().data().document( - id, - access, - QByteArray(), - date, - attributes, - mime, - QByteArray(), - ImageWithLocation(), - ImageWithLocation(), - dc, - size); - if (!doc->sticker()) { - continue; - } - - if (value > 0) { - def->stickers.push_back(doc); - ++def->count; - } else { - custom->stickers.push_back(doc); - ++custom->count; - } - if (recent.size() < Global::StickersRecentLimit() && qAbs(value) > 1) { - recent.push_back(qMakePair(doc, qAbs(value))); - } - } - if (def->stickers.isEmpty()) { - sets.remove(Data::Stickers::DefaultSetId); - } else { - order.push_front(Data::Stickers::DefaultSetId); - } - if (custom->stickers.isEmpty()) { - sets.remove(Data::Stickers::CustomSetId); - } - - writeInstalledStickers(); - writeUserSettings(); - - ClearKey(_recentStickersKeyOld); - _recentStickersKeyOld = 0; - _writeMap(); -} - -void readInstalledStickers() { - if (!_installedStickersKey) { - return importOldRecentStickers(); - } - - Auth().data().stickers().setsRef().clear(); - _readStickerSets( - _installedStickersKey, - &Auth().data().stickers().setsOrderRef(), - MTPDstickerSet::Flag::f_installed_date); -} - -void readFeaturedStickers() { - _readStickerSets( - _featuredStickersKey, - &Auth().data().stickers().featuredSetsOrderRef(), - MTPDstickerSet::Flags() | MTPDstickerSet_ClientFlag::f_featured); - - const auto &sets = Auth().data().stickers().sets(); - const auto &order = Auth().data().stickers().featuredSetsOrder(); - int unreadCount = 0; - for (const auto setId : order) { - auto it = sets.find(setId); - if (it != sets.cend() - && (it->second->flags & MTPDstickerSet_ClientFlag::f_unread)) { - ++unreadCount; - } - } - Auth().data().stickers().setFeaturedSetsUnreadCount(unreadCount); -} - -void readRecentStickers() { - _readStickerSets(_recentStickersKey); -} - -void readFavedStickers() { - _readStickerSets(_favedStickersKey); -} - -void readArchivedStickers() { - static bool archivedStickersRead = false; - if (!archivedStickersRead) { - _readStickerSets(_archivedStickersKey, &Auth().data().stickers().archivedSetsOrderRef()); - archivedStickersRead = true; - } -} - -int32 countDocumentVectorHash(const QVector vector) { - auto result = Api::HashInit(); - for (const auto document : vector) { - Api::HashUpdate(result, document->id); - } - return Api::HashFinalize(result); -} - -int32 countSpecialStickerSetHash(uint64 setId) { - const auto &sets = Auth().data().stickers().sets(); - const auto it = sets.find(setId); - if (it != sets.cend()) { - return countDocumentVectorHash(it->second->stickers); - } - return 0; -} - -int32 countStickersHash(bool checkOutdatedInfo) { - auto result = Api::HashInit(); - bool foundOutdated = false; - const auto &sets = Auth().data().stickers().sets(); - const auto &order = Auth().data().stickers().setsOrder(); - for (auto i = order.cbegin(), e = order.cend(); i != e; ++i) { - auto it = sets.find(*i); - if (it != sets.cend()) { - const auto set = it->second.get(); - if (set->id == Data::Stickers::DefaultSetId) { - foundOutdated = true; - } else if (!(set->flags & MTPDstickerSet_ClientFlag::f_special) - && !(set->flags & MTPDstickerSet::Flag::f_archived)) { - Api::HashUpdate(result, set->hash); - } - } - } - return (!checkOutdatedInfo || !foundOutdated) - ? Api::HashFinalize(result) - : 0; -} - -int32 countRecentStickersHash() { - return countSpecialStickerSetHash(Data::Stickers::CloudRecentSetId); -} - -int32 countFavedStickersHash() { - return countSpecialStickerSetHash(Data::Stickers::FavedSetId); -} - -int32 countFeaturedStickersHash() { - auto result = Api::HashInit(); - const auto &sets = Auth().data().stickers().sets(); - const auto &featured = Auth().data().stickers().featuredSetsOrder(); - for (const auto setId : featured) { - Api::HashUpdate(result, setId); - - const auto it = sets.find(setId); - if (it != sets.cend() - && (it->second->flags & MTPDstickerSet_ClientFlag::f_unread)) { - Api::HashUpdate(result, 1); - } - } - return Api::HashFinalize(result); -} - -int32 countSavedGifsHash() { - return countDocumentVectorHash(Auth().data().stickers().savedGifs()); -} - -void writeSavedGifs() { - if (!_working()) return; - - auto &saved = Auth().data().stickers().savedGifs(); - if (saved.isEmpty()) { - if (_savedGifsKey) { - ClearKey(_savedGifsKey); - _savedGifsKey = 0; - _mapChanged = true; - } - _writeMap(); - } else { - quint32 size = sizeof(quint32); // count - for_const (auto gif, saved) { - size += Serialize::Document::sizeInStream(gif); - } - - if (!_savedGifsKey) { - _savedGifsKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - EncryptedDescriptor data(size); - data.stream << quint32(saved.size()); - for_const (auto gif, saved) { - Serialize::Document::writeToStream(data.stream, gif); - } - FileWriteDescriptor file(_savedGifsKey); - file.writeEncrypted(data); - } -} - -void readSavedGifs() { - if (!_savedGifsKey) return; - - FileReadDescriptor gifs; - if (!ReadEncryptedFile(gifs, _savedGifsKey)) { - ClearKey(_savedGifsKey); - _savedGifsKey = 0; - _writeMap(); - return; - } - - auto &saved = Auth().data().stickers().savedGifsRef(); - const auto failed = [&] { - ClearKey(_savedGifsKey); - _savedGifsKey = 0; - saved.clear(); - }; - saved.clear(); - - quint32 cnt; - gifs.stream >> cnt; - saved.reserve(cnt); - OrderedSet read; - for (uint32 i = 0; i < cnt; ++i) { - auto document = Serialize::Document::readFromStream(gifs.version, gifs.stream); - if (!_checkStreamStatus(gifs.stream)) { - return failed(); - } else if (!document || !document->isGifv()) { - continue; - } - - if (read.contains(document->id)) continue; - read.insert(document->id); - - saved.push_back(document); - } -} - -void writeBackground(const Data::WallPaper &paper, const QImage &image) { - if (!_working() || !_backgroundCanWrite) { - return; - } - - if (!LocalKey) { - LOG(("App Error: localkey not created in writeBackground()")); - return; - } - - auto &backgroundKey = Window::Theme::IsNightMode() - ? _backgroundKeyNight - : _backgroundKeyDay; - auto imageData = QByteArray(); - if (!image.isNull()) { - const auto width = qint32(image.width()); - const auto height = qint32(image.height()); - const auto perpixel = (image.depth() >> 3); - const auto srcperline = image.bytesPerLine(); - const auto srcsize = srcperline * height; - const auto dstperline = width * perpixel; - const auto dstsize = dstperline * height; - const auto copy = (image.format() != kSavedBackgroundFormat) - ? image.convertToFormat(kSavedBackgroundFormat) - : image; - imageData.resize(2 * sizeof(qint32) + dstsize); - - auto dst = bytes::make_detached_span(imageData); - bytes::copy(dst, bytes::object_as_span(&width)); - dst = dst.subspan(sizeof(qint32)); - bytes::copy(dst, bytes::object_as_span(&height)); - dst = dst.subspan(sizeof(qint32)); - const auto src = bytes::make_span(image.constBits(), srcsize); - if (srcsize == dstsize) { - bytes::copy(dst, src); - } else { - for (auto y = 0; y != height; ++y) { - bytes::copy(dst, src.subspan(y * srcperline, dstperline)); - dst = dst.subspan(dstperline); - } - } - } - if (!backgroundKey) { - backgroundKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - const auto serialized = paper.serialize(); - quint32 size = sizeof(qint32) - + Serialize::bytearraySize(serialized) - + Serialize::bytearraySize(imageData); - EncryptedDescriptor data(size); - data.stream - << qint32(kWallPaperSerializeTagId) - << serialized - << imageData; - - FileWriteDescriptor file(backgroundKey); - file.writeEncrypted(data); -} - -bool readBackground() { - FileReadDescriptor bg; - auto &backgroundKey = Window::Theme::IsNightMode() - ? _backgroundKeyNight - : _backgroundKeyDay; - if (!ReadEncryptedFile(bg, backgroundKey)) { - if (backgroundKey) { - ClearKey(backgroundKey); - backgroundKey = 0; - _mapChanged = true; - _writeMap(); - } - return false; - } - - qint32 legacyId = 0; - bg.stream >> legacyId; - const auto paper = [&] { - if (legacyId == kWallPaperLegacySerializeTagId) { - quint64 id = 0; - quint64 accessHash = 0; - quint32 flags = 0; - QString slug; - bg.stream - >> id - >> accessHash - >> flags - >> slug; - return Data::WallPaper::FromLegacySerialized( - id, - accessHash, - flags, - slug); - } else if (legacyId == kWallPaperSerializeTagId) { - QByteArray serialized; - bg.stream >> serialized; - return Data::WallPaper::FromSerialized(serialized); - } else { - return Data::WallPaper::FromLegacyId(legacyId); - } - }(); - if (bg.stream.status() != QDataStream::Ok || !paper) { - return false; - } - - QByteArray imageData; - bg.stream >> imageData; - const auto isOldEmptyImage = (bg.stream.status() != QDataStream::Ok); - if (isOldEmptyImage - || Data::IsLegacy1DefaultWallPaper(*paper) - || Data::IsDefaultWallPaper(*paper)) { - _backgroundCanWrite = false; - if (isOldEmptyImage || bg.version < 8005) { - Window::Theme::Background()->set(Data::DefaultWallPaper()); - Window::Theme::Background()->setTile(false); - } else { - Window::Theme::Background()->set(*paper); - } - _backgroundCanWrite = true; - return true; - } else if (Data::IsThemeWallPaper(*paper) && imageData.isEmpty()) { - _backgroundCanWrite = false; - Window::Theme::Background()->set(*paper); - _backgroundCanWrite = true; - return true; - } - auto image = QImage(); - if (legacyId == kWallPaperSerializeTagId) { - const auto perpixel = 4; - auto src = bytes::make_span(imageData); - auto width = qint32(); - auto height = qint32(); - if (src.size() > 2 * sizeof(qint32)) { - bytes::copy( - bytes::object_as_span(&width), - src.subspan(0, sizeof(qint32))); - src = src.subspan(sizeof(qint32)); - bytes::copy( - bytes::object_as_span(&height), - src.subspan(0, sizeof(qint32))); - src = src.subspan(sizeof(qint32)); - if (width + height <= kWallPaperSidesLimit - && src.size() == width * height * perpixel) { - image = QImage( - width, - height, - QImage::Format_ARGB32_Premultiplied); - if (!image.isNull()) { - const auto srcperline = width * perpixel; - const auto srcsize = srcperline * height; - const auto dstperline = image.bytesPerLine(); - const auto dstsize = dstperline * height; - Assert(srcsize == dstsize); - bytes::copy( - bytes::make_span(image.bits(), dstsize), - src); - } - } - } - } else { - auto buffer = QBuffer(&imageData); - auto reader = QImageReader(&buffer); -#ifndef OS_MAC_OLD - reader.setAutoTransform(true); -#endif // OS_MAC_OLD - if (!reader.read(&image)) { - image = QImage(); - } - } - if (!image.isNull() || paper->backgroundColor()) { - _backgroundCanWrite = false; - Window::Theme::Background()->set(*paper, std::move(image)); - _backgroundCanWrite = true; - return true; - } - return false; -} - Window::Theme::Saved readThemeUsingKey(FileKey key) { using namespace Window::Theme; FileReadDescriptor theme; - if (!ReadEncryptedFile(theme, key, FileOwner::Global, SettingsKey)) { + if (!ReadEncryptedFile(theme, key, _basePath, SettingsKey)) { return {}; } @@ -4442,7 +712,7 @@ void writeTheme(const Window::Theme::Saved &saved) { : _themeKeyDay; if (saved.object.content.isEmpty()) { if (themeKey) { - ClearKey(themeKey, FileOwner::Global); + ClearKey(themeKey, _basePath); themeKey = 0; writeSettings(); } @@ -4450,7 +720,7 @@ void writeTheme(const Window::Theme::Saved &saved) { } if (!themeKey) { - themeKey = GenerateKey(FileOwner::Global); + themeKey = GenerateKey(_basePath); writeSettings(); } @@ -4487,7 +757,7 @@ void writeTheme(const Window::Theme::Saved &saved) { << cache.background << quint32(cache.tiled ? 1 : 0); - FileWriteDescriptor file(themeKey, FileOwner::Global); + FileWriteDescriptor file(themeKey, _basePath); file.writeEncrypted(data, SettingsKey); } @@ -4539,7 +809,7 @@ Window::Theme::Saved readThemeAfterSwitch() { void readLangPack() { FileReadDescriptor langpack; - if (!_langPackKey || !ReadEncryptedFile(langpack, _langPackKey, FileOwner::Global, SettingsKey)) { + if (!_langPackKey || !ReadEncryptedFile(langpack, _langPackKey, _basePath, SettingsKey)) { return; } auto data = QByteArray(); @@ -4552,21 +822,21 @@ void readLangPack() { void writeLangPack() { auto langpack = Lang::Current().serialize(); if (!_langPackKey) { - _langPackKey = GenerateKey(FileOwner::Global); + _langPackKey = GenerateKey(_basePath); writeSettings(); } EncryptedDescriptor data(Serialize::bytearraySize(langpack)); data.stream << langpack; - FileWriteDescriptor file(_langPackKey, FileOwner::Global); + FileWriteDescriptor file(_langPackKey, _basePath); file.writeEncrypted(data, SettingsKey); } void saveRecentLanguages(const std::vector &list) { if (list.empty()) { if (_languagesKey) { - ClearKey(_languagesKey, FileOwner::Global); + ClearKey(_languagesKey, _basePath); _languagesKey = 0; writeSettings(); } @@ -4582,7 +852,7 @@ void saveRecentLanguages(const std::vector &list) { + Serialize::stringSize(language.nativeName); } if (!_languagesKey) { - _languagesKey = GenerateKey(FileOwner::Global); + _languagesKey = GenerateKey(_basePath); writeSettings(); } @@ -4597,7 +867,7 @@ void saveRecentLanguages(const std::vector &list) { << language.nativeName; } - FileWriteDescriptor file(_languagesKey, FileOwner::Global); + FileWriteDescriptor file(_languagesKey, _basePath); file.writeEncrypted(data, SettingsKey); } @@ -4629,7 +899,7 @@ void removeRecentLanguage(const QString &id) { std::vector readRecentLanguages() { FileReadDescriptor languages; - if (!_languagesKey || !ReadEncryptedFile(languages, _languagesKey, FileOwner::Global, SettingsKey)) { + if (!_languagesKey || !ReadEncryptedFile(languages, _languagesKey, _basePath, SettingsKey)) { return {}; } qint32 count = 0; @@ -4664,7 +934,7 @@ Window::Theme::Object ReadThemeContent() { } FileReadDescriptor theme; - if (!ReadEncryptedFile(theme, themeKey, FileOwner::Global, SettingsKey)) { + if (!ReadEncryptedFile(theme, themeKey, _basePath, SettingsKey)) { return Object(); } @@ -4681,120 +951,6 @@ Window::Theme::Object ReadThemeContent() { return result; } -void writeRecentHashtagsAndBots() { - if (!_working()) return; - - const RecentHashtagPack &write(cRecentWriteHashtags()), &search(cRecentSearchHashtags()); - const RecentInlineBots &bots(cRecentInlineBots()); - if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) readRecentHashtagsAndBots(); - if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) { - if (_recentHashtagsAndBotsKey) { - ClearKey(_recentHashtagsAndBotsKey); - _recentHashtagsAndBotsKey = 0; - _mapChanged = true; - } - _writeMap(); - } else { - if (!_recentHashtagsAndBotsKey) { - _recentHashtagsAndBotsKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = cRecentInlineBots().size(); - for (auto i = write.cbegin(), e = write.cend(); i != e; ++i) { - if (!i->first.isEmpty()) { - size += Serialize::stringSize(i->first) + sizeof(quint16); - ++writeCnt; - } - } - for (auto i = search.cbegin(), e = search.cend(); i != e; ++i) { - if (!i->first.isEmpty()) { - size += Serialize::stringSize(i->first) + sizeof(quint16); - ++searchCnt; - } - } - for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) { - size += Serialize::peerSize(*i); - } - - EncryptedDescriptor data(size); - data.stream << quint32(writeCnt) << quint32(searchCnt); - for (auto i = write.cbegin(), e = write.cend(); i != e; ++i) { - if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second); - } - for (auto i = search.cbegin(), e = search.cend(); i != e; ++i) { - if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second); - } - data.stream << quint32(botsCnt); - for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) { - Serialize::writePeer(data.stream, *i); - } - FileWriteDescriptor file(_recentHashtagsAndBotsKey); - file.writeEncrypted(data); - } -} - -void readRecentHashtagsAndBots() { - if (_recentHashtagsAndBotsWereRead) return; - _recentHashtagsAndBotsWereRead = true; - - if (!_recentHashtagsAndBotsKey) return; - - FileReadDescriptor hashtags; - if (!ReadEncryptedFile(hashtags, _recentHashtagsAndBotsKey)) { - ClearKey(_recentHashtagsAndBotsKey); - _recentHashtagsAndBotsKey = 0; - _writeMap(); - return; - } - - quint32 writeCount = 0, searchCount = 0, botsCount = 0; - hashtags.stream >> writeCount >> searchCount; - - QString tag; - quint16 count; - - RecentHashtagPack write, search; - RecentInlineBots bots; - if (writeCount) { - write.reserve(writeCount); - for (uint32 i = 0; i < writeCount; ++i) { - hashtags.stream >> tag >> count; - write.push_back(qMakePair(tag.trimmed(), count)); - } - } - if (searchCount) { - search.reserve(searchCount); - for (uint32 i = 0; i < searchCount; ++i) { - hashtags.stream >> tag >> count; - search.push_back(qMakePair(tag.trimmed(), count)); - } - } - cSetRecentWriteHashtags(write); - cSetRecentSearchHashtags(search); - - if (!hashtags.stream.atEnd()) { - hashtags.stream >> botsCount; - if (botsCount) { - bots.reserve(botsCount); - for (auto i = 0; i < botsCount; ++i) { - const auto peer = Serialize::readPeer( - hashtags.version, - hashtags.stream); - if (!peer) { - return; // Broken data. - } else if (peer->isUser() - && peer->asUser()->isBot() - && !peer->asUser()->botInfo->inlinePlaceholder.isEmpty() - && !peer->asUser()->username.isEmpty()) { - bots.push_back(peer->asUser()); - } - } - } - cSetRecentInlineBots(bots); - } -} - void incrementRecentHashtag(RecentHashtagPack &recent, const QString &tag) { auto i = recent.begin(), e = recent.end(); for (; i != e; ++i) { @@ -4830,299 +986,12 @@ void incrementRecentHashtag(RecentHashtagPack &recent, const QString &tag) { } } -std::optional saveRecentHashtags( - Fn getPack, - const QString &text) { - auto found = false; - auto m = QRegularExpressionMatch(); - auto recent = getPack(); - for (auto i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) { - i = m.capturedStart(); - next = m.capturedEnd(); - if (m.hasMatch()) { - if (!m.capturedRef(1).isEmpty()) { - ++i; - } - if (!m.capturedRef(2).isEmpty()) { - --next; - } - } - const auto tag = text.mid(i + 1, next - i - 1); - if (TextUtilities::RegExpHashtagExclude().match(tag).hasMatch()) { - continue; - } - if (!found - && cRecentWriteHashtags().isEmpty() - && cRecentSearchHashtags().isEmpty()) { - Local::readRecentHashtagsAndBots(); - recent = getPack(); - } - found = true; - incrementRecentHashtag(recent, tag); - } - return found ? base::make_optional(recent) : std::nullopt; +bool readOldMtpData(bool remove, ReadSettingsContext &context) { + return _readOldMtpData(remove, context); } -void saveRecentSentHashtags(const QString &text) { - const auto result = saveRecentHashtags( - [] { return cRecentWriteHashtags(); }, - text); - if (result) { - cSetRecentWriteHashtags(*result); - Local::writeRecentHashtagsAndBots(); - } -} - -void saveRecentSearchHashtags(const QString &text) { - const auto result = saveRecentHashtags( - [] { return cRecentSearchHashtags(); }, - text); - if (result) { - cSetRecentSearchHashtags(*result); - Local::writeRecentHashtagsAndBots(); - } -} - -void WriteExportSettings(const Export::Settings &settings) { - if (!_working()) return; - - const auto check = Export::Settings(); - if (settings.types == check.types - && settings.fullChats == check.fullChats - && settings.media.types == check.media.types - && settings.media.sizeLimit == check.media.sizeLimit - && settings.path == check.path - && settings.format == check.format - && settings.availableAt == check.availableAt - && !settings.onlySinglePeer()) { - if (_exportSettingsKey) { - ClearKey(_exportSettingsKey); - _exportSettingsKey = 0; - _mapChanged = true; - } - _writeMap(); - } else { - if (!_exportSettingsKey) { - _exportSettingsKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - quint32 size = sizeof(quint32) * 6 - + Serialize::stringSize(settings.path) - + sizeof(qint32) * 2 + sizeof(quint64); - EncryptedDescriptor data(size); - data.stream - << quint32(settings.types) - << quint32(settings.fullChats) - << quint32(settings.media.types) - << quint32(settings.media.sizeLimit) - << quint32(settings.format) - << settings.path - << quint32(settings.availableAt); - settings.singlePeer.match([&](const MTPDinputPeerUser & user) { - data.stream - << kSinglePeerTypeUser - << qint32(user.vuser_id().v) - << quint64(user.vaccess_hash().v); - }, [&](const MTPDinputPeerChat & chat) { - data.stream << kSinglePeerTypeChat << qint32(chat.vchat_id().v); - }, [&](const MTPDinputPeerChannel & channel) { - data.stream - << kSinglePeerTypeChannel - << qint32(channel.vchannel_id().v) - << quint64(channel.vaccess_hash().v); - }, [&](const MTPDinputPeerSelf &) { - data.stream << kSinglePeerTypeSelf; - }, [&](const MTPDinputPeerEmpty &) { - data.stream << kSinglePeerTypeEmpty; - }, [&](const MTPDinputPeerUserFromMessage &) { - Unexpected("From message peer in single peer export settings."); - }, [&](const MTPDinputPeerChannelFromMessage &) { - Unexpected("From message peer in single peer export settings."); - }); - data.stream << qint32(settings.singlePeerFrom); - data.stream << qint32(settings.singlePeerTill); - - FileWriteDescriptor file(_exportSettingsKey); - file.writeEncrypted(data); - } -} - -Export::Settings ReadExportSettings() { - FileReadDescriptor file; - if (!ReadEncryptedFile(file, _exportSettingsKey)) { - ClearKey(_exportSettingsKey); - _exportSettingsKey = 0; - _writeMap(); - return Export::Settings(); - } - - quint32 types = 0, fullChats = 0; - quint32 mediaTypes = 0, mediaSizeLimit = 0; - quint32 format = 0, availableAt = 0; - QString path; - qint32 singlePeerType = 0, singlePeerBareId = 0; - quint64 singlePeerAccessHash = 0; - qint32 singlePeerFrom = 0, singlePeerTill = 0; - file.stream - >> types - >> fullChats - >> mediaTypes - >> mediaSizeLimit - >> format - >> path - >> availableAt; - if (!file.stream.atEnd()) { - file.stream >> singlePeerType; - switch (singlePeerType) { - case kSinglePeerTypeUser: - case kSinglePeerTypeChannel: { - file.stream >> singlePeerBareId >> singlePeerAccessHash; - } break; - case kSinglePeerTypeChat: file.stream >> singlePeerBareId; break; - case kSinglePeerTypeSelf: - case kSinglePeerTypeEmpty: break; - default: return Export::Settings(); - } - } - if (!file.stream.atEnd()) { - file.stream >> singlePeerFrom >> singlePeerTill; - } - auto result = Export::Settings(); - result.types = Export::Settings::Types::from_raw(types); - result.fullChats = Export::Settings::Types::from_raw(fullChats); - result.media.types = Export::MediaSettings::Types::from_raw(mediaTypes); - result.media.sizeLimit = mediaSizeLimit; - result.format = Export::Output::Format(format); - result.path = path; - result.availableAt = availableAt; - result.singlePeer = [&] { - switch (singlePeerType) { - case kSinglePeerTypeUser: - return MTP_inputPeerUser( - MTP_int(singlePeerBareId), - MTP_long(singlePeerAccessHash)); - case kSinglePeerTypeChat: - return MTP_inputPeerChat(MTP_int(singlePeerBareId)); - case kSinglePeerTypeChannel: - return MTP_inputPeerChannel( - MTP_int(singlePeerBareId), - MTP_long(singlePeerAccessHash)); - case kSinglePeerTypeSelf: - return MTP_inputPeerSelf(); - case kSinglePeerTypeEmpty: - return MTP_inputPeerEmpty(); - } - Unexpected("Type in export data single peer."); - }(); - result.singlePeerFrom = singlePeerFrom; - result.singlePeerTill = singlePeerTill; - return (file.stream.status() == QDataStream::Ok && result.validate()) - ? result - : Export::Settings(); -} - -void writeSelf() { - _mapChanged = true; - _writeMap(); -} - -void readSelf(const QByteArray &serialized, int32 streamVersion) { - QDataStream stream(serialized); - const auto user = Auth().user(); - const auto wasLoadedStatus = std::exchange( - user->loadedStatus, - PeerData::NotLoaded); - const auto self = Serialize::readPeer(streamVersion, stream); - if (!self || !self->isSelf() || self != user) { - user->loadedStatus = wasLoadedStatus; - return; - } - - QString about; - stream >> about; - if (_checkStreamStatus(stream)) { - self->asUser()->setAbout(about); - } -} - -void writeTrustedBots() { - if (!_working()) return; - - if (_trustedBots.isEmpty()) { - if (_trustedBotsKey) { - ClearKey(_trustedBotsKey); - _trustedBotsKey = 0; - _mapChanged = true; - _writeMap(); - } - } else { - if (!_trustedBotsKey) { - _trustedBotsKey = GenerateKey(); - _mapChanged = true; - _writeMap(WriteMapWhen::Fast); - } - quint32 size = sizeof(qint32) + _trustedBots.size() * sizeof(quint64); - EncryptedDescriptor data(size); - data.stream << qint32(_trustedBots.size()); - for_const (auto botId, _trustedBots) { - data.stream << quint64(botId); - } - - FileWriteDescriptor file(_trustedBotsKey); - file.writeEncrypted(data); - } -} - -void readTrustedBots() { - if (!_trustedBotsKey) return; - - FileReadDescriptor trusted; - if (!ReadEncryptedFile(trusted, _trustedBotsKey)) { - ClearKey(_trustedBotsKey); - _trustedBotsKey = 0; - _writeMap(); - return; - } - - qint32 size = 0; - trusted.stream >> size; - for (int i = 0; i < size; ++i) { - quint64 botId = 0; - trusted.stream >> botId; - _trustedBots.insert(botId); - } -} - -void makeBotTrusted(UserData *bot) { - if (!isBotTrusted(bot)) { - _trustedBots.insert(bot->id); - writeTrustedBots(); - } -} - -bool isBotTrusted(UserData *bot) { - if (!_trustedBotsRead) { - readTrustedBots(); - _trustedBotsRead = true; - } - return _trustedBots.contains(bot->id); -} - -bool encrypt(const void *src, void *dst, uint32 len, const void *key128) { - if (!LocalKey) { - return false; - } - MTP::aesEncryptLocal(src, dst, len, LocalKey, key128); - return true; -} - -bool decrypt(const void *src, void *dst, uint32 len, const void *key128) { - if (!LocalKey) { - return false; - } - MTP::aesDecryptLocal(src, dst, len, LocalKey, key128); - return true; +bool readOldUserSettings(bool remove, ReadSettingsContext &context) { + return _readOldUserSettings(remove, context); } struct ClearManagerData { @@ -5144,35 +1013,6 @@ bool ClearManager::addTask(int task) { if (!data->tasks.isEmpty() && (data->tasks.at(0) == ClearManagerAll)) return true; if (task == ClearManagerAll) { data->tasks.clear(); - if (!_draftsMap.isEmpty()) { - _draftsMap.clear(); - _mapChanged = true; - } - if (!_draftCursorsMap.isEmpty()) { - _draftCursorsMap.clear(); - _mapChanged = true; - } - if (_locationsKey) { - _locationsKey = 0; - _mapChanged = true; - } - if (_trustedBotsKey) { - _trustedBotsKey = 0; - _mapChanged = true; - } - if (_recentStickersKeyOld) { - _recentStickersKeyOld = 0; - _mapChanged = true; - } - if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) { - _installedStickersKey = _featuredStickersKey = _recentStickersKey = _archivedStickersKey = 0; - _mapChanged = true; - } - if (_recentHashtagsAndBotsKey) { - _recentHashtagsAndBotsKey = 0; - _mapChanged = true; - } - _writeMap(); } else { for (int32 i = 0, l = data->tasks.size(); i < l; ++i) { if (data->tasks.at(i) == task) return true; @@ -5229,26 +1069,24 @@ void ClearManager::onStart() { switch (task) { case ClearManagerAll: { result = QDir(cTempDir()).removeRecursively(); - QDirIterator di(_userBasePath, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot); - while (di.hasNext()) { - di.next(); - const QFileInfo& fi = di.fileInfo(); - if (fi.isDir() && !fi.isSymLink()) { - if (!QDir(di.filePath()).removeRecursively()) result = false; - } else { - QString path = di.filePath(); - if (!path.endsWith(qstr("map0")) && !path.endsWith(qstr("map1")) && !path.endsWith(qstr("maps"))) { - if (!QFile::remove(di.filePath())) result = false; - } - } - } + // #TODO + //QDirIterator di(_userBasePath, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot); + //while (di.hasNext()) { + // di.next(); + // const QFileInfo& fi = di.fileInfo(); + // if (fi.isDir() && !fi.isSymLink()) { + // if (!QDir(di.filePath()).removeRecursively()) result = false; + // } else { + // QString path = di.filePath(); + // if (!path.endsWith(qstr("map0")) && !path.endsWith(qstr("map1")) && !path.endsWith(qstr("maps"))) { + // if (!QFile::remove(di.filePath())) result = false; + // } + // } + //} } break; case ClearManagerDownloads: result = QDir(cTempDir()).removeRecursively(); break; - case ClearManagerStorage: - result = true; - break; } { QMutexLocker lock(&data->mutex); @@ -5271,51 +1109,9 @@ void ClearManager::onStart() { namespace internal { Manager::Manager() { - _mapWriteTimer.setSingleShot(true); - connect(&_mapWriteTimer, SIGNAL(timeout()), this, SLOT(mapWriteTimeout())); - _locationsWriteTimer.setSingleShot(true); - connect(&_locationsWriteTimer, SIGNAL(timeout()), this, SLOT(locationsWriteTimeout())); -} - -void Manager::writeMap(bool fast) { - if (!_mapWriteTimer.isActive() || fast) { - _mapWriteTimer.start(fast ? 1 : kWriteMapTimeout); - } else if (_mapWriteTimer.remainingTime() <= 0) { - mapWriteTimeout(); - } -} - -void Manager::writingMap() { - _mapWriteTimer.stop(); -} - -void Manager::writeLocations(bool fast) { - if (!_locationsWriteTimer.isActive() || fast) { - _locationsWriteTimer.start(fast ? 1 : kWriteMapTimeout); - } else if (_locationsWriteTimer.remainingTime() <= 0) { - locationsWriteTimeout(); - } -} - -void Manager::writingLocations() { - _locationsWriteTimer.stop(); -} - -void Manager::mapWriteTimeout() { - _writeMap(WriteMapWhen::Now); -} - -void Manager::locationsWriteTimeout() { - _writeLocations(WriteMapWhen::Now); } void Manager::finish() { - if (_mapWriteTimer.isActive()) { - mapWriteTimeout(); - } - if (_locationsWriteTimer.isActive()) { - locationsWriteTimeout(); - } } } // namespace internal diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h index 34918bed5f..bf23d8ff15 100644 --- a/Telegram/SourceFiles/storage/localstorage.h +++ b/Telegram/SourceFiles/storage/localstorage.h @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "storage/file_download.h" -#include "storage/cache/storage_cache_database.h" #include "storage/localimageloader.h" #include "main/main_session.h" @@ -26,6 +25,9 @@ struct Language; } // namespace Lang namespace Storage { +namespace details { +struct ReadSettingsContext; +} // namespace details class EncryptionKey; } // namespace Storage @@ -45,23 +47,16 @@ namespace Local { void start(); void finish(); -void readSettings(); void writeSettings(); -void writeUserSettings(); -void writeMtpData(); void writeAutoupdatePrefix(const QString &prefix); QString readAutoupdatePrefix(); void reset(); -bool checkPasscode(const QByteArray &passcode); -void setPasscode(const QByteArray &passcode); - enum ClearManagerTask { ClearManagerAll = 0xFFFF, ClearManagerDownloads = 0x01, - ClearManagerStorage = 0x02, }; struct ClearManagerData; @@ -89,73 +84,12 @@ private: }; -enum ReadMapState { - ReadMapFailed = 0, - ReadMapDone = 1, - ReadMapPassNeeded = 2, -}; -ReadMapState readMap(const QByteArray &pass); -int32 oldMapVersion(); - int32 oldSettingsVersion(); -struct MessageDraft { - MessageDraft(MsgId msgId = 0, TextWithTags textWithTags = TextWithTags(), bool previewCancelled = false) - : msgId(msgId) - , textWithTags(textWithTags) - , previewCancelled(previewCancelled) { - } - MsgId msgId; - TextWithTags textWithTags; - bool previewCancelled; -}; -void writeDrafts(const PeerId &peer, const MessageDraft &localDraft, const MessageDraft &editDraft); -void readDraftsWithCursors(History *h); -void writeDraftCursors(const PeerId &peer, const MessageCursor &localCursor, const MessageCursor &editCursor); -bool hasDraftCursors(const PeerId &peer); -bool hasDraft(const PeerId &peer); - -void writeFileLocation(MediaKey location, const FileLocation &local); -FileLocation readFileLocation(MediaKey location); -void removeFileLocation(MediaKey location); - -Storage::EncryptionKey cacheKey(); -QString cachePath(); -Storage::Cache::Database::Settings cacheSettings(); -void updateCacheSettings( - Storage::Cache::Database::SettingsUpdate &update, - Storage::Cache::Database::SettingsUpdate &updateBig); - -Storage::EncryptionKey cacheBigFileKey(); -QString cacheBigFilePath(); -Storage::Cache::Database::Settings cacheBigFileSettings(); - void countVoiceWaveform(not_null media); void cancelTask(TaskId id); -void writeInstalledStickers(); -void writeFeaturedStickers(); -void writeRecentStickers(); -void writeFavedStickers(); -void writeArchivedStickers(); -void readInstalledStickers(); -void readFeaturedStickers(); -void readRecentStickers(); -void readFavedStickers(); -void readArchivedStickers(); -int32 countStickersHash(bool checkOutdatedInfo = false); -int32 countRecentStickersHash(); -int32 countFavedStickersHash(); -int32 countFeaturedStickersHash(); - -void writeSavedGifs(); -void readSavedGifs(); -int32 countSavedGifsHash(); - -void writeBackground(const Data::WallPaper &paper, const QImage &image); -bool readBackground(); - void writeTheme(const Window::Theme::Saved &saved); void clearTheme(); [[nodiscard]] Window::Theme::Saved readThemeAfterSwitch(); @@ -167,46 +101,23 @@ void pushRecentLanguage(const Lang::Language &language); std::vector readRecentLanguages(); void saveRecentLanguages(const std::vector &list); void removeRecentLanguage(const QString &id); +void incrementRecentHashtag(RecentHashtagPack &recent, const QString &tag); -void writeRecentHashtagsAndBots(); -void readRecentHashtagsAndBots(); -void saveRecentSentHashtags(const QString &text); -void saveRecentSearchHashtags(const QString &text); - -void WriteExportSettings(const Export::Settings &settings); -Export::Settings ReadExportSettings(); - -void writeSelf(); -void readSelf(const QByteArray &serialized, int32 streamVersion); - -void makeBotTrusted(UserData *bot); -bool isBotTrusted(UserData *bot); - -bool encrypt(const void *src, void *dst, uint32 len, const void *key128); -bool decrypt(const void *src, void *dst, uint32 len, const void *key128); +bool readOldMtpData( + bool remove, + Storage::details::ReadSettingsContext &context); +bool readOldUserSettings( + bool remove, + Storage::details::ReadSettingsContext &context); namespace internal { -class Manager : public QObject { - Q_OBJECT - +class Manager final : public QObject { public: Manager(); - void writeMap(bool fast); - void writingMap(); - void writeLocations(bool fast); - void writingLocations(); void finish(); -public slots: - void mapWriteTimeout(); - void locationsWriteTimeout(); - -private: - QTimer _mapWriteTimer; - QTimer _locationsWriteTimer; - }; } // namespace internal diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp new file mode 100644 index 0000000000..c277d2245b --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -0,0 +1,2531 @@ +/* +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_account.h" + +#include "storage/localstorage.h" +#include "storage/storage_encryption.h" +#include "storage/storage_clear_legacy.h" +#include "storage/cache/storage_cache_types.h" +#include "storage/details/storage_file_utilities.h" +#include "storage/details/storage_settings_scheme.h" +#include "storage/serialize_common.h" +#include "storage/serialize_document.h" +#include "main/main_account.h" +#include "main/main_session.h" +#include "mtproto/dc_options.h" +#include "history/history.h" +#include "core/application.h" +#include "data/stickers/data_stickers.h" +#include "data/data_session.h" +#include "data/data_document.h" +#include "data/data_user.h" +#include "data/data_drafts.h" +#include "export/export_settings.h" +#include "window/themes/window_theme.h" +#include "facades.h" + +namespace Storage { +namespace { + +using namespace details; +using Database = Cache::Database; + +constexpr auto kLocalKeySize = MTP::AuthKey::kSize; +constexpr auto kDelayedWriteTimeout = crl::time(1000); +constexpr auto kSavedBackgroundFormat = QImage::Format_ARGB32_Premultiplied; + +constexpr auto kStickersVersionTag = quint32(-1); +constexpr auto kStickersSerializeVersion = 1; +constexpr auto kMaxSavedStickerSetsCount = 1000; +constexpr auto kDefaultStickerInstallDate = TimeId(1); + +constexpr auto kWallPaperLegacySerializeTagId = int32(-111); +constexpr auto kWallPaperSerializeTagId = int32(-112); +constexpr auto kWallPaperSidesLimit = 10'000; + +constexpr auto kSinglePeerTypeUser = qint32(1); +constexpr auto kSinglePeerTypeChat = qint32(2); +constexpr auto kSinglePeerTypeChannel = qint32(3); +constexpr auto kSinglePeerTypeSelf = qint32(4); +constexpr auto kSinglePeerTypeEmpty = qint32(0); + +enum { // Local Storage Keys + lskUserMap = 0x00, + lskDraft = 0x01, // data: PeerId peer + lskDraftPosition = 0x02, // data: PeerId peer + lskLegacyImages = 0x03, // legacy + lskLocations = 0x04, // no data + lskLegacyStickerImages = 0x05, // legacy + lskLegacyAudios = 0x06, // legacy + lskRecentStickersOld = 0x07, // no data + lskBackgroundOld = 0x08, // no data + lskUserSettings = 0x09, // no data + lskRecentHashtagsAndBots = 0x0a, // no data + lskStickersOld = 0x0b, // no data + lskSavedPeersOld = 0x0c, // no data + lskReportSpamStatusesOld = 0x0d, // no data + lskSavedGifsOld = 0x0e, // no data + lskSavedGifs = 0x0f, // no data + lskStickersKeys = 0x10, // no data + lskTrustedBots = 0x11, // no data + lskFavedStickers = 0x12, // no data + lskExportSettings = 0x13, // no data + lskBackground = 0x14, // no data + lskSelfSerialized = 0x15, // serialized self +}; + +[[nodiscard]] FileKey ComputeDataNameKey(const QString &dataName) { + const auto testAddition = (cTestMode() ? qsl(":/test/") : QString()); + const auto dataNameUtf8 = (dataName + testAddition).toUtf8(); + FileKey dataNameHash[2] = { 0 }; + hashMd5(dataNameUtf8.constData(), dataNameUtf8.size(), dataNameHash); + return dataNameHash[0]; +} + +[[nodiscard]] QString BaseGlobalPath() { + return cWorkingDir() + qsl("tdata/"); +} + +[[nodiscard]] QString ComputeDatabasePath(const QString &dataName) { + return BaseGlobalPath() + + "user_" + dataName + + (cTestMode() ? "[test]" : "") + + '/'; +} + +} // namespace + +Account::Account(not_null owner, const QString &dataName) +: _owner(owner) +, _dataName(dataName) +, _dataNameKey(ComputeDataNameKey(dataName)) +, _basePath(BaseGlobalPath() + ToFilePart(_dataNameKey) + QChar('/')) +, _databasePath(ComputeDatabasePath(dataName)) +, _cacheTotalSizeLimit(Database::Settings().totalSizeLimit) +, _cacheBigFileTotalSizeLimit(Database::Settings().totalSizeLimit) +, _cacheTotalTimeLimit(Database::Settings().totalTimeLimit) +, _cacheBigFileTotalTimeLimit(Database::Settings().totalTimeLimit) +, _writeMapTimer([=] { writeMap(); }) +, _writeLocationsTimer([=] { writeLocations(); }) { +} + +Account::~Account() { + if (_localKey && _mapChanged) { + writeMap(); + } +} + +StartResult Account::start(const QByteArray &passcode) { + const auto result = readMap(passcode); + if (result == ReadMapResult::Failed) { + _mapChanged = true; + writeMap(); + } else if (result == ReadMapResult::IncorrectPasscode) { + return StartResult::IncorrectPasscode; + } + const auto weak = base::make_weak(_owner.get()); + ClearLegacyFiles(_basePath, [weak, this]( + FnMut&&)> then) { + crl::on_main(weak, [this, then = std::move(then)]() mutable { + then(collectGoodNames()); + }); + }); + + if (Local::oldSettingsVersion() < AppVersion) { + Local::writeSettings(); + } + + return StartResult::Success; +} + +base::flat_set Account::collectGoodNames() const { + const auto keys = { + _locationsKey, + _settingsKey, + _installedStickersKey, + _featuredStickersKey, + _recentStickersKey, + _favedStickersKey, + _archivedStickersKey, + _recentStickersKeyOld, + _savedGifsKey, + _backgroundKeyNight, + _backgroundKeyDay, + _recentHashtagsAndBotsKey, + _exportSettingsKey, + _trustedBotsKey, + }; + auto result = base::flat_set{ "map0", "map1", "maps" }; + const auto push = [&](FileKey key) { + if (!key) { + return; + } + auto name = ToFilePart(key) + '0'; + result.emplace(name); + name[name.size() - 1] = '1'; + result.emplace(name); + name[name.size() - 1] = 's'; + result.emplace(name); + }; + for (const auto &[key, value] : _draftsMap) { + push(value); + } + for (const auto &[key, value] : _draftCursorsMap) { + push(value); + } + for (const auto &value : keys) { + push(value); + } + return result; +} + +Account::ReadMapResult Account::readMap(const QByteArray &passcode) { + auto ms = crl::now(); + + FileReadDescriptor mapData; + if (!ReadFile(mapData, qsl("map"), _basePath)) { + return ReadMapResult::Failed; + } + LOG(("App Info: reading map...")); + + QByteArray salt, keyEncrypted, mapEncrypted; + mapData.stream >> salt >> keyEncrypted >> mapEncrypted; + if (!CheckStreamStatus(mapData.stream)) { + return ReadMapResult::Failed; + } + + if (salt.size() != LocalEncryptSaltSize) { + LOG(("App Error: bad salt in map file, size: %1").arg(salt.size())); + return ReadMapResult::Failed; + } + _passcodeKey = CreateLocalKey(passcode, salt); + + EncryptedDescriptor keyData, map; + if (!DecryptLocal(keyData, keyEncrypted, _passcodeKey)) { + LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password...")); + return ReadMapResult::IncorrectPasscode; + } + auto key = Serialize::read(keyData.stream); + if (keyData.stream.status() != QDataStream::Ok || !keyData.stream.atEnd()) { + LOG(("App Error: could not read pass-protected key from map file")); + return ReadMapResult::Failed; + } + _localKey = std::make_shared(key); + + _passcodeKeyEncrypted = keyEncrypted; + _passcodeKeySalt = salt; + + if (!DecryptLocal(map, mapEncrypted, _localKey)) { + LOG(("App Error: could not decrypt map.")); + return ReadMapResult::Failed; + } + LOG(("App Info: reading encrypted map...")); + + QByteArray selfSerialized; + base::flat_map draftsMap; + base::flat_map draftCursorsMap; + base::flat_map draftsNotReadMap; + quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedBotsKey = 0; + quint64 recentStickersKeyOld = 0; + quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0; + quint64 savedGifsKey = 0; + quint64 backgroundKeyDay = 0, backgroundKeyNight = 0; + quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0; + while (!map.stream.atEnd()) { + quint32 keyType; + map.stream >> keyType; + switch (keyType) { + case lskDraft: { + quint32 count = 0; + map.stream >> count; + for (quint32 i = 0; i < count; ++i) { + FileKey key; + quint64 p; + map.stream >> key >> p; + draftsMap.emplace(p, key); + draftsNotReadMap.emplace(p, true); + } + } break; + case lskSelfSerialized: { + map.stream >> selfSerialized; + } break; + case lskDraftPosition: { + quint32 count = 0; + map.stream >> count; + for (quint32 i = 0; i < count; ++i) { + FileKey key; + quint64 p; + map.stream >> key >> p; + draftCursorsMap.emplace(p, key); + } + } break; + case lskLegacyImages: + case lskLegacyStickerImages: + case lskLegacyAudios: { + quint32 count = 0; + map.stream >> count; + for (quint32 i = 0; i < count; ++i) { + FileKey key; + quint64 first, second; + qint32 size; + map.stream >> key >> first >> second >> size; + // Just ignore the key, it will be removed as a leaked one. + } + } break; + case lskLocations: { + map.stream >> locationsKey; + } break; + case lskReportSpamStatusesOld: { + map.stream >> reportSpamStatusesKey; + ClearKey(reportSpamStatusesKey, _basePath); + } break; + case lskTrustedBots: { + map.stream >> trustedBotsKey; + } break; + case lskRecentStickersOld: { + map.stream >> recentStickersKeyOld; + } break; + case lskBackgroundOld: { + map.stream >> (Window::Theme::IsNightMode() + ? backgroundKeyNight + : backgroundKeyDay); + } break; + case lskBackground: { + map.stream >> backgroundKeyDay >> backgroundKeyNight; + } break; + case lskUserSettings: { + map.stream >> userSettingsKey; + } break; + case lskRecentHashtagsAndBots: { + map.stream >> recentHashtagsAndBotsKey; + } break; + case lskStickersOld: { + map.stream >> installedStickersKey; + } break; + case lskStickersKeys: { + map.stream >> installedStickersKey >> featuredStickersKey >> recentStickersKey >> archivedStickersKey; + } break; + case lskFavedStickers: { + map.stream >> favedStickersKey; + } break; + case lskSavedGifsOld: { + quint64 key; + map.stream >> key; + } break; + case lskSavedGifs: { + map.stream >> savedGifsKey; + } break; + case lskSavedPeersOld: { + quint64 key; + map.stream >> key; + } break; + case lskExportSettings: { + map.stream >> exportSettingsKey; + } break; + default: + LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType)); + return ReadMapResult::Failed; + } + if (!CheckStreamStatus(map.stream)) { + return ReadMapResult::Failed; + } + } + + _draftsMap = draftsMap; + _draftCursorsMap = draftCursorsMap; + _draftsNotReadMap = draftsNotReadMap; + + _locationsKey = locationsKey; + _trustedBotsKey = trustedBotsKey; + _recentStickersKeyOld = recentStickersKeyOld; + _installedStickersKey = installedStickersKey; + _featuredStickersKey = featuredStickersKey; + _recentStickersKey = recentStickersKey; + _favedStickersKey = favedStickersKey; + _archivedStickersKey = archivedStickersKey; + _savedGifsKey = savedGifsKey; + _backgroundKeyDay = backgroundKeyDay; + _backgroundKeyNight = backgroundKeyNight; + _settingsKey = userSettingsKey; + _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey; + _exportSettingsKey = exportSettingsKey; + _oldMapVersion = mapData.version; + if (_oldMapVersion < AppVersion) { + writeMapDelayed(); + } else { + _mapChanged = false; + } + + if (_locationsKey) { + readLocations(); + } + + auto stored = readSettings(); + readMtpData(); + + DEBUG_LOG(("selfSerialized set: %1").arg(selfSerialized.size())); + _owner->setSessionFromStorage( + std::move(stored), + std::move(selfSerialized), + _oldMapVersion); + + LOG(("Map read time: %1").arg(crl::now() - ms)); + return ReadMapResult::Success; +} + +void Account::writeMapDelayed() { + _mapChanged = true; + _writeMapTimer.callOnce(kDelayedWriteTimeout); +} + +void Account::writeMapQueued() { + _mapChanged = true; + crl::on_main(_owner, [=] { + writeMap(); + }); +} + +void Account::writeMap() { + _writeMapTimer.cancel(); + if (!_mapChanged) { + return; + } + _mapChanged = false; + + if (!QDir().exists(_basePath)) { + QDir().mkpath(_basePath); + } + + FileWriteDescriptor map(u"map"_q, _basePath); + if (_passcodeKeySalt.isEmpty() || _passcodeKeyEncrypted.isEmpty()) { + auto pass = QByteArray(kLocalKeySize, Qt::Uninitialized); + auto salt = QByteArray(LocalEncryptSaltSize, Qt::Uninitialized); + memset_rand(pass.data(), pass.size()); + memset_rand(salt.data(), salt.size()); + _localKey = CreateLocalKey(pass, salt); + + _passcodeKeySalt.resize(LocalEncryptSaltSize); + memset_rand(_passcodeKeySalt.data(), _passcodeKeySalt.size()); + _passcodeKey = CreateLocalKey(QByteArray(), _passcodeKeySalt); + + EncryptedDescriptor passKeyData(kLocalKeySize); + _localKey->write(passKeyData.stream); + _passcodeKeyEncrypted = PrepareEncrypted(passKeyData, _passcodeKey); + } + map.writeData(_passcodeKeySalt); + map.writeData(_passcodeKeyEncrypted); + + uint32 mapSize = 0; + const auto self = [] { + if (!Main::Session::Exists()) { + DEBUG_LOG(("AuthSelf Warning: Session does not exist.")); + return QByteArray(); + } + const auto self = Auth().user(); + if (self->phone().isEmpty()) { + DEBUG_LOG(("AuthSelf Error: Phone is empty.")); + return QByteArray(); + } + auto result = QByteArray(); + result.reserve(Serialize::peerSize(self) + + Serialize::stringSize(self->about())); + { + QBuffer buffer(&result); + buffer.open(QIODevice::WriteOnly); + QDataStream stream(&buffer); + Serialize::writePeer(stream, self); + stream << self->about(); + } + return result; + }(); + if (!self.isEmpty()) mapSize += sizeof(quint32) + Serialize::bytearraySize(self); + if (!_draftsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftsMap.size() * sizeof(quint64) * 2; + if (!_draftCursorsMap.empty()) mapSize += sizeof(quint32) * 2 + _draftCursorsMap.size() * sizeof(quint64) * 2; + if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_trustedBotsKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_recentStickersKeyOld) mapSize += sizeof(quint32) + sizeof(quint64); + if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) { + mapSize += sizeof(quint32) + 4 * sizeof(quint64); + } + if (_favedStickersKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_savedGifsKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_backgroundKeyDay || _backgroundKeyNight) mapSize += sizeof(quint32) + sizeof(quint64) + sizeof(quint64); + if (_settingsKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_exportSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); + + EncryptedDescriptor mapData(mapSize); + if (!self.isEmpty()) { + mapData.stream << quint32(lskSelfSerialized) << self; + } + if (!_draftsMap.empty()) { + mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size()); + for (const auto &[key, value] : _draftsMap) { + mapData.stream << quint64(value) << quint64(key); + } + } + if (!_draftCursorsMap.empty()) { + mapData.stream << quint32(lskDraftPosition) << quint32(_draftCursorsMap.size()); + for (const auto &[key, value] : _draftCursorsMap) { + mapData.stream << quint64(value) << quint64(key); + } + } + if (_locationsKey) { + mapData.stream << quint32(lskLocations) << quint64(_locationsKey); + } + if (_trustedBotsKey) { + mapData.stream << quint32(lskTrustedBots) << quint64(_trustedBotsKey); + } + if (_recentStickersKeyOld) { + mapData.stream << quint32(lskRecentStickersOld) << quint64(_recentStickersKeyOld); + } + if (_installedStickersKey || _featuredStickersKey || _recentStickersKey || _archivedStickersKey) { + mapData.stream << quint32(lskStickersKeys); + mapData.stream << quint64(_installedStickersKey) << quint64(_featuredStickersKey) << quint64(_recentStickersKey) << quint64(_archivedStickersKey); + } + if (_favedStickersKey) { + mapData.stream << quint32(lskFavedStickers) << quint64(_favedStickersKey); + } + if (_savedGifsKey) { + mapData.stream << quint32(lskSavedGifs) << quint64(_savedGifsKey); + } + if (_backgroundKeyDay || _backgroundKeyNight) { + mapData.stream + << quint32(lskBackground) + << quint64(_backgroundKeyDay) + << quint64(_backgroundKeyNight); + } + if (_settingsKey) { + mapData.stream << quint32(lskUserSettings) << quint64(_settingsKey); + } + if (_recentHashtagsAndBotsKey) { + mapData.stream << quint32(lskRecentHashtagsAndBots) << quint64(_recentHashtagsAndBotsKey); + } + if (_exportSettingsKey) { + mapData.stream << quint32(lskExportSettings) << quint64(_exportSettingsKey); + } + map.writeEncrypted(mapData, _localKey); + + _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() { + _passcodeKeySalt.clear(); + _draftsMap.clear(); + _draftCursorsMap.clear(); + _draftsNotReadMap.clear(); + _locationsKey = _trustedBotsKey = 0; + _recentStickersKeyOld = 0; + _installedStickersKey = 0; + _featuredStickersKey = 0; + _recentStickersKey = 0; + _favedStickersKey = 0; + _archivedStickersKey = 0; + _savedGifsKey = 0; + _backgroundKeyDay = _backgroundKeyNight = 0; + _settingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0; + _oldMapVersion = 0; + _fileLocations.clear(); + _fileLocationPairs.clear(); + _fileLocationAliases.clear(); + _cacheTotalSizeLimit = Database::Settings().totalSizeLimit; + _cacheTotalTimeLimit = Database::Settings().totalTimeLimit; + _cacheBigFileTotalSizeLimit = Database::Settings().totalSizeLimit; + _cacheBigFileTotalTimeLimit = Database::Settings().totalTimeLimit; + _mapChanged = true; + writeMap(); +} + +void Account::writeLocations() { + _writeLocationsTimer.cancel(); + if (!_locationsChanged) { + return; + } + _locationsChanged = false; + + if (_fileLocations.isEmpty()) { + if (_locationsKey) { + ClearKey(_locationsKey, _basePath); + _locationsKey = 0; + writeMapDelayed(); + } + } else { + if (!_locationsKey) { + _locationsKey = GenerateKey(_basePath); + writeMapQueued(); + } + quint32 size = 0; + for (auto i = _fileLocations.cbegin(), e = _fileLocations.cend(); i != e; ++i) { + // location + type + namelen + name + size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(i.value().name()); + if (AppVersion > 9013) { + // bookmark + size += Serialize::bytearraySize(i.value().bookmark()); + } + // date + size + size += Serialize::dateTimeSize() + sizeof(quint32); + } + + //end mark + size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(QString()); + if (AppVersion > 9013) { + size += Serialize::bytearraySize(QByteArray()); + } + size += Serialize::dateTimeSize() + sizeof(quint32); + + size += sizeof(quint32); // aliases count + for (auto i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) { + // alias + location + size += sizeof(quint64) * 2 + sizeof(quint64) * 2; + } + + EncryptedDescriptor data(size); + auto legacyTypeField = 0; + for (auto i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) { + data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(legacyTypeField) << i.value().name(); + if (AppVersion > 9013) { + data.stream << i.value().bookmark(); + } + data.stream << i.value().modified << quint32(i.value().size); + } + + data.stream << quint64(0) << quint64(0) << quint32(0) << QString(); + if (AppVersion > 9013) { + data.stream << QByteArray(); + } + data.stream << QDateTime::currentDateTime() << quint32(0); + + data.stream << quint32(_fileLocationAliases.size()); + for (auto i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) { + data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second); + } + + FileWriteDescriptor file(_locationsKey, _basePath); + file.writeEncrypted(data, _localKey); + } +} + +void Account::writeLocationsQueued() { + _locationsChanged = true; + crl::on_main(_owner, [=] { + writeLocations(); + }); +} + +void Account::writeLocationsDelayed() { + _locationsChanged = true; + _writeLocationsTimer.callOnce(kDelayedWriteTimeout); +} + +void Account::readLocations() { + FileReadDescriptor locations; + if (!ReadEncryptedFile(locations, _locationsKey, _basePath, _localKey)) { + ClearKey(_locationsKey, _basePath); + _locationsKey = 0; + writeMapDelayed(); + return; + } + + bool endMarkFound = false; + while (!locations.stream.atEnd()) { + quint64 first, second; + QByteArray bookmark; + FileLocation loc; + quint32 legacyTypeField = 0; + locations.stream >> first >> second >> legacyTypeField >> loc.fname; + if (locations.version > 9013) { + locations.stream >> bookmark; + } + locations.stream >> loc.modified >> loc.size; + loc.setBookmark(bookmark); + + if (!first && !second && !legacyTypeField && loc.fname.isEmpty() && !loc.size) { // end mark + endMarkFound = true; + break; + } + + MediaKey key(first, second); + + _fileLocations.insert(key, loc); + if (!loc.inMediaCache()) { + _fileLocationPairs.insert(loc.fname, { key, loc }); + } + } + + if (endMarkFound) { + quint32 cnt; + locations.stream >> cnt; + for (quint32 i = 0; i < cnt; ++i) { + quint64 kfirst, ksecond, vfirst, vsecond; + locations.stream >> kfirst >> ksecond >> vfirst >> vsecond; + _fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond)); + } + + if (!locations.stream.atEnd()) { + quint32 webLocationsCount; + locations.stream >> webLocationsCount; + for (quint32 i = 0; i < webLocationsCount; ++i) { + QString url; + quint64 key; + qint32 size; + locations.stream >> url >> key >> size; + ClearKey(key, _basePath); + } + } + } +} + +QByteArray Account::serializeCallSettings() { + QByteArray result = QByteArray(); + uint32 size = 3 * sizeof(qint32) + Serialize::stringSize(Global::CallOutputDeviceID()) + Serialize::stringSize(Global::CallInputDeviceID()); + result.reserve(size); + QDataStream stream(&result, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_5_1); + stream << Global::CallOutputDeviceID(); + stream << qint32(Global::CallOutputVolume()); + stream << Global::CallInputDeviceID(); + stream << qint32(Global::CallInputVolume()); + stream << qint32(Global::CallAudioDuckingEnabled() ? 1 : 0); + return result; +} + +void Account::deserializeCallSettings(QByteArray &settings) { + QDataStream stream(&settings, QIODevice::ReadOnly); + stream.setVersion(QDataStream::Qt_5_1); + QString outputDeviceID; + QString inputDeviceID; + qint32 outputVolume; + qint32 inputVolume; + qint32 duckingEnabled; + + stream >> outputDeviceID; + stream >> outputVolume; + stream >> inputDeviceID; + stream >> inputVolume; + stream >> duckingEnabled; + if (CheckStreamStatus(stream)) { + Global::SetCallOutputDeviceID(outputDeviceID); + Global::SetCallOutputVolume(outputVolume); + Global::SetCallInputDeviceID(inputDeviceID); + Global::SetCallInputVolume(inputVolume); + Global::SetCallAudioDuckingEnabled(duckingEnabled); + } +} + +void Account::writeSettings() { + writeSettings(nullptr); +} + +void Account::writeSettings(Main::Settings *stored) { + if (_readingUserSettings) { + LOG(("App Error: attempt to write settings while reading them!")); + return; + } + LOG(("App Info: writing encrypted user settings...")); + + if (!_settingsKey) { + _settingsKey = GenerateKey(_basePath); + writeMapQueued(); + } + + auto recentEmojiPreloadData = cRecentEmojiPreload(); + if (recentEmojiPreloadData.isEmpty()) { + recentEmojiPreloadData.reserve(GetRecentEmoji().size()); + for (auto &item : GetRecentEmoji()) { + recentEmojiPreloadData.push_back(qMakePair(item.first->id(), item.second)); + } + } + auto userDataInstance = stored + ? stored + : _owner->getSessionSettings(); + auto userData = userDataInstance + ? userDataInstance->serialize() + : QByteArray(); + auto callSettings = serializeCallSettings(); + + auto recentStickers = cRecentStickersPreload(); + if (recentStickers.isEmpty() && _owner->sessionExists()) { + const auto &stickers = _owner->session().data().stickers(); + recentStickers.reserve(stickers.getRecentPack().size()); + for (const auto &pair : std::as_const(stickers.getRecentPack())) { + recentStickers.push_back(qMakePair(pair.first->id, pair.second)); + } + } + + uint32 size = 24 * (sizeof(quint32) + sizeof(qint32)); + size += sizeof(quint32) + Serialize::stringSize(Global::AskDownloadPath() ? QString() : Global::DownloadPath()) + Serialize::bytearraySize(Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark()); + + size += sizeof(quint32) + sizeof(qint32); + for (auto &item : recentEmojiPreloadData) { + size += Serialize::stringSize(item.first) + sizeof(item.second); + } + + size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64)); + size += sizeof(quint32) + sizeof(qint32) + recentStickers.size() * (sizeof(uint64) + sizeof(ushort)); + size += sizeof(quint32) + 3 * sizeof(qint32); + size += sizeof(quint32) + 2 * sizeof(qint32); + size += sizeof(quint32) + sizeof(qint64) + sizeof(qint32); + if (!Global::HiddenPinnedMessages().isEmpty()) { + size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId)); + } + if (!userData.isEmpty()) { + size += sizeof(quint32) + Serialize::bytearraySize(userData); + } + size += sizeof(quint32) + Serialize::bytearraySize(callSettings); + + const auto soundFlashBounce = (Global::SoundNotify() ? 0x01 : 0x00) + | (Global::FlashBounceNotify() ? 0x00 : 0x02); + + EncryptedDescriptor data(size); + data.stream + << quint32(dbiTileBackground) + << qint32(Window::Theme::Background()->tileDay() ? 1 : 0) + << qint32(Window::Theme::Background()->tileNight() ? 1 : 0); + data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0); + data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock()); + data.stream << quint32(dbiSoundFlashBounceNotify) << qint32(soundFlashBounce); + data.stream << quint32(dbiDesktopNotify) << qint32(Global::DesktopNotify()); + data.stream << quint32(dbiNotifyView) << qint32(Global::NotifyView()); + data.stream << quint32(dbiNativeNotifications) << qint32(Global::NativeNotifications()); + data.stream << quint32(dbiNotificationsCount) << qint32(Global::NotificationsCount()); + data.stream << quint32(dbiNotificationsCorner) << qint32(Global::NotificationsCorner()); + data.stream << quint32(dbiAskDownloadPath) << qint32(Global::AskDownloadPath()); + data.stream << quint32(dbiDownloadPath) << (Global::AskDownloadPath() ? QString() : Global::DownloadPath()) << (Global::AskDownloadPath() ? QByteArray() : Global::DownloadPathBookmark()); + data.stream << quint32(dbiSongVolume) << qint32(qRound(Global::SongVolume() * 1e6)); + data.stream << quint32(dbiVideoVolume) << qint32(qRound(Global::VideoVolume() * 1e6)); + data.stream << quint32(dbiDialogsFilters) << qint32(Global::DialogsFiltersEnabled() ? 1 : 0); + data.stream << quint32(dbiModerateMode) << qint32(Global::ModerateModeEnabled() ? 1 : 0); + data.stream << quint32(dbiUseExternalVideoPlayer) << qint32(cUseExternalVideoPlayer()); + data.stream << quint32(dbiCacheSettings) << qint64(_cacheTotalSizeLimit) << qint32(_cacheTotalTimeLimit) << qint64(_cacheBigFileTotalSizeLimit) << qint32(_cacheBigFileTotalTimeLimit); + if (!userData.isEmpty()) { + data.stream << quint32(dbiSessionSettings) << userData; + } + data.stream << quint32(dbiPlaybackSpeed) << qint32(Global::VoiceMsgPlaybackDoubled() ? 2 : 1); + data.stream << quint32(dbiRecentEmoji) << recentEmojiPreloadData; + data.stream << quint32(dbiEmojiVariants) << cEmojiVariants(); + data.stream << quint32(dbiRecentStickers) << recentStickers; + if (!Global::HiddenPinnedMessages().isEmpty()) { + data.stream << quint32(dbiHiddenPinnedMessages) << Global::HiddenPinnedMessages(); + } + data.stream << qint32(dbiCallSettings) << callSettings; + + FileWriteDescriptor file(_settingsKey, _basePath); + file.writeEncrypted(data, _localKey); +} + +ReadSettingsContext Account::prepareReadSettingsContext() const { + return ReadSettingsContext{ + .hasCustomDayBackground = (_backgroundKeyDay != 0) + }; +} + +std::unique_ptr Account::readSettings() { + ReadSettingsContext context; + FileReadDescriptor userSettings; + if (!ReadEncryptedFile(userSettings, _settingsKey, _basePath, _localKey)) { + LOG(("App Info: could not read encrypted user settings...")); + + Local::readOldUserSettings(true, context); + auto result = applyReadContext(std::move(context)); + + writeSettings(result.get()); + + return result; + } + + LOG(("App Info: reading encrypted user settings...")); + _readingUserSettings = true; + while (!userSettings.stream.atEnd()) { + quint32 blockId; + userSettings.stream >> blockId; + if (!CheckStreamStatus(userSettings.stream)) { + _readingUserSettings = false; + writeSettings(); + return nullptr; + } + + if (!ReadSetting(blockId, userSettings.stream, userSettings.version, context)) { + _readingUserSettings = false; + writeSettings(); + return nullptr; + } + } + _readingUserSettings = false; + LOG(("App Info: encrypted user settings read.")); + + return applyReadContext(std::move(context)); +} + +std::unique_ptr Account::applyReadContext( + ReadSettingsContext &&context) { + Core::App().dcOptions()->addFromOther(std::move(context.dcOptions)); + + _cacheTotalSizeLimit = context.cacheTotalSizeLimit; + _cacheTotalTimeLimit = context.cacheTotalTimeLimit; + _cacheBigFileTotalSizeLimit = context.cacheBigFileTotalSizeLimit; + _cacheBigFileTotalTimeLimit = context.cacheBigFileTotalTimeLimit; + + deserializeCallSettings(context.callSettings); + + return std::move(context.sessionSettingsStorage); +} + +void Account::writeMtpData() { + FileWriteDescriptor mtp(ToFilePart(_dataNameKey), BaseGlobalPath()); + if (!_localKey) { + LOG(("App Error: localkey not created in _writeMtpData()")); + return; + } + + auto mtpAuthorizationSerialized = Core::App().activeAccount().serializeMtpAuthorization(); + + quint32 size = sizeof(quint32) + Serialize::bytearraySize(mtpAuthorizationSerialized); + + EncryptedDescriptor data(size); + data.stream << quint32(dbiMtpAuthorization) << mtpAuthorizationSerialized; + mtp.writeEncrypted(data, _localKey); +} + +void Account::readMtpData() { + auto context = prepareReadSettingsContext(); + + FileReadDescriptor mtp; + if (!ReadEncryptedFile(mtp, ToFilePart(_dataNameKey), BaseGlobalPath(), _localKey)) { + if (_localKey) { + Local::readOldMtpData(true, context); + applyReadContext(std::move(context)); + writeMtpData(); + } + return; + } + + LOG(("App Info: reading encrypted mtp data...")); + while (!mtp.stream.atEnd()) { + quint32 blockId; + mtp.stream >> blockId; + if (!CheckStreamStatus(mtp.stream)) { + return writeMtpData(); + } + + if (!ReadSetting(blockId, mtp.stream, mtp.version, context)) { + return writeMtpData(); + } + } + applyReadContext(std::move(context)); +} + +void Account::writeDrafts( + const PeerId &peer, + const MessageDraft &localDraft, + const MessageDraft &editDraft) { + if (localDraft.msgId <= 0 && localDraft.textWithTags.text.isEmpty() && editDraft.msgId <= 0) { + auto i = _draftsMap.find(peer); + if (i != _draftsMap.cend()) { + ClearKey(i->second, _basePath); + _draftsMap.erase(i); + writeMapDelayed(); + } + + _draftsNotReadMap.remove(peer); + } else { + auto i = _draftsMap.find(peer); + if (i == _draftsMap.cend()) { + i = _draftsMap.emplace(peer, GenerateKey(_basePath)).first; + writeMapQueued(); + } + + auto msgTags = TextUtilities::SerializeTags( + localDraft.textWithTags.tags); + auto editTags = TextUtilities::SerializeTags( + editDraft.textWithTags.tags); + + int size = sizeof(quint64); + size += Serialize::stringSize(localDraft.textWithTags.text) + Serialize::bytearraySize(msgTags) + 2 * sizeof(qint32); + size += Serialize::stringSize(editDraft.textWithTags.text) + Serialize::bytearraySize(editTags) + 2 * sizeof(qint32); + + EncryptedDescriptor data(size); + data.stream << quint64(peer); + data.stream << localDraft.textWithTags.text << msgTags; + data.stream << qint32(localDraft.msgId) << qint32(localDraft.previewCancelled ? 1 : 0); + data.stream << editDraft.textWithTags.text << editTags; + data.stream << qint32(editDraft.msgId) << qint32(editDraft.previewCancelled ? 1 : 0); + + FileWriteDescriptor file(i->second, _basePath); + file.writeEncrypted(data, _localKey); + + _draftsNotReadMap.remove(peer); + } +} + +void Account::clearDraftCursors(const PeerId &peer) { + const auto i = _draftCursorsMap.find(peer); + if (i != _draftCursorsMap.cend()) { + ClearKey(i->second, _basePath); + _draftCursorsMap.erase(i); + writeMapDelayed(); + } +} + +void Account::readDraftCursors( + const PeerId &peer, + MessageCursor &localCursor, + MessageCursor &editCursor) { + const auto j = _draftCursorsMap.find(peer); + if (j == _draftCursorsMap.cend()) { + return; + } + + FileReadDescriptor draft; + if (!ReadEncryptedFile(draft, j->second, _basePath, _localKey)) { + clearDraftCursors(peer); + return; + } + quint64 draftPeer; + qint32 localPosition = 0, localAnchor = 0, localScroll = QFIXED_MAX; + qint32 editPosition = 0, editAnchor = 0, editScroll = QFIXED_MAX; + draft.stream >> draftPeer >> localPosition >> localAnchor >> localScroll; + if (!draft.stream.atEnd()) { + draft.stream >> editPosition >> editAnchor >> editScroll; + } + + if (draftPeer != peer) { + clearDraftCursors(peer); + return; + } + + localCursor = MessageCursor(localPosition, localAnchor, localScroll); + editCursor = MessageCursor(editPosition, editAnchor, editScroll); +} + +void Account::readDraftsWithCursors(not_null history) { + PeerId peer = history->peer->id; + if (!_draftsNotReadMap.remove(peer)) { + clearDraftCursors(peer); + return; + } + + const auto j = _draftsMap.find(peer); + if (j == _draftsMap.cend()) { + clearDraftCursors(peer); + return; + } + FileReadDescriptor draft; + if (!ReadEncryptedFile(draft, j->second, _basePath, _localKey)) { + ClearKey(j->second, _basePath); + _draftsMap.erase(j); + clearDraftCursors(peer); + return; + } + + quint64 draftPeer = 0; + TextWithTags msgData, editData; + QByteArray msgTagsSerialized, editTagsSerialized; + qint32 msgReplyTo = 0, msgPreviewCancelled = 0, editMsgId = 0, editPreviewCancelled = 0; + draft.stream >> draftPeer >> msgData.text; + if (draft.version >= 9048) { + draft.stream >> msgTagsSerialized; + } + if (draft.version >= 7021) { + draft.stream >> msgReplyTo; + if (draft.version >= 8001) { + draft.stream >> msgPreviewCancelled; + if (!draft.stream.atEnd()) { + draft.stream >> editData.text; + if (draft.version >= 9048) { + draft.stream >> editTagsSerialized; + } + draft.stream >> editMsgId >> editPreviewCancelled; + } + } + } + if (draftPeer != peer) { + ClearKey(j->second, _basePath); + _draftsMap.erase(j); + clearDraftCursors(peer); + return; + } + + msgData.tags = TextUtilities::DeserializeTags( + msgTagsSerialized, + msgData.text.size()); + editData.tags = TextUtilities::DeserializeTags( + editTagsSerialized, + editData.text.size()); + + MessageCursor msgCursor, editCursor; + readDraftCursors(peer, msgCursor, editCursor); + + if (!history->localDraft()) { + if (msgData.text.isEmpty() && !msgReplyTo) { + history->clearLocalDraft(); + } else { + history->setLocalDraft(std::make_unique( + msgData, + msgReplyTo, + msgCursor, + msgPreviewCancelled)); + } + } + if (!editMsgId) { + history->clearEditDraft(); + } else { + history->setEditDraft(std::make_unique( + editData, + editMsgId, + editCursor, + editPreviewCancelled)); + } +} + +void Account::writeDraftCursors( + const PeerId &peer, + const MessageCursor &msgCursor, + const MessageCursor &editCursor) { + if (msgCursor == MessageCursor() && editCursor == MessageCursor()) { + clearDraftCursors(peer); + } else { + auto i = _draftCursorsMap.find(peer); + if (i == _draftCursorsMap.cend()) { + i = _draftCursorsMap.emplace(peer, GenerateKey(_basePath)).first; + writeMapQueued(); + } + + EncryptedDescriptor data(sizeof(quint64) + sizeof(qint32) * 3); + data.stream + << quint64(peer) + << qint32(msgCursor.position) + << qint32(msgCursor.anchor) + << qint32(msgCursor.scroll); + data.stream + << qint32(editCursor.position) + << qint32(editCursor.anchor) + << qint32(editCursor.scroll); + + FileWriteDescriptor file(i->second, _basePath); + file.writeEncrypted(data, _localKey); + } +} + +bool Account::hasDraftCursors(const PeerId &peer) { + return _draftCursorsMap.contains(peer); +} + +bool Account::hasDraft(const PeerId &peer) { + return _draftsMap.contains(peer); +} + +void Account::writeFileLocation(MediaKey location, const FileLocation &local) { + if (local.fname.isEmpty()) { + return; + } + if (!local.inMediaCache()) { + const auto aliasIt = _fileLocationAliases.constFind(location); + if (aliasIt != _fileLocationAliases.cend()) { + location = aliasIt.value(); + } + + const auto i = _fileLocationPairs.find(local.fname); + if (i != _fileLocationPairs.cend()) { + if (i.value().second == local) { + if (i.value().first != location) { + _fileLocationAliases.insert(location, i.value().first); + writeLocationsQueued(); + } + return; + } + if (i.value().first != location) { + for (auto j = _fileLocations.find(i.value().first), e = _fileLocations.end(); (j != e) && (j.key() == i.value().first); ++j) { + if (j.value() == i.value().second) { + _fileLocations.erase(j); + break; + } + } + _fileLocationPairs.erase(i); + } + } + _fileLocationPairs.insert(local.fname, { location, local }); + } else { + for (auto i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { + if (i.value().inMediaCache() || i.value().check()) { + return; + } + i = _fileLocations.erase(i); + } + } + _fileLocations.insert(location, local); + writeLocationsQueued(); +} + +void Account::removeFileLocation(MediaKey location) { + auto i = _fileLocations.find(location); + if (i == _fileLocations.end()) { + return; + } + while (i != _fileLocations.end() && (i.key() == location)) { + i = _fileLocations.erase(i); + } + writeLocationsQueued(); +} + +FileLocation Account::readFileLocation(MediaKey location) { + const auto aliasIt = _fileLocationAliases.constFind(location); + if (aliasIt != _fileLocationAliases.cend()) { + location = aliasIt.value(); + } + + for (auto i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { + if (!i.value().inMediaCache() && !i.value().check()) { + _fileLocationPairs.remove(i.value().fname); + i = _fileLocations.erase(i); + writeLocationsDelayed(); + continue; + } + return i.value(); + } + return FileLocation(); +} + +EncryptionKey Account::cacheKey() const { + Expects(_localKey != nullptr); + + return EncryptionKey(bytes::make_vector(_localKey->data())); +} + +EncryptionKey Account::cacheBigFileKey() const { + return cacheKey(); +} + +QString Account::cachePath() const { + Expects(!_databasePath.isEmpty()); + + return _databasePath + "cache"; +} + +Cache::Database::Settings Account::cacheSettings() const { + auto result = Cache::Database::Settings(); + result.clearOnWrongKey = true; + result.totalSizeLimit = _cacheTotalSizeLimit; + result.totalTimeLimit = _cacheTotalTimeLimit; + result.maxDataSize = kMaxFileInMemory; + return result; +} + +void Account::updateCacheSettings( + Cache::Database::SettingsUpdate &update, + Cache::Database::SettingsUpdate &updateBig) { + Expects(update.totalSizeLimit > Database::Settings().maxDataSize); + Expects(update.totalTimeLimit >= 0); + Expects(updateBig.totalSizeLimit > Database::Settings().maxDataSize); + Expects(updateBig.totalTimeLimit >= 0); + + if (_cacheTotalSizeLimit == update.totalSizeLimit + && _cacheTotalTimeLimit == update.totalTimeLimit + && _cacheBigFileTotalSizeLimit == updateBig.totalSizeLimit + && _cacheBigFileTotalTimeLimit == updateBig.totalTimeLimit) { + return; + } + _cacheTotalSizeLimit = update.totalSizeLimit; + _cacheTotalTimeLimit = update.totalTimeLimit; + _cacheBigFileTotalSizeLimit = updateBig.totalSizeLimit; + _cacheBigFileTotalTimeLimit = updateBig.totalTimeLimit; + writeSettings(); +} + +QString Account::cacheBigFilePath() const { + Expects(!_databasePath.isEmpty()); + + return _databasePath + "media_cache"; +} + +Cache::Database::Settings Account::cacheBigFileSettings() const { + auto result = Cache::Database::Settings(); + result.clearOnWrongKey = true; + result.totalSizeLimit = _cacheBigFileTotalSizeLimit; + result.totalTimeLimit = _cacheBigFileTotalTimeLimit; + result.maxDataSize = kMaxFileInMemory; + return result; +} + +void Account::writeStickerSet( + QDataStream &stream, + const Data::StickersSet &set) { + const auto writeInfo = [&](int count) { + stream + << quint64(set.id) + << quint64(set.access) + << set.title + << set.shortName + << qint32(count) + << qint32(set.hash) + << qint32(set.flags) + << qint32(set.installDate); + Serialize::writeImageLocation(stream, set.thumbnailLocation()); + }; + if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { + writeInfo(-set.count); + return; + } else if (set.stickers.isEmpty()) { + return; + } + + writeInfo(set.stickers.size()); + for (const auto &sticker : set.stickers) { + Serialize::Document::writeToStream(stream, sticker); + } + stream << qint32(set.dates.size()); + if (!set.dates.empty()) { + Assert(set.dates.size() == set.stickers.size()); + for (const auto date : set.dates) { + stream << qint32(date); + } + } + stream << qint32(set.emoji.size()); + for (auto j = set.emoji.cbegin(), e = set.emoji.cend(); j != e; ++j) { + stream << j.key()->id() << qint32(j->size()); + for (const auto sticker : *j) { + stream << quint64(sticker->id); + } + } +} + +// In generic method _writeStickerSets() we look through all the sets and call a +// callback on each set to see, if we write it, skip it or abort the whole write. +enum class StickerSetCheckResult { + Write, + Skip, + Abort, +}; + +// CheckSet is a functor on Data::StickersSet, which returns a StickerSetCheckResult. +template +void Account::writeStickerSets( + FileKey &stickersKey, + CheckSet checkSet, + const Data::StickersSetsOrder &order) { + const auto &sets = Auth().data().stickers().sets(); + if (sets.empty()) { + if (stickersKey) { + ClearKey(stickersKey, _basePath); + stickersKey = 0; + writeMapDelayed(); + } + return; + } + + // versionTag + version + count + quint32 size = sizeof(quint32) + sizeof(qint32) + sizeof(qint32); + + int32 setsCount = 0; + for (const auto &[id, set] : sets) { + const auto raw = set.get(); + auto result = checkSet(*raw); + if (result == StickerSetCheckResult::Abort) { + return; + } else if (result == StickerSetCheckResult::Skip) { + continue; + } + + // id + access + title + shortName + stickersCount + hash + flags + installDate + size += sizeof(quint64) * 2 + + Serialize::stringSize(raw->title) + + Serialize::stringSize(raw->shortName) + + sizeof(qint32) * 4 + + Serialize::imageLocationSize(raw->thumbnailLocation()); + if (raw->flags & MTPDstickerSet_ClientFlag::f_not_loaded) { + continue; + } + + for (const auto sticker : raw->stickers) { + size += Serialize::Document::sizeInStream(sticker); + } + + size += sizeof(qint32); // datesCount + if (!raw->dates.empty()) { + Assert(raw->stickers.size() == raw->dates.size()); + size += raw->dates.size() * sizeof(qint32); + } + + size += sizeof(qint32); // emojiCount + for (auto j = raw->emoji.cbegin(), e = raw->emoji.cend(); j != e; ++j) { + size += Serialize::stringSize(j.key()->id()) + sizeof(qint32) + (j->size() * sizeof(quint64)); + } + + ++setsCount; + } + if (!setsCount && order.isEmpty()) { + if (stickersKey) { + ClearKey(stickersKey, _basePath); + stickersKey = 0; + writeMapDelayed(); + } + return; + } + size += sizeof(qint32) + (order.size() * sizeof(quint64)); + + if (!stickersKey) { + stickersKey = GenerateKey(_basePath); + writeMapQueued(); + } + EncryptedDescriptor data(size); + data.stream + << quint32(kStickersVersionTag) + << qint32(kStickersSerializeVersion) + << qint32(setsCount); + for (const auto &[id, set] : sets) { + auto result = checkSet(*set); + if (result == StickerSetCheckResult::Abort) { + return; + } else if (result == StickerSetCheckResult::Skip) { + continue; + } + writeStickerSet(data.stream, *set); + } + data.stream << order; + + FileWriteDescriptor file(stickersKey, _basePath); + file.writeEncrypted(data, _localKey); +} + +void Account::readStickerSets( + FileKey &stickersKey, + Data::StickersSetsOrder *outOrder, + MTPDstickerSet::Flags readingFlags) { + FileReadDescriptor stickers; + if (!ReadEncryptedFile(stickers, stickersKey, _basePath, _localKey)) { + ClearKey(stickersKey, _basePath); + stickersKey = 0; + writeMapDelayed(); + return; + } + + const auto failed = [&] { + ClearKey(stickersKey, _basePath); + stickersKey = 0; + }; + + auto &sets = Auth().data().stickers().setsRef(); + if (outOrder) outOrder->clear(); + + quint32 versionTag = 0; + qint32 version = 0; + stickers.stream >> versionTag >> version; + if (versionTag != kStickersVersionTag + || version != kStickersSerializeVersion) { + // Old data, without sticker set thumbnails. + return failed(); + } + qint32 count = 0; + stickers.stream >> count; + if (!CheckStreamStatus(stickers.stream) + || (count < 0) + || (count > kMaxSavedStickerSetsCount)) { + return failed(); + } + for (auto i = 0; i != count; ++i) { + using LocationType = StorageFileLocation::Type; + + quint64 setId = 0, setAccess = 0; + QString setTitle, setShortName; + qint32 scnt = 0; + qint32 setInstallDate = 0; + qint32 setHash = 0; + MTPDstickerSet::Flags setFlags = 0; + qint32 setFlagsValue = 0; + ImageLocation setThumbnail; + + stickers.stream + >> setId + >> setAccess + >> setTitle + >> setShortName + >> scnt + >> setHash + >> setFlagsValue + >> setInstallDate; + const auto thumbnail = Serialize::readImageLocation( + stickers.version, + stickers.stream); + if (!thumbnail || !CheckStreamStatus(stickers.stream)) { + return failed(); + } else if (thumbnail->valid() && thumbnail->isLegacy()) { + setThumbnail = thumbnail->convertToModern( + LocationType::StickerSetThumb, + setId, + setAccess); + } else { + setThumbnail = *thumbnail; + } + + setFlags = MTPDstickerSet::Flags::from_raw(setFlagsValue); + if (setId == Data::Stickers::DefaultSetId) { + setTitle = tr::lng_stickers_default_set(tr::now); + setFlags |= MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special; + } else if (setId == Data::Stickers::CustomSetId) { + setTitle = qsl("Custom stickers"); + setFlags |= MTPDstickerSet_ClientFlag::f_special; + } else if (setId == Data::Stickers::CloudRecentSetId) { + setTitle = tr::lng_recent_stickers(tr::now); + setFlags |= MTPDstickerSet_ClientFlag::f_special; + } else if (setId == Data::Stickers::FavedSetId) { + setTitle = Lang::Hard::FavedSetTitle(); + setFlags |= MTPDstickerSet_ClientFlag::f_special; + } else if (!setId) { + continue; + } + + auto it = sets.find(setId); + if (it == sets.cend()) { + // We will set this flags from order lists when reading those stickers. + setFlags &= ~(MTPDstickerSet::Flag::f_installed_date | MTPDstickerSet_ClientFlag::f_featured); + it = sets.emplace(setId, std::make_unique( + &Auth().data(), + setId, + setAccess, + setTitle, + setShortName, + 0, + setHash, + MTPDstickerSet::Flags(setFlags), + setInstallDate)).first; + it->second->setThumbnail( + ImageWithLocation{ .location = setThumbnail }); + } + const auto set = it->second.get(); + auto inputSet = MTP_inputStickerSetID(MTP_long(set->id), MTP_long(set->access)); + const auto fillStickers = set->stickers.isEmpty(); + + if (scnt < 0) { // disabled not loaded set + if (!set->count || fillStickers) { + set->count = -scnt; + } + continue; + } + + if (fillStickers) { + set->stickers.reserve(scnt); + set->count = 0; + } + + Serialize::Document::StickerSetInfo info(setId, setAccess, setShortName); + base::flat_set read; + for (int32 j = 0; j < scnt; ++j) { + auto document = Serialize::Document::readStickerFromStream(stickers.version, stickers.stream, info); + if (!CheckStreamStatus(stickers.stream)) { + return failed(); + } else if (!document + || !document->sticker() + || read.contains(document->id)) { + continue; + } + read.emplace(document->id); + if (fillStickers) { + set->stickers.push_back(document); + if (!(set->flags & MTPDstickerSet_ClientFlag::f_special)) { + if (document->sticker()->set.type() != mtpc_inputStickerSetID) { + document->sticker()->set = inputSet; + } + } + ++set->count; + } + } + + qint32 datesCount = 0; + stickers.stream >> datesCount; + if (datesCount > 0) { + if (datesCount != scnt) { + return failed(); + } + const auto fillDates = (set->id == Data::Stickers::CloudRecentSetId) + && (set->stickers.size() == datesCount); + if (fillDates) { + set->dates.clear(); + set->dates.reserve(datesCount); + } + for (auto i = 0; i != datesCount; ++i) { + qint32 date = 0; + stickers.stream >> date; + if (fillDates) { + set->dates.push_back(TimeId(date)); + } + } + } + + qint32 emojiCount = 0; + stickers.stream >> emojiCount; + if (!CheckStreamStatus(stickers.stream) || emojiCount < 0) { + return failed(); + } + for (int32 j = 0; j < emojiCount; ++j) { + QString emojiString; + qint32 stickersCount; + stickers.stream >> emojiString >> stickersCount; + Data::StickersPack pack; + pack.reserve(stickersCount); + for (int32 k = 0; k < stickersCount; ++k) { + quint64 id; + stickers.stream >> id; + const auto doc = Auth().data().document(id); + if (!doc->sticker()) continue; + + pack.push_back(doc); + } + if (fillStickers) { + if (auto emoji = Ui::Emoji::Find(emojiString)) { + emoji = emoji->original(); + set->emoji.insert(emoji, pack); + } + } + } + } + + // Read orders of installed and featured stickers. + if (outOrder) { + auto outOrderCount = quint32(); + stickers.stream >> outOrderCount; + if (!CheckStreamStatus(stickers.stream) || outOrderCount > 1000) { + return failed(); + } + outOrder->reserve(outOrderCount); + for (auto i = 0; i != outOrderCount; ++i) { + auto value = uint64(); + stickers.stream >> value; + if (!CheckStreamStatus(stickers.stream)) { + outOrder->clear(); + return failed(); + } + outOrder->push_back(value); + } + } + if (!CheckStreamStatus(stickers.stream)) { + return failed(); + } + + // Set flags that we dropped above from the order. + if (readingFlags && outOrder) { + for (const auto setId : std::as_const(*outOrder)) { + auto it = sets.find(setId); + if (it != sets.cend()) { + const auto set = it->second.get(); + set->flags |= readingFlags; + if ((readingFlags == MTPDstickerSet::Flag::f_installed_date) + && !set->installDate) { + set->installDate = kDefaultStickerInstallDate; + } + } + } + } +} + +void Account::writeInstalledStickers() { + writeStickerSets(_installedStickersKey, [](const Data::StickersSet &set) { + if (set.id == Data::Stickers::CloudRecentSetId || set.id == Data::Stickers::FavedSetId) { // separate files for them + return StickerSetCheckResult::Skip; + } else if (set.flags & MTPDstickerSet_ClientFlag::f_special) { + if (set.stickers.isEmpty()) { // all other special are "installed" + return StickerSetCheckResult::Skip; + } + } else if (!(set.flags & MTPDstickerSet::Flag::f_installed_date) || (set.flags & MTPDstickerSet::Flag::f_archived)) { + return StickerSetCheckResult::Skip; + } else if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { // waiting to receive + return StickerSetCheckResult::Abort; + } else if (set.stickers.isEmpty()) { + return StickerSetCheckResult::Skip; + } + return StickerSetCheckResult::Write; + }, Auth().data().stickers().setsOrder()); +} + +void Account::writeFeaturedStickers() { + writeStickerSets(_featuredStickersKey, [](const Data::StickersSet &set) { + if (set.id == Data::Stickers::CloudRecentSetId + || set.id == Data::Stickers::FavedSetId) { // separate files for them + return StickerSetCheckResult::Skip; + } else if (set.flags & MTPDstickerSet_ClientFlag::f_special) { + return StickerSetCheckResult::Skip; + } else if (!(set.flags & MTPDstickerSet_ClientFlag::f_featured)) { + return StickerSetCheckResult::Skip; + } else if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { // waiting to receive + return StickerSetCheckResult::Abort; + } else if (set.stickers.isEmpty()) { + return StickerSetCheckResult::Skip; + } + return StickerSetCheckResult::Write; + }, Auth().data().stickers().featuredSetsOrder()); +} + +void Account::writeRecentStickers() { + writeStickerSets(_recentStickersKey, [](const Data::StickersSet &set) { + if (set.id != Data::Stickers::CloudRecentSetId || set.stickers.isEmpty()) { + return StickerSetCheckResult::Skip; + } + return StickerSetCheckResult::Write; + }, Data::StickersSetsOrder()); +} + +void Account::writeFavedStickers() { + writeStickerSets(_favedStickersKey, [](const Data::StickersSet &set) { + if (set.id != Data::Stickers::FavedSetId || set.stickers.isEmpty()) { + return StickerSetCheckResult::Skip; + } + return StickerSetCheckResult::Write; + }, Data::StickersSetsOrder()); +} + +void Account::writeArchivedStickers() { + writeStickerSets(_archivedStickersKey, [](const Data::StickersSet &set) { + if (!(set.flags & MTPDstickerSet::Flag::f_archived) || set.stickers.isEmpty()) { + return StickerSetCheckResult::Skip; + } + return StickerSetCheckResult::Write; + }, Auth().data().stickers().archivedSetsOrder()); +} + +void Account::importOldRecentStickers() { + if (!_recentStickersKeyOld) { + return; + } + + FileReadDescriptor stickers; + if (!ReadEncryptedFile(stickers, _recentStickersKeyOld, _basePath, _localKey)) { + ClearKey(_recentStickersKeyOld, _basePath); + _recentStickersKeyOld = 0; + writeMapDelayed(); + return; + } + + auto &sets = Auth().data().stickers().setsRef(); + sets.clear(); + + auto &order = Auth().data().stickers().setsOrderRef(); + order.clear(); + + auto &recent = cRefRecentStickers(); + recent.clear(); + + const auto def = sets.emplace( + Data::Stickers::DefaultSetId, + std::make_unique( + &Auth().data(), + Data::Stickers::DefaultSetId, + uint64(0), + tr::lng_stickers_default_set(tr::now), + QString(), + 0, // count + 0, // hash + (MTPDstickerSet::Flag::f_official + | MTPDstickerSet::Flag::f_installed_date + | MTPDstickerSet_ClientFlag::f_special), + kDefaultStickerInstallDate)).first->second.get(); + const auto custom = sets.emplace( + Data::Stickers::CustomSetId, + std::make_unique( + &Auth().data(), + Data::Stickers::CustomSetId, + uint64(0), + qsl("Custom stickers"), + QString(), + 0, // count + 0, // hash + (MTPDstickerSet::Flag::f_installed_date + | MTPDstickerSet_ClientFlag::f_special), + kDefaultStickerInstallDate)).first->second.get(); + + QMap read; + while (!stickers.stream.atEnd()) { + quint64 id, access; + QString name, mime, alt; + qint32 date, dc, size, width, height, type; + qint16 value; + stickers.stream >> id >> value >> access >> date >> name >> mime >> dc >> size >> width >> height >> type; + if (stickers.version >= 7021) { + stickers.stream >> alt; + } + if (!value || read.contains(id)) continue; + read.insert(id, true); + + QVector attributes; + if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name))); + if (type == AnimatedDocument) { + attributes.push_back(MTP_documentAttributeAnimated()); + } else if (type == StickerDocument) { + attributes.push_back(MTP_documentAttributeSticker(MTP_flags(0), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords())); + } + if (width > 0 && height > 0) { + attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); + } + + const auto doc = Auth().data().document( + id, + access, + QByteArray(), + date, + attributes, + mime, + QByteArray(), + ImageWithLocation(), + ImageWithLocation(), + dc, + size); + if (!doc->sticker()) { + continue; + } + + if (value > 0) { + def->stickers.push_back(doc); + ++def->count; + } else { + custom->stickers.push_back(doc); + ++custom->count; + } + if (recent.size() < Global::StickersRecentLimit() && qAbs(value) > 1) { + recent.push_back(qMakePair(doc, qAbs(value))); + } + } + if (def->stickers.isEmpty()) { + sets.remove(Data::Stickers::DefaultSetId); + } else { + order.push_front(Data::Stickers::DefaultSetId); + } + if (custom->stickers.isEmpty()) { + sets.remove(Data::Stickers::CustomSetId); + } + + writeInstalledStickers(); + writeSettings(); + + ClearKey(_recentStickersKeyOld, _basePath); + _recentStickersKeyOld = 0; + writeMapDelayed(); +} + +void Account::readInstalledStickers() { + if (!_installedStickersKey) { + return importOldRecentStickers(); + } + + Auth().data().stickers().setsRef().clear(); + readStickerSets( + _installedStickersKey, + &Auth().data().stickers().setsOrderRef(), + MTPDstickerSet::Flag::f_installed_date); +} + +void Account::readFeaturedStickers() { + readStickerSets( + _featuredStickersKey, + &Auth().data().stickers().featuredSetsOrderRef(), + MTPDstickerSet::Flags() | MTPDstickerSet_ClientFlag::f_featured); + + const auto &sets = Auth().data().stickers().sets(); + const auto &order = Auth().data().stickers().featuredSetsOrder(); + int unreadCount = 0; + for (const auto setId : order) { + auto it = sets.find(setId); + if (it != sets.cend() + && (it->second->flags & MTPDstickerSet_ClientFlag::f_unread)) { + ++unreadCount; + } + } + Auth().data().stickers().setFeaturedSetsUnreadCount(unreadCount); +} + +void Account::readRecentStickers() { + readStickerSets(_recentStickersKey); +} + +void Account::readFavedStickers() { + readStickerSets(_favedStickersKey); +} + +void Account::readArchivedStickers() { + static bool archivedStickersRead = false; + if (!archivedStickersRead) { + readStickerSets(_archivedStickersKey, &Auth().data().stickers().archivedSetsOrderRef()); + archivedStickersRead = true; + } +} + +void Account::writeSavedGifs() { + auto &saved = Auth().data().stickers().savedGifs(); + if (saved.isEmpty()) { + if (_savedGifsKey) { + ClearKey(_savedGifsKey, _basePath); + _savedGifsKey = 0; + writeMapDelayed(); + } + } else { + quint32 size = sizeof(quint32); // count + for_const (auto gif, saved) { + size += Serialize::Document::sizeInStream(gif); + } + + if (!_savedGifsKey) { + _savedGifsKey = GenerateKey(_basePath); + writeMapQueued(); + } + EncryptedDescriptor data(size); + data.stream << quint32(saved.size()); + for_const (auto gif, saved) { + Serialize::Document::writeToStream(data.stream, gif); + } + FileWriteDescriptor file(_savedGifsKey, _basePath); + file.writeEncrypted(data, _localKey); + } +} + +void Account::readSavedGifs() { + if (!_savedGifsKey) return; + + FileReadDescriptor gifs; + if (!ReadEncryptedFile(gifs, _savedGifsKey, _basePath, _localKey)) { + ClearKey(_savedGifsKey, _basePath); + _savedGifsKey = 0; + writeMapDelayed(); + return; + } + + auto &saved = Auth().data().stickers().savedGifsRef(); + const auto failed = [&] { + ClearKey(_savedGifsKey, _basePath); + _savedGifsKey = 0; + saved.clear(); + }; + saved.clear(); + + quint32 cnt; + gifs.stream >> cnt; + saved.reserve(cnt); + OrderedSet read; + for (uint32 i = 0; i < cnt; ++i) { + auto document = Serialize::Document::readFromStream(gifs.version, gifs.stream); + if (!CheckStreamStatus(gifs.stream)) { + return failed(); + } else if (!document || !document->isGifv()) { + continue; + } + + if (read.contains(document->id)) continue; + read.insert(document->id); + + saved.push_back(document); + } +} + +void Account::writeBackground( + const Data::WallPaper &paper, + const QImage &image) { + if (!_backgroundCanWrite) { + return; + } + + if (!_localKey) { + LOG(("App Error: localkey not created in writeBackground()")); + return; + } + + auto &backgroundKey = Window::Theme::IsNightMode() + ? _backgroundKeyNight + : _backgroundKeyDay; + auto imageData = QByteArray(); + if (!image.isNull()) { + const auto width = qint32(image.width()); + const auto height = qint32(image.height()); + const auto perpixel = (image.depth() >> 3); + const auto srcperline = image.bytesPerLine(); + const auto srcsize = srcperline * height; + const auto dstperline = width * perpixel; + const auto dstsize = dstperline * height; + const auto copy = (image.format() != kSavedBackgroundFormat) + ? image.convertToFormat(kSavedBackgroundFormat) + : image; + imageData.resize(2 * sizeof(qint32) + dstsize); + + auto dst = bytes::make_detached_span(imageData); + bytes::copy(dst, bytes::object_as_span(&width)); + dst = dst.subspan(sizeof(qint32)); + bytes::copy(dst, bytes::object_as_span(&height)); + dst = dst.subspan(sizeof(qint32)); + const auto src = bytes::make_span(image.constBits(), srcsize); + if (srcsize == dstsize) { + bytes::copy(dst, src); + } else { + for (auto y = 0; y != height; ++y) { + bytes::copy(dst, src.subspan(y * srcperline, dstperline)); + dst = dst.subspan(dstperline); + } + } + } + if (!backgroundKey) { + backgroundKey = GenerateKey(_basePath); + writeMapQueued(); + } + const auto serialized = paper.serialize(); + quint32 size = sizeof(qint32) + + Serialize::bytearraySize(serialized) + + Serialize::bytearraySize(imageData); + EncryptedDescriptor data(size); + data.stream + << qint32(kWallPaperSerializeTagId) + << serialized + << imageData; + + FileWriteDescriptor file(backgroundKey, _basePath); + file.writeEncrypted(data, _localKey); +} + +bool Account::readBackground() { + FileReadDescriptor bg; + auto &backgroundKey = Window::Theme::IsNightMode() + ? _backgroundKeyNight + : _backgroundKeyDay; + if (!ReadEncryptedFile(bg, backgroundKey, _basePath, _localKey)) { + if (backgroundKey) { + ClearKey(backgroundKey, _basePath); + backgroundKey = 0; + writeMapDelayed(); + } + return false; + } + + qint32 legacyId = 0; + bg.stream >> legacyId; + const auto paper = [&] { + if (legacyId == kWallPaperLegacySerializeTagId) { + quint64 id = 0; + quint64 accessHash = 0; + quint32 flags = 0; + QString slug; + bg.stream + >> id + >> accessHash + >> flags + >> slug; + return Data::WallPaper::FromLegacySerialized( + id, + accessHash, + flags, + slug); + } else if (legacyId == kWallPaperSerializeTagId) { + QByteArray serialized; + bg.stream >> serialized; + return Data::WallPaper::FromSerialized(serialized); + } else { + return Data::WallPaper::FromLegacyId(legacyId); + } + }(); + if (bg.stream.status() != QDataStream::Ok || !paper) { + return false; + } + + QByteArray imageData; + bg.stream >> imageData; + const auto isOldEmptyImage = (bg.stream.status() != QDataStream::Ok); + if (isOldEmptyImage + || Data::IsLegacy1DefaultWallPaper(*paper) + || Data::IsDefaultWallPaper(*paper)) { + _backgroundCanWrite = false; + if (isOldEmptyImage || bg.version < 8005) { + Window::Theme::Background()->set(Data::DefaultWallPaper()); + Window::Theme::Background()->setTile(false); + } else { + Window::Theme::Background()->set(*paper); + } + _backgroundCanWrite = true; + return true; + } else if (Data::IsThemeWallPaper(*paper) && imageData.isEmpty()) { + _backgroundCanWrite = false; + Window::Theme::Background()->set(*paper); + _backgroundCanWrite = true; + return true; + } + auto image = QImage(); + if (legacyId == kWallPaperSerializeTagId) { + const auto perpixel = 4; + auto src = bytes::make_span(imageData); + auto width = qint32(); + auto height = qint32(); + if (src.size() > 2 * sizeof(qint32)) { + bytes::copy( + bytes::object_as_span(&width), + src.subspan(0, sizeof(qint32))); + src = src.subspan(sizeof(qint32)); + bytes::copy( + bytes::object_as_span(&height), + src.subspan(0, sizeof(qint32))); + src = src.subspan(sizeof(qint32)); + if (width + height <= kWallPaperSidesLimit + && src.size() == width * height * perpixel) { + image = QImage( + width, + height, + QImage::Format_ARGB32_Premultiplied); + if (!image.isNull()) { + const auto srcperline = width * perpixel; + const auto srcsize = srcperline * height; + const auto dstperline = image.bytesPerLine(); + const auto dstsize = dstperline * height; + Assert(srcsize == dstsize); + bytes::copy( + bytes::make_span(image.bits(), dstsize), + src); + } + } + } + } else { + auto buffer = QBuffer(&imageData); + auto reader = QImageReader(&buffer); +#ifndef OS_MAC_OLD + reader.setAutoTransform(true); +#endif // OS_MAC_OLD + if (!reader.read(&image)) { + image = QImage(); + } + } + if (!image.isNull() || paper->backgroundColor()) { + _backgroundCanWrite = false; + Window::Theme::Background()->set(*paper, std::move(image)); + _backgroundCanWrite = true; + return true; + } + return false; +} + +void Account::writeRecentHashtagsAndBots() { + const auto &write = cRecentWriteHashtags(); + const auto &search = cRecentSearchHashtags(); + const auto &bots = cRecentInlineBots(); + + if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) { + readRecentHashtagsAndBots(); + } + if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) { + if (_recentHashtagsAndBotsKey) { + ClearKey(_recentHashtagsAndBotsKey, _basePath); + _recentHashtagsAndBotsKey = 0; + writeMapDelayed(); + } + return; + } + if (!_recentHashtagsAndBotsKey) { + _recentHashtagsAndBotsKey = GenerateKey(_basePath); + writeMapQueued(); + } + quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = cRecentInlineBots().size(); + for (auto i = write.cbegin(), e = write.cend(); i != e; ++i) { + if (!i->first.isEmpty()) { + size += Serialize::stringSize(i->first) + sizeof(quint16); + ++writeCnt; + } + } + for (auto i = search.cbegin(), e = search.cend(); i != e; ++i) { + if (!i->first.isEmpty()) { + size += Serialize::stringSize(i->first) + sizeof(quint16); + ++searchCnt; + } + } + for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) { + size += Serialize::peerSize(*i); + } + + EncryptedDescriptor data(size); + data.stream << quint32(writeCnt) << quint32(searchCnt); + for (auto i = write.cbegin(), e = write.cend(); i != e; ++i) { + if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second); + } + for (auto i = search.cbegin(), e = search.cend(); i != e; ++i) { + if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second); + } + data.stream << quint32(botsCnt); + for (auto i = bots.cbegin(), e = bots.cend(); i != e; ++i) { + Serialize::writePeer(data.stream, *i); + } + FileWriteDescriptor file(_recentHashtagsAndBotsKey, _basePath); + file.writeEncrypted(data, _localKey); +} + +void Account::readRecentHashtagsAndBots() { + if (_recentHashtagsAndBotsWereRead) return; + _recentHashtagsAndBotsWereRead = true; + + if (!_recentHashtagsAndBotsKey) return; + + FileReadDescriptor hashtags; + if (!ReadEncryptedFile(hashtags, _recentHashtagsAndBotsKey, _basePath, _localKey)) { + ClearKey(_recentHashtagsAndBotsKey, _basePath); + _recentHashtagsAndBotsKey = 0; + writeMapDelayed(); + return; + } + + quint32 writeCount = 0, searchCount = 0, botsCount = 0; + hashtags.stream >> writeCount >> searchCount; + + QString tag; + quint16 count; + + RecentHashtagPack write, search; + RecentInlineBots bots; + if (writeCount) { + write.reserve(writeCount); + for (uint32 i = 0; i < writeCount; ++i) { + hashtags.stream >> tag >> count; + write.push_back(qMakePair(tag.trimmed(), count)); + } + } + if (searchCount) { + search.reserve(searchCount); + for (uint32 i = 0; i < searchCount; ++i) { + hashtags.stream >> tag >> count; + search.push_back(qMakePair(tag.trimmed(), count)); + } + } + cSetRecentWriteHashtags(write); + cSetRecentSearchHashtags(search); + + if (!hashtags.stream.atEnd()) { + hashtags.stream >> botsCount; + if (botsCount) { + bots.reserve(botsCount); + for (auto i = 0; i < botsCount; ++i) { + const auto peer = Serialize::readPeer( + hashtags.version, + hashtags.stream); + if (!peer) { + return; // Broken data. + } else if (peer->isUser() + && peer->asUser()->isBot() + && !peer->asUser()->botInfo->inlinePlaceholder.isEmpty() + && !peer->asUser()->username.isEmpty()) { + bots.push_back(peer->asUser()); + } + } + } + cSetRecentInlineBots(bots); + } +} + +std::optional Account::saveRecentHashtags( + Fn getPack, + const QString &text) { + auto found = false; + auto m = QRegularExpressionMatch(); + auto recent = getPack(); + for (auto i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) { + i = m.capturedStart(); + next = m.capturedEnd(); + if (m.hasMatch()) { + if (!m.capturedRef(1).isEmpty()) { + ++i; + } + if (!m.capturedRef(2).isEmpty()) { + --next; + } + } + const auto tag = text.mid(i + 1, next - i - 1); + if (TextUtilities::RegExpHashtagExclude().match(tag).hasMatch()) { + continue; + } + if (!found + && cRecentWriteHashtags().isEmpty() + && cRecentSearchHashtags().isEmpty()) { + readRecentHashtagsAndBots(); + recent = getPack(); + } + found = true; + Local::incrementRecentHashtag(recent, tag); + } + return found ? base::make_optional(recent) : std::nullopt; +} + +void Account::saveRecentSentHashtags(const QString &text) { + const auto result = saveRecentHashtags( + [] { return cRecentWriteHashtags(); }, + text); + if (result) { + cSetRecentWriteHashtags(*result); + writeRecentHashtagsAndBots(); + } +} + +void Account::saveRecentSearchHashtags(const QString &text) { + const auto result = saveRecentHashtags( + [] { return cRecentSearchHashtags(); }, + text); + if (result) { + cSetRecentSearchHashtags(*result); + writeRecentHashtagsAndBots(); + } +} + +void Account::writeExportSettings(const Export::Settings &settings) { + const auto check = Export::Settings(); + if (settings.types == check.types + && settings.fullChats == check.fullChats + && settings.media.types == check.media.types + && settings.media.sizeLimit == check.media.sizeLimit + && settings.path == check.path + && settings.format == check.format + && settings.availableAt == check.availableAt + && !settings.onlySinglePeer()) { + if (_exportSettingsKey) { + ClearKey(_exportSettingsKey, _basePath); + _exportSettingsKey = 0; + writeMapDelayed(); + } + return; + } + if (!_exportSettingsKey) { + _exportSettingsKey = GenerateKey(_basePath); + writeMapQueued(); + } + quint32 size = sizeof(quint32) * 6 + + Serialize::stringSize(settings.path) + + sizeof(qint32) * 2 + sizeof(quint64); + EncryptedDescriptor data(size); + data.stream + << quint32(settings.types) + << quint32(settings.fullChats) + << quint32(settings.media.types) + << quint32(settings.media.sizeLimit) + << quint32(settings.format) + << settings.path + << quint32(settings.availableAt); + settings.singlePeer.match([&](const MTPDinputPeerUser & user) { + data.stream + << kSinglePeerTypeUser + << qint32(user.vuser_id().v) + << quint64(user.vaccess_hash().v); + }, [&](const MTPDinputPeerChat & chat) { + data.stream << kSinglePeerTypeChat << qint32(chat.vchat_id().v); + }, [&](const MTPDinputPeerChannel & channel) { + data.stream + << kSinglePeerTypeChannel + << qint32(channel.vchannel_id().v) + << quint64(channel.vaccess_hash().v); + }, [&](const MTPDinputPeerSelf &) { + data.stream << kSinglePeerTypeSelf; + }, [&](const MTPDinputPeerEmpty &) { + data.stream << kSinglePeerTypeEmpty; + }, [&](const MTPDinputPeerUserFromMessage &) { + Unexpected("From message peer in single peer export settings."); + }, [&](const MTPDinputPeerChannelFromMessage &) { + Unexpected("From message peer in single peer export settings."); + }); + data.stream << qint32(settings.singlePeerFrom); + data.stream << qint32(settings.singlePeerTill); + + FileWriteDescriptor file(_exportSettingsKey, _basePath); + file.writeEncrypted(data, _localKey); +} + +Export::Settings Account::readExportSettings() { + FileReadDescriptor file; + if (!ReadEncryptedFile(file, _exportSettingsKey, _basePath, _localKey)) { + ClearKey(_exportSettingsKey, _basePath); + _exportSettingsKey = 0; + writeMapDelayed(); + return Export::Settings(); + } + + quint32 types = 0, fullChats = 0; + quint32 mediaTypes = 0, mediaSizeLimit = 0; + quint32 format = 0, availableAt = 0; + QString path; + qint32 singlePeerType = 0, singlePeerBareId = 0; + quint64 singlePeerAccessHash = 0; + qint32 singlePeerFrom = 0, singlePeerTill = 0; + file.stream + >> types + >> fullChats + >> mediaTypes + >> mediaSizeLimit + >> format + >> path + >> availableAt; + if (!file.stream.atEnd()) { + file.stream >> singlePeerType; + switch (singlePeerType) { + case kSinglePeerTypeUser: + case kSinglePeerTypeChannel: { + file.stream >> singlePeerBareId >> singlePeerAccessHash; + } break; + case kSinglePeerTypeChat: file.stream >> singlePeerBareId; break; + case kSinglePeerTypeSelf: + case kSinglePeerTypeEmpty: break; + default: return Export::Settings(); + } + } + if (!file.stream.atEnd()) { + file.stream >> singlePeerFrom >> singlePeerTill; + } + auto result = Export::Settings(); + result.types = Export::Settings::Types::from_raw(types); + result.fullChats = Export::Settings::Types::from_raw(fullChats); + result.media.types = Export::MediaSettings::Types::from_raw(mediaTypes); + result.media.sizeLimit = mediaSizeLimit; + result.format = Export::Output::Format(format); + result.path = path; + result.availableAt = availableAt; + result.singlePeer = [&] { + switch (singlePeerType) { + case kSinglePeerTypeUser: + return MTP_inputPeerUser( + MTP_int(singlePeerBareId), + MTP_long(singlePeerAccessHash)); + case kSinglePeerTypeChat: + return MTP_inputPeerChat(MTP_int(singlePeerBareId)); + case kSinglePeerTypeChannel: + return MTP_inputPeerChannel( + MTP_int(singlePeerBareId), + MTP_long(singlePeerAccessHash)); + case kSinglePeerTypeSelf: + return MTP_inputPeerSelf(); + case kSinglePeerTypeEmpty: + return MTP_inputPeerEmpty(); + } + Unexpected("Type in export data single peer."); + }(); + result.singlePeerFrom = singlePeerFrom; + result.singlePeerTill = singlePeerTill; + return (file.stream.status() == QDataStream::Ok && result.validate()) + ? result + : Export::Settings(); +} + +void Account::writeSelf() { + writeMapDelayed(); +} + +void Account::readSelf(const QByteArray &serialized, int32 streamVersion) { + QDataStream stream(serialized); + const auto user = Auth().user(); + const auto wasLoadedStatus = std::exchange( + user->loadedStatus, + PeerData::NotLoaded); + const auto self = Serialize::readPeer(streamVersion, stream); + if (!self || !self->isSelf() || self != user) { + user->loadedStatus = wasLoadedStatus; + return; + } + + QString about; + stream >> about; + if (CheckStreamStatus(stream)) { + self->asUser()->setAbout(about); + } +} + +void Account::writeTrustedBots() { + if (_trustedBots.empty()) { + if (_trustedBotsKey) { + ClearKey(_trustedBotsKey, _basePath); + _trustedBotsKey = 0; + writeMapDelayed(); + } + return; + } + if (!_trustedBotsKey) { + _trustedBotsKey = GenerateKey(_basePath); + writeMapQueued(); + } + quint32 size = sizeof(qint32) + _trustedBots.size() * sizeof(quint64); + EncryptedDescriptor data(size); + data.stream << qint32(_trustedBots.size()); + for_const (auto botId, _trustedBots) { + data.stream << quint64(botId); + } + + FileWriteDescriptor file(_trustedBotsKey, _basePath); + file.writeEncrypted(data, _localKey); +} + +void Account::readTrustedBots() { + if (!_trustedBotsKey) return; + + FileReadDescriptor trusted; + if (!ReadEncryptedFile(trusted, _trustedBotsKey, _basePath, _localKey)) { + ClearKey(_trustedBotsKey, _basePath); + _trustedBotsKey = 0; + writeMapDelayed(); + return; + } + + qint32 size = 0; + trusted.stream >> size; + for (int i = 0; i < size; ++i) { + quint64 botId = 0; + trusted.stream >> botId; + _trustedBots.insert(botId); + } +} + +void Account::markBotTrusted(not_null bot) { + if (!isBotTrusted(bot)) { + _trustedBots.insert(bot->id); + writeTrustedBots(); + } +} + +bool Account::isBotTrusted(not_null bot) { + if (!_trustedBotsRead) { + readTrustedBots(); + _trustedBotsRead = true; + } + return _trustedBots.contains(bot->id); +} + +bool Account::encrypt( + const void *src, + void *dst, + uint32 len, + const void *key128) const { + if (!_localKey) { + return false; + } + MTP::aesEncryptLocal(src, dst, len, _localKey, key128); + return true; +} + +bool Account::decrypt( + const void *src, + void *dst, + uint32 len, + const void *key128) const { + if (!_localKey) { + return false; + } + MTP::aesDecryptLocal(src, dst, len, _localKey, key128); + return true; +} + +} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_account.h b/Telegram/SourceFiles/storage/storage_account.h new file mode 100644 index 0000000000..b07892021c --- /dev/null +++ b/Telegram/SourceFiles/storage/storage_account.h @@ -0,0 +1,265 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/timer.h" +#include "storage/cache/storage_cache_database.h" +#include "data/stickers/data_stickers_set.h" + +class History; +class FileLocation; + +namespace Export { +struct Settings; +} // namespace Export + +namespace Main { +class Account; +class Settings; +} // namespace Main + +namespace Data { +class WallPaper; +} // namespace Data + +namespace MTP { +class AuthKey; +using AuthKeyPtr = std::shared_ptr; +} // namespace MTP + +namespace Storage { +namespace details { +struct ReadSettingsContext; +} // namespace details + +class EncryptionKey; + +using FileKey = quint64; + +enum class StartResult : uchar { + Success, + IncorrectPasscode, +}; + +struct MessageDraft { + MsgId msgId = 0; + TextWithTags textWithTags; + bool previewCancelled = false; +}; + +class Account final { +public: + Account(not_null owner, const QString &dataName); + ~Account(); + + [[nodiscard]] StartResult start(const QByteArray &passcode); + [[nodiscard]] int oldMapVersion() const { + return _oldMapVersion; + } + + [[nodiscard]] bool checkPasscode(const QByteArray &passcode) const; + void setPasscode(const QByteArray &passcode); + + void writeSettings(); + void writeMtpData(); + + void writeBackground(const Data::WallPaper &paper, const QImage &image); + bool readBackground(); + + void writeDrafts( + const PeerId &peer, + const MessageDraft &localDraft, + const MessageDraft &editDraft); + void readDraftsWithCursors(not_null history); + void writeDraftCursors( + const PeerId &peer, + const MessageCursor &localCursor, + const MessageCursor &editCursor); + [[nodiscard]] bool hasDraftCursors(const PeerId &peer); + [[nodiscard]] bool hasDraft(const PeerId &peer); + + void writeFileLocation(MediaKey location, const FileLocation &local); + [[nodiscard]] FileLocation readFileLocation(MediaKey location); + void removeFileLocation(MediaKey location); + + [[nodiscard]] EncryptionKey cacheKey() const; + [[nodiscard]] QString cachePath() const; + [[nodiscard]] Cache::Database::Settings cacheSettings() const; + void updateCacheSettings( + Cache::Database::SettingsUpdate &update, + Cache::Database::SettingsUpdate &updateBig); + + [[nodiscard]] EncryptionKey cacheBigFileKey() const; + [[nodiscard]] QString cacheBigFilePath() const; + [[nodiscard]] Cache::Database::Settings cacheBigFileSettings() const; + + void writeInstalledStickers(); + void writeFeaturedStickers(); + void writeRecentStickers(); + void writeFavedStickers(); + void writeArchivedStickers(); + void readInstalledStickers(); + void readFeaturedStickers(); + void readRecentStickers(); + void readFavedStickers(); + void readArchivedStickers(); + void writeSavedGifs(); + void readSavedGifs(); + + void writeRecentHashtagsAndBots(); + void readRecentHashtagsAndBots(); + void saveRecentSentHashtags(const QString &text); + void saveRecentSearchHashtags(const QString &text); + + void writeExportSettings(const Export::Settings &settings); + [[nodiscard]] Export::Settings readExportSettings(); + + void writeSelf(); + void readSelf(const QByteArray &serialized, int32 streamVersion); + + void markBotTrusted(not_null bot); + [[nodiscard]] bool isBotTrusted(not_null bot); + + [[nodiscard]] bool encrypt( + const void *src, + void *dst, + uint32 len, + const void *key128) const; + [[nodiscard]] bool decrypt( + const void *src, + void *dst, + uint32 len, + const void *key128) const; + + void reset(); + +private: + enum class ReadMapResult { + Success, + IncorrectPasscode, + Failed, + }; + + [[nodiscard]] base::flat_set collectGoodNames() const; + [[nodiscard]] auto prepareReadSettingsContext() const + -> details::ReadSettingsContext; + + [[nodiscard]] ReadMapResult readMap(const QByteArray &passcode); + void writeMapDelayed(); + void writeMapQueued(); + void writeMap(); + + void readLocations(); + void writeLocations(); + void writeLocationsQueued(); + void writeLocationsDelayed(); + + std::unique_ptr readSettings(); + void writeSettings(Main::Settings *stored); + bool readOldUserSettings( + bool remove, + details::ReadSettingsContext &context); + void readOldUserSettingsFields( + QIODevice *device, + qint32 &version, + details::ReadSettingsContext &context); + void readOldMtpDataFields( + QIODevice *device, + qint32 &version, + details::ReadSettingsContext &context); + bool readOldMtpData(bool remove, details::ReadSettingsContext &context); + + void readMtpData(); + std::unique_ptr applyReadContext( + details::ReadSettingsContext &&context); + + [[nodiscard]] QByteArray serializeCallSettings(); + void deserializeCallSettings(QByteArray &settings); + + void readDraftCursors( + const PeerId &peer, + MessageCursor &localCursor, + MessageCursor &editCursor); + void clearDraftCursors(const PeerId &peer); + + void writeStickerSet( + QDataStream &stream, + const Data::StickersSet &set); + template + void writeStickerSets( + FileKey &stickersKey, + CheckSet checkSet, + const Data::StickersSetsOrder &order); + void readStickerSets( + FileKey &stickersKey, + Data::StickersSetsOrder *outOrder = nullptr, + MTPDstickerSet::Flags readingFlags = 0); + void importOldRecentStickers(); + + void readTrustedBots(); + void writeTrustedBots(); + + std::optional saveRecentHashtags( + Fn getPack, + const QString &text); + + const not_null _owner; + const QString _dataName; + const FileKey _dataNameKey = 0; + const QString _basePath; + const QString _databasePath; + + MTP::AuthKeyPtr _localKey; + MTP::AuthKeyPtr _passcodeKey; + QByteArray _passcodeKeySalt; + QByteArray _passcodeKeyEncrypted; + + base::flat_map _draftsMap; + base::flat_map _draftCursorsMap; + base::flat_map _draftsNotReadMap; + + QMultiMap _fileLocations; + QMap> _fileLocationPairs; + QMap _fileLocationAliases; + + FileKey _locationsKey = 0; + FileKey _trustedBotsKey = 0; + FileKey _installedStickersKey = 0; + FileKey _featuredStickersKey = 0; + FileKey _recentStickersKey = 0; + FileKey _favedStickersKey = 0; + FileKey _archivedStickersKey = 0; + FileKey _savedGifsKey = 0; + FileKey _recentStickersKeyOld = 0; + FileKey _backgroundKeyDay = 0; + FileKey _backgroundKeyNight = 0; + FileKey _settingsKey = 0; + FileKey _recentHashtagsAndBotsKey = 0; + FileKey _exportSettingsKey = 0; + + qint64 _cacheTotalSizeLimit = 0; + qint64 _cacheBigFileTotalSizeLimit = 0; + qint32 _cacheTotalTimeLimit = 0; + qint32 _cacheBigFileTotalTimeLimit = 0; + + base::flat_set _trustedBots; + bool _trustedBotsRead = false; + bool _backgroundCanWrite = true; + bool _readingUserSettings = false; + bool _recentHashtagsAndBotsWereRead = false; + + int _oldMapVersion = 0; + + base::Timer _writeMapTimer; + base::Timer _writeLocationsTimer; + bool _mapChanged = false; + bool _locationsChanged = false; + +}; + +} // namespace Storage diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index dffd6ee0af..e8ee538906 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "apiwrap.h" #include "storage/localstorage.h" +#include "storage/storage_account.h" #include "storage/localimageloader.h" #include "storage/file_upload.h" #include "base/parse_helper.h" @@ -529,6 +530,10 @@ ChatBackground::ChatBackground() : _adjustableColors({ }); } +Storage::Account &ChatBackground::local() const { + return Core::App().activeAccount().local(); +} + void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) { _themeImage = validateBackgroundImage(std::move(themeImage)); _themeTile = themeTile; @@ -538,7 +543,7 @@ void ChatBackground::start() { if (!Data::details::IsUninitializedWallPaper(_paper)) { return; } - if (!Local::readBackground()) { + if (!local().readBackground()) { set(Data::ThemeWallPaper()); } @@ -653,7 +658,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) { setPaper(Data::DefaultWallPaper().withParamsFrom(_paper)); image.load(qsl(":/gui/art/bg.jpg")); } - Local::writeBackground( + local().writeBackground( _paper, ((Data::IsDefaultWallPaper(_paper) || Data::IsLegacy1DefaultWallPaper(_paper)) @@ -869,7 +874,7 @@ void ChatBackground::setTile(bool tile) { if (this->tile() != old) { if (!Data::details::IsTestingThemeWallPaper(_paper) && !Data::details::IsTestingDefaultWallPaper(_paper)) { - Local::writeUserSettings(); + local().writeSettings(); } notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, tile)); } @@ -1024,9 +1029,9 @@ bool ChatBackground::isNonDefaultBackground() { void ChatBackground::writeNewBackgroundSettings() { if (tile() != _tileForRevert) { - Local::writeUserSettings(); + local().writeSettings(); } - Local::writeBackground( + local().writeBackground( _paper, ((Data::IsThemeWallPaper(_paper) || Data::IsDefaultWallPaper(_paper)) @@ -1111,12 +1116,12 @@ void ChatBackground::reapplyWithNightMode( ClearApplying(); keepApplied(saved.object, settingExactTheme); if (tile() != _tileForRevert) { - Local::writeUserSettings(); + local().writeSettings(); } if (nightModeChanged) { Local::writeSettings(); } - if (!settingExactTheme && !Local::readBackground()) { + if (!settingExactTheme && !local().readBackground()) { set(Data::ThemeWallPaper()); } }; diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index be9833fb9a..109111573c 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -14,6 +14,10 @@ namespace Main { class Session; } // namespace Main +namespace Storage { +class Account; +} // namespace Storage + namespace Window { namespace Theme { @@ -177,6 +181,8 @@ private: QColor original; }; + Storage::Account &local() const; + void ensureStarted(); void saveForRevert(); void setPreparedImage(QImage original, QImage prepared); diff --git a/Telegram/SourceFiles/window/window_lock_widgets.cpp b/Telegram/SourceFiles/window/window_lock_widgets.cpp index e7469c5b01..9302b42dea 100644 --- a/Telegram/SourceFiles/window/window_lock_widgets.cpp +++ b/Telegram/SourceFiles/window/window_lock_widgets.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_lock_widgets.h" #include "lang/lang_keys.h" -#include "storage/localstorage.h" +#include "storage/storage_account.h" #include "mainwindow.h" #include "core/application.h" #include "api/api_text_entities.h" @@ -147,9 +147,11 @@ void PasscodeLockWidget::submit() { } const auto passcode = _passcode->text().toUtf8(); - const auto correct = window()->account().sessionExists() - ? Local::checkPasscode(passcode) - : (Local::readMap(passcode) != Local::ReadMapPassNeeded); + auto &account = window()->account(); + auto &local = window()->account().local(); + const auto correct = account.sessionExists() + ? local.checkPasscode(passcode) + : (local.start(passcode) != Storage::StartResult::IncorrectPasscode); if (!correct) { cSetPasscodeBadTries(cPasscodeBadTries() + 1); cSetPasscodeLastTry(crl::now()); diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 1e5b6bb94c..ee7b6388b5 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/empty_userpic.h" #include "mainwindow.h" #include "storage/localstorage.h" +#include "storage/storage_account.h" #include "support/support_templates.h" #include "settings/settings_common.h" #include "base/qt_signal_producer.h" @@ -281,7 +282,7 @@ void MainMenu::refreshMenu() { (*fix)->setChecked(!(*fix)->isChecked()); _controller->session().settings().setSupportFixChatsOrder( (*fix)->isChecked()); - Local::writeUserSettings(); + _controller->session().local().writeSettings(); }, &st::mainMenuFixOrder, &st::mainMenuFixOrderOver); (*fix)->setCheckable(true); (*fix)->setChecked(