| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308 |
- /*
- 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/localstorage.h"
- #include "storage/serialize_common.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 "base/platform/base_platform_info.h"
- #include "base/random.h"
- #include "ui/power_saving.h"
- #include "core/update_checker.h"
- #include "core/file_location.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "media/audio/media_audio.h"
- #include "mtproto/mtproto_config.h"
- #include "mtproto/mtproto_dc_options.h"
- #include "main/main_domain.h"
- #include "main/main_account.h"
- #include "main/main_session.h"
- #include "window/themes/window_theme.h"
- #include "lang/lang_instance.h"
- #include <QtCore/QDirIterator>
- #ifndef Q_OS_WIN
- #include <unistd.h>
- #endif // Q_OS_WIN
- //extern "C" {
- //#include <openssl/evp.h>
- //} // extern "C"
- namespace Local {
- namespace {
- constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
- constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000);
- constexpr auto kSavedBackgroundFormat = QImage::Format_ARGB32_Premultiplied;
- constexpr auto kWallPaperLegacySerializeTagId = int32(-111);
- constexpr auto kWallPaperSerializeTagId = int32(-112);
- constexpr auto kWallPaperSidesLimit = 10'000;
- const auto kThemeNewPathRelativeTag = u"special://new_tag"_q;
- using namespace Storage::details;
- using Storage::FileKey;
- using Database = Storage::Cache::Database;
- QString _basePath, _userBasePath, _userDbPath;
- TaskQueue *_localLoader = nullptr;
- QByteArray _settingsSalt;
- auto OldKey = MTP::AuthKeyPtr();
- auto SettingsKey = MTP::AuthKeyPtr();
- FileKey _themeKeyDay = 0;
- FileKey _themeKeyNight = 0;
- // Theme key legacy may be read in start() with settings.
- // But it should be moved to keyDay or keyNight inside InitialLoadTheme()
- // and never used after.
- FileKey _themeKeyLegacy = 0;
- FileKey _langPackKey = 0;
- FileKey _languagesKey = 0;
- FileKey _backgroundKeyDay = 0;
- FileKey _backgroundKeyNight = 0;
- bool _useGlobalBackgroundKeys = false;
- bool _backgroundCanWrite = true;
- int32 _oldSettingsVersion = 0;
- bool _settingsRewriteNeeded = false;
- bool _settingsWriteAllowed = false;
- enum class WriteMapWhen {
- Now,
- Fast,
- Soon,
- };
- bool CheckStreamStatus(QDataStream &stream) {
- if (stream.status() != QDataStream::Ok) {
- LOG(("Bad data stream status: %1").arg(stream.status()));
- return false;
- }
- return true;
- }
- [[nodiscard]] const MTP::Config &LookupFallbackConfig() {
- static const auto lookupConfig = [](not_null<Main::Account*> account) {
- const auto mtp = &account->mtp();
- const auto production = MTP::Environment::Production;
- return (mtp->environment() == production)
- ? &mtp->config()
- : nullptr;
- };
- const auto &app = Core::App();
- const auto &domain = app.domain();
- if (!domain.started()) {
- return app.fallbackProductionConfig();
- }
- if (const auto result = lookupConfig(&app.activeAccount())) {
- return *result;
- }
- for (const auto &[_, account] : domain.accounts()) {
- if (const auto result = lookupConfig(account.get())) {
- return *result;
- }
- }
- return app.fallbackProductionConfig();
- }
- void applyReadContext(ReadSettingsContext &&context) {
- ApplyReadFallbackConfig(context);
- DEBUG_LOG(("Theme: applying context, legacy: %1, day: %2, night: %3"
- ).arg(context.themeKeyLegacy
- ).arg(context.themeKeyDay
- ).arg(context.themeKeyNight));
- _themeKeyLegacy = context.themeKeyLegacy;
- _themeKeyDay = context.themeKeyDay;
- _themeKeyNight = context.themeKeyNight;
- _backgroundKeyDay = context.backgroundKeyDay;
- _backgroundKeyNight = context.backgroundKeyNight;
- _useGlobalBackgroundKeys = context.backgroundKeysRead;
- _langPackKey = context.langPackKey;
- _languagesKey = context.languagesKey;
- }
- bool _readOldSettings(bool remove, ReadSettingsContext &context) {
- bool result = false;
- auto file = QFile(cWorkingDir() + u"tdata/config"_q);
- if (file.open(QIODevice::ReadOnly)) {
- LOG(("App Info: reading old config..."));
- QDataStream stream(&file);
- stream.setVersion(QDataStream::Qt_5_1);
- qint32 version = 0;
- while (!stream.atEnd()) {
- quint32 blockId;
- stream >> blockId;
- if (!CheckStreamStatus(stream)) break;
- if (blockId == dbiVersion) {
- stream >> version;
- if (!CheckStreamStatus(stream)) break;
- if (version > AppVersion) break;
- } else if (!ReadSetting(blockId, stream, version, context)) {
- break;
- }
- }
- file.close();
- result = true;
- }
- if (remove) file.remove();
- return result;
- }
- 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)) {
- break;
- }
- if (blockId == dbiVersion) {
- stream >> version;
- if (!CheckStreamStatus(stream)) {
- break;
- }
- if (version > AppVersion) return;
- } else if (blockId == dbiEncryptedWithSalt) {
- QByteArray salt, data, decrypted;
- stream >> salt >> data;
- if (!CheckStreamStatus(stream)) {
- break;
- }
- if (salt.size() != 32) {
- LOG(("App Error: bad salt in old user config encrypted part, size: %1").arg(salt.size()));
- continue;
- }
- OldKey = CreateLegacyLocalKey(QByteArray(), salt);
- if (data.size() <= 16 || (data.size() & 0x0F)) {
- LOG(("App Error: bad encrypted part size in old user config: %1").arg(data.size()));
- continue;
- }
- uint32 fullDataLen = data.size() - 16;
- decrypted.resize(fullDataLen);
- const char *dataKey = data.constData(), *encrypted = data.constData() + 16;
- aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, OldKey, dataKey);
- uchar sha1Buffer[20];
- if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) {
- LOG(("App Error: bad decrypt key, data from old user config not decrypted"));
- continue;
- }
- uint32 dataLen = *(const uint32*)decrypted.constData();
- if (dataLen > uint32(decrypted.size()) || dataLen <= fullDataLen - 16 || dataLen < 4) {
- LOG(("App Error: bad decrypted part size in old user config: %1, fullDataLen: %2, decrypted size: %3").arg(dataLen).arg(fullDataLen).arg(decrypted.size()));
- continue;
- }
- decrypted.resize(dataLen);
- QBuffer decryptedStream(&decrypted);
- decryptedStream.open(QIODevice::ReadOnly);
- decryptedStream.seek(4); // skip size
- LOG(("App Info: reading encrypted old user config..."));
- _readOldUserSettingsFields(&decryptedStream, version, context);
- } else if (!ReadSetting(blockId, stream, version, context)) {
- return;
- }
- }
- }
- bool _readOldUserSettings(bool remove, ReadSettingsContext &context) {
- bool result = false;
- // We dropped old test authorizations when migrated to multi auth.
- //const auto testPrefix = (cTestMode() ? u"_test"_q : QString());
- const auto testPrefix = QString();
- QFile file(cWorkingDir() + cDataFile() + testPrefix + u"_config"_q);
- if (file.open(QIODevice::ReadOnly)) {
- LOG(("App Info: reading old user config..."));
- qint32 version = 0;
- _readOldUserSettingsFields(&file, version, context);
- file.close();
- result = true;
- }
- if (remove) file.remove();
- return result;
- }
- 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)) {
- break;
- }
- if (blockId == dbiVersion) {
- stream >> version;
- if (!CheckStreamStatus(stream)) {
- break;
- }
- if (version > AppVersion) return;
- } else if (blockId == dbiEncrypted) {
- QByteArray data, decrypted;
- stream >> data;
- if (!CheckStreamStatus(stream)) {
- break;
- }
- if (!OldKey) {
- LOG(("MTP Error: reading old encrypted keys without old key!"));
- continue;
- }
- if (data.size() <= 16 || (data.size() & 0x0F)) {
- LOG(("MTP Error: bad encrypted part size in old keys: %1").arg(data.size()));
- continue;
- }
- uint32 fullDataLen = data.size() - 16;
- decrypted.resize(fullDataLen);
- const char *dataKey = data.constData(), *encrypted = data.constData() + 16;
- aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, OldKey, dataKey);
- uchar sha1Buffer[20];
- if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) {
- LOG(("MTP Error: bad decrypt key, data from old keys not decrypted"));
- continue;
- }
- uint32 dataLen = *(const uint32*)decrypted.constData();
- if (dataLen > uint32(decrypted.size()) || dataLen <= fullDataLen - 16 || dataLen < 4) {
- LOG(("MTP Error: bad decrypted part size in old keys: %1, fullDataLen: %2, decrypted size: %3").arg(dataLen).arg(fullDataLen).arg(decrypted.size()));
- continue;
- }
- decrypted.resize(dataLen);
- QBuffer decryptedStream(&decrypted);
- decryptedStream.open(QIODevice::ReadOnly);
- decryptedStream.seek(4); // skip size
- LOG(("App Info: reading encrypted old keys..."));
- _readOldMtpDataFields(&decryptedStream, version, context);
- } else if (!ReadSetting(blockId, stream, version, context)) {
- return;
- }
- }
- }
- bool _readOldMtpData(bool remove, ReadSettingsContext &context) {
- bool result = false;
- // We dropped old test authorizations when migrated to multi auth.
- //const auto testPostfix = (cTestMode() ? u"_test"_q : QString());
- const auto testPostfix = QString();
- QFile file(cWorkingDir() + cDataFile() + testPostfix);
- if (file.open(QIODevice::ReadOnly)) {
- LOG(("App Info: reading old keys..."));
- qint32 version = 0;
- _readOldMtpDataFields(&file, version, context);
- file.close();
- result = true;
- }
- if (remove) file.remove();
- return result;
- }
- } // namespace
- void sync() {
- Storage::details::Sync();
- }
- void finish() {
- delete base::take(_localLoader);
- Storage::details::Finish();
- }
- void InitialLoadTheme();
- bool ApplyDefaultNightMode();
- void readLangPack();
- void start() {
- Expects(_basePath.isEmpty());
- _localLoader = new TaskQueue(kFileLoaderQueueStopTimeout);
- _basePath = cWorkingDir() + u"tdata/"_q;
- if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
- ReadSettingsContext context;
- FileReadDescriptor settingsData;
- // We dropped old test authorizations when migrated to multi auth.
- //const auto name = cTestMode() ? u"settings_test"_q : u"settings"_q;
- const auto name = u"settings"_q;
- if (!ReadFile(settingsData, name, _basePath)) {
- _readOldSettings(true, context);
- _readOldUserSettings(false, context); // needed further in _readUserSettings
- _readOldMtpData(false, context); // needed further in _readMtpData
- applyReadContext(std::move(context));
- _settingsRewriteNeeded = true;
- ApplyDefaultNightMode();
- return;
- }
- LOG(("App Info: reading settings..."));
- QByteArray salt, settingsEncrypted;
- settingsData.stream >> salt >> settingsEncrypted;
- if (!CheckStreamStatus(settingsData.stream)) {
- return writeSettings();
- }
- if (salt.size() != LocalEncryptSaltSize) {
- LOG(("App Error: bad salt in settings file, size: %1").arg(salt.size()));
- return writeSettings();
- }
- SettingsKey = CreateLegacyLocalKey(QByteArray(), salt);
- EncryptedDescriptor settings;
- if (!DecryptLocal(settings, settingsEncrypted, SettingsKey)) {
- LOG(("App Error: could not decrypt settings from settings file..."));
- return writeSettings();
- }
- LOG(("App Info: reading encrypted settings..."));
- while (!settings.stream.atEnd()) {
- quint32 blockId;
- settings.stream >> blockId;
- if (!CheckStreamStatus(settings.stream)) {
- return writeSettings();
- }
- if (!ReadSetting(blockId, settings.stream, settingsData.version, context)) {
- return writeSettings();
- }
- }
- _oldSettingsVersion = settingsData.version;
- _settingsSalt = salt;
- applyReadContext(std::move(context));
- if (context.legacyRead) {
- writeSettings();
- }
- InitialLoadTheme();
- if (context.tileRead && _useGlobalBackgroundKeys) {
- Window::Theme::Background()->setTileDayValue(context.tileDay);
- Window::Theme::Background()->setTileNightValue(context.tileNight);
- }
- readLangPack();
- }
- void writeSettings() {
- if (!_settingsWriteAllowed) {
- _settingsRewriteNeeded = true;
- // We need to generate SettingsKey anyway,
- // for the moveLegacyBackground to work.
- if (SettingsKey) {
- return;
- }
- }
- if (_basePath.isEmpty()) {
- LOG(("App Error: _basePath is empty in writeSettings()"));
- return;
- }
- if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
- // We dropped old test authorizations when migrated to multi auth.
- //const auto name = cTestMode() ? u"settings_test"_q : u"settings"_q;
- const auto name = u"settings"_q;
- FileWriteDescriptor settings(name, _basePath);
- if (_settingsSalt.isEmpty() || !SettingsKey) {
- _settingsSalt.resize(LocalEncryptSaltSize);
- base::RandomFill(_settingsSalt.data(), _settingsSalt.size());
- SettingsKey = CreateLegacyLocalKey(QByteArray(), _settingsSalt);
- }
- settings.writeData(_settingsSalt);
- if (!_settingsWriteAllowed) {
- EncryptedDescriptor data(0);
- settings.writeEncrypted(data, SettingsKey);
- return;
- }
- const auto configSerialized = LookupFallbackConfig().serialize();
- const auto applicationSettings = Core::App().settings().serialize();
- quint32 size = 9 * (sizeof(quint32) + sizeof(qint32));
- size += sizeof(quint32) + Serialize::bytearraySize(configSerialized);
- size += sizeof(quint32) + Serialize::bytearraySize(applicationSettings);
- size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath());
- // Theme keys and night mode.
- size += sizeof(quint32) + sizeof(quint64) * 2 + sizeof(quint32);
- size += sizeof(quint32) + sizeof(quint64) * 2;
- if (_langPackKey) {
- size += sizeof(quint32) + sizeof(quint64);
- }
- size += sizeof(quint32) + sizeof(qint32) * 8;
- const auto powerSaving = PowerSaving::Current().value();
- EncryptedDescriptor data(size);
- data.stream << quint32(dbiAutoStart) << qint32(cAutoStart());
- data.stream << quint32(dbiStartMinimized) << qint32(cStartMinimized());
- data.stream << quint32(dbiSendToMenu) << qint32(cSendToMenu());
- data.stream << quint32(dbiSeenTrayTooltip) << qint32(cSeenTrayTooltip());
- data.stream << quint32(dbiAutoUpdate) << qint32(cAutoUpdate());
- data.stream << quint32(dbiLastUpdateCheck) << qint32(cLastUpdateCheck());
- data.stream << quint32(dbiScalePercent) << qint32(cConfigScale());
- data.stream << quint32(dbiFallbackProductionConfig) << configSerialized;
- data.stream << quint32(dbiApplicationSettings) << applicationSettings;
- data.stream << quint32(dbiDialogLastPath) << cDialogLastPath();
- data.stream << quint32(dbiPowerSaving) << qint32(powerSaving);
- data.stream
- << quint32(dbiThemeKey)
- << quint64(_themeKeyDay)
- << quint64(_themeKeyNight)
- << quint32(Window::Theme::IsNightMode() ? 1 : 0);
- if (_useGlobalBackgroundKeys) {
- data.stream
- << quint32(dbiBackgroundKey)
- << quint64(_backgroundKeyDay)
- << quint64(_backgroundKeyNight);
- data.stream
- << quint32(dbiTileBackground)
- << qint32(Window::Theme::Background()->tileDay() ? 1 : 0)
- << qint32(Window::Theme::Background()->tileNight() ? 1 : 0);
- }
- if (_langPackKey) {
- data.stream << quint32(dbiLangPackKey) << quint64(_langPackKey);
- }
- if (_languagesKey) {
- data.stream << quint32(dbiLanguagesKey) << quint64(_languagesKey);
- }
- settings.writeEncrypted(data, SettingsKey);
- }
- void rewriteSettingsIfNeeded() {
- if (_settingsWriteAllowed) {
- return;
- }
- _settingsWriteAllowed = true;
- if (_oldSettingsVersion < AppVersion || _settingsRewriteNeeded) {
- writeSettings();
- }
- }
- const QString &AutoupdatePrefix(const QString &replaceWith = {}) {
- Expects(!Core::UpdaterDisabled());
- static auto value = QString();
- if (!replaceWith.isEmpty()) {
- value = replaceWith;
- }
- return value;
- }
- QString autoupdatePrefixFile() {
- Expects(!Core::UpdaterDisabled());
- return cWorkingDir() + "tdata/prefix";
- }
- const QString &readAutoupdatePrefixRaw() {
- Expects(!Core::UpdaterDisabled());
- const auto &result = AutoupdatePrefix();
- if (!result.isEmpty()) {
- return result;
- }
- QFile f(autoupdatePrefixFile());
- if (f.open(QIODevice::ReadOnly)) {
- const auto value = QString::fromUtf8(f.readAll());
- if (!value.isEmpty()) {
- return AutoupdatePrefix(value);
- }
- }
- return AutoupdatePrefix("https://td.telegram.org");
- }
- void writeAutoupdatePrefix(const QString &prefix) {
- if (Core::UpdaterDisabled()) {
- return;
- }
- const auto current = readAutoupdatePrefixRaw();
- if (current != prefix) {
- AutoupdatePrefix(prefix);
- QFile f(autoupdatePrefixFile());
- if (f.open(QIODevice::WriteOnly)) {
- f.write(prefix.toUtf8());
- f.close();
- }
- if (cAutoUpdate()) {
- Core::UpdateChecker checker;
- checker.start();
- }
- }
- }
- QString readAutoupdatePrefix() {
- Expects(!Core::UpdaterDisabled());
- static const auto RegExp = QRegularExpression("/+$");
- auto result = readAutoupdatePrefixRaw();
- return result.replace(RegExp, QString());
- }
- void writeBackground(const Data::WallPaper &paper, const QImage &image) {
- Expects(_settingsWriteAllowed);
- if (!_backgroundCanWrite) {
- return;
- }
- _useGlobalBackgroundKeys = true;
- 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(copy.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);
- writeSettings();
- }
- 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, SettingsKey);
- }
- bool readBackground() {
- FileReadDescriptor bg;
- auto &backgroundKey = Window::Theme::IsNightMode()
- ? _backgroundKeyNight
- : _backgroundKeyDay;
- if (!ReadEncryptedFile(bg, backgroundKey, _basePath, SettingsKey)) {
- if (backgroundKey) {
- ClearKey(backgroundKey, _basePath);
- backgroundKey = 0;
- writeSettings();
- }
- 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::IsLegacy2DefaultWallPaper(*paper) && bg.version < 3000000)
- || (Data::IsLegacy3DefaultWallPaper(*paper) && bg.version < 3000000)
- || (Data::IsLegacy4DefaultWallPaper(*paper) && bg.version < 3000000)
- || Data::IsDefaultWallPaper(*paper)) {
- _backgroundCanWrite = false;
- if (isOldEmptyImage || bg.version < 3000000) {
- Window::Theme::Background()->set(Data::DefaultWallPaper());
- } 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);
- reader.setAutoTransform(true);
- if (!reader.read(&image)) {
- image = QImage();
- }
- }
- if (!image.isNull() || !paper->backgroundColors().empty()) {
- _backgroundCanWrite = false;
- Window::Theme::Background()->set(*paper, std::move(image));
- _backgroundCanWrite = true;
- return true;
- }
- return false;
- }
- void moveLegacyBackground(
- const QString &fromBasePath,
- const MTP::AuthKeyPtr &fromLocalKey,
- uint64 legacyBackgroundKeyDay,
- uint64 legacyBackgroundKeyNight) {
- if (_useGlobalBackgroundKeys
- || (!legacyBackgroundKeyDay && !legacyBackgroundKeyNight)) {
- return;
- }
- const auto move = [&](uint64 from, FileKey &to) {
- if (!from || to) {
- return;
- }
- to = GenerateKey(_basePath);
- FileReadDescriptor read;
- if (!ReadEncryptedFile(read, from, fromBasePath, fromLocalKey)) {
- return;
- }
- EncryptedDescriptor data;
- data.data = read.data;
- FileWriteDescriptor write(to, _basePath);
- write.writeEncrypted(data, SettingsKey);
- };
- move(legacyBackgroundKeyDay, _backgroundKeyDay);
- move(legacyBackgroundKeyNight, _backgroundKeyNight);
- _useGlobalBackgroundKeys = true;
- _settingsRewriteNeeded = true;
- }
- void reset() {
- if (_localLoader) {
- _localLoader->stop();
- }
- Window::Theme::Background()->reset();
- _oldSettingsVersion = 0;
- Core::App().settings().resetOnLastLogout();
- writeSettings();
- }
- int32 oldSettingsVersion() {
- return _oldSettingsVersion;
- }
- class CountWaveformTask : public Task {
- public:
- CountWaveformTask(not_null<Data::DocumentMedia*> media)
- : _doc(media->owner())
- , _loc(_doc->location(true))
- , _data(media->bytes())
- , _wavemax(0) {
- if (_data.isEmpty() && !_loc.accessEnable()) {
- _doc = nullptr;
- }
- }
- void process() override {
- if (!_doc) return;
- _waveform = audioCountWaveform(_loc, _data);
- _wavemax = _waveform.empty()
- ? char(0)
- : *ranges::max_element(_waveform);
- }
- void finish() override {
- if (const auto voice = _doc ? _doc->voice() : nullptr) {
- if (!_waveform.isEmpty()) {
- voice->waveform = _waveform;
- voice->wavemax = _wavemax;
- }
- if (voice->waveform.isEmpty()) {
- voice->waveform.resize(1);
- voice->waveform[0] = -2;
- voice->wavemax = 0;
- } else if (voice->waveform[0] < 0) {
- voice->waveform[0] = -2;
- voice->wavemax = 0;
- }
- _doc->owner().requestDocumentViewRepaint(_doc);
- }
- }
- ~CountWaveformTask() {
- if (_data.isEmpty() && _doc) {
- _loc.accessDisable();
- }
- }
- protected:
- DocumentData *_doc = nullptr;
- Core::FileLocation _loc;
- QByteArray _data;
- VoiceWaveform _waveform;
- char _wavemax;
- };
- void countVoiceWaveform(not_null<Data::DocumentMedia*> media) {
- const auto document = media->owner();
- if (const auto voice = document->voice()) {
- if (_localLoader) {
- voice->waveform.resize(1 + sizeof(TaskId));
- voice->waveform[0] = -1; // counting
- TaskId taskId = _localLoader->addTask(
- std::make_unique<CountWaveformTask>(media));
- memcpy(voice->waveform.data() + 1, &taskId, sizeof(taskId));
- }
- }
- }
- void cancelTask(TaskId id) {
- if (_localLoader) {
- _localLoader->cancelTask(id);
- }
- }
- Window::Theme::Saved readThemeUsingKey(FileKey key) {
- using namespace Window::Theme;
- FileReadDescriptor theme;
- if (!ReadEncryptedFile(theme, key, _basePath, SettingsKey)) {
- DEBUG_LOG(("Theme: Could not read file for key: %1").arg(key));
- return {};
- }
- auto tag = QString();
- auto result = Saved();
- auto &object = result.object;
- auto &cache = result.cache;
- auto field1 = qint32();
- auto field2 = quint32();
- theme.stream >> object.content;
- theme.stream >> tag >> object.pathAbsolute;
- if (tag == kThemeNewPathRelativeTag) {
- theme.stream
- >> object.pathRelative
- >> object.cloud.id
- >> object.cloud.accessHash
- >> object.cloud.slug
- >> object.cloud.title
- >> object.cloud.documentId
- >> field1;
- } else {
- object.pathRelative = tag;
- }
- if (theme.stream.status() != QDataStream::Ok) {
- DEBUG_LOG(("Theme: Bad status for key: %1, tag: %2"
- ).arg(key
- ).arg(tag));
- return {};
- }
- auto ignoreCache = false;
- if (!object.cloud.id) {
- auto file = QFile(object.pathRelative);
- if (object.pathRelative.isEmpty() || !file.exists()) {
- file.setFileName(object.pathAbsolute);
- }
- if (!file.fileName().isEmpty()
- && file.exists()
- && file.open(QIODevice::ReadOnly)) {
- if (file.size() > kThemeFileSizeLimit) {
- LOG(("Error: theme file too large: %1 "
- "(should be less than 5 MB, got %2)"
- ).arg(file.fileName()
- ).arg(file.size()));
- return {};
- }
- auto fileContent = file.readAll();
- file.close();
- if (object.content != fileContent) {
- object.content = fileContent;
- ignoreCache = true;
- }
- }
- }
- int32 cachePaletteChecksum = 0;
- int32 cacheContentChecksum = 0;
- QByteArray cacheColors;
- QByteArray cacheBackground;
- theme.stream
- >> cachePaletteChecksum
- >> cacheContentChecksum
- >> cacheColors
- >> cacheBackground
- >> field2;
- if (!ignoreCache) {
- if (theme.stream.status() != QDataStream::Ok) {
- DEBUG_LOG(("Theme: Bad status for cache, key: %1, tag: %2"
- ).arg(key
- ).arg(tag));
- return {};
- }
- cache.paletteChecksum = cachePaletteChecksum;
- cache.contentChecksum = cacheContentChecksum;
- cache.colors = std::move(cacheColors);
- cache.background = std::move(cacheBackground);
- cache.tiled = ((field2 & quint32(0xFF)) == 1);
- }
- if (tag == kThemeNewPathRelativeTag) {
- object.cloud.createdBy = UserId(
- ((quint64(field2) >> 8) << 32) | quint64(quint32(field1)));
- }
- return result;
- }
- std::optional<QString> InitialLoadThemeUsingKey(FileKey key) {
- auto read = readThemeUsingKey(key);
- const auto result = read.object.pathAbsolute;
- if (read.object.content.isEmpty()) {
- DEBUG_LOG(("Theme: Could not read content for key: %1").arg(key));
- }
- if (read.object.content.isEmpty()
- || !Window::Theme::Initialize(std::move(read))) {
- DEBUG_LOG(("Theme: Could not initialized for key: %1").arg(key));
- return std::nullopt;
- }
- return result;
- }
- void writeTheme(const Window::Theme::Saved &saved) {
- using namespace Window::Theme;
- if (_themeKeyLegacy) {
- DEBUG_LOG(("Theme: skipping write, because legacy: %1"
- ).arg(_themeKeyLegacy));
- return;
- }
- auto &themeKey = IsNightMode()
- ? _themeKeyNight
- : _themeKeyDay;
- DEBUG_LOG(("Theme: writing (night: %1), key_day: %2, key_night: %3"
- ).arg(Logs::b(IsNightMode())
- ).arg(_themeKeyDay
- ).arg(_themeKeyNight));
- if (saved.object.content.isEmpty()) {
- if (themeKey) {
- if (IsNightMode()) {
- DEBUG_LOG(("Theme: cleared for night mode."));
- SetNightModeValue(false);
- }
- ClearKey(themeKey, _basePath);
- themeKey = 0;
- writeSettings();
- }
- return;
- }
- if (!themeKey) {
- themeKey = GenerateKey(_basePath);
- writeSettings();
- }
- const auto &object = saved.object;
- const auto &cache = saved.cache;
- const auto tag = QString(kThemeNewPathRelativeTag);
- quint32 size = Serialize::bytearraySize(object.content)
- + Serialize::stringSize(tag)
- + Serialize::stringSize(object.pathAbsolute)
- + Serialize::stringSize(object.pathRelative)
- + sizeof(uint64) * 3
- + Serialize::stringSize(object.cloud.slug)
- + Serialize::stringSize(object.cloud.title)
- + sizeof(qint32)
- + sizeof(qint32) * 2
- + Serialize::bytearraySize(cache.colors)
- + Serialize::bytearraySize(cache.background)
- + sizeof(quint32);
- const auto bareCreatedById = object.cloud.createdBy.bare;
- Assert((bareCreatedById & PeerId::kChatTypeMask) == bareCreatedById);
- const auto field1 = qint32(quint32(bareCreatedById & 0xFFFFFFFFULL));
- const auto field2 = quint32(cache.tiled ? 1 : 0)
- | (quint32(bareCreatedById >> 32) << 8);
- EncryptedDescriptor data(size);
- data.stream
- << object.content
- << tag
- << object.pathAbsolute
- << object.pathRelative
- << object.cloud.id
- << object.cloud.accessHash
- << object.cloud.slug
- << object.cloud.title
- << object.cloud.documentId
- << field1
- << cache.paletteChecksum
- << cache.contentChecksum
- << cache.colors
- << cache.background
- << field2;
- FileWriteDescriptor file(themeKey, _basePath);
- file.writeEncrypted(data, SettingsKey);
- }
- void clearTheme() {
- writeTheme(Window::Theme::Saved());
- }
- void InitialLoadTheme() {
- const auto key = (_themeKeyLegacy != 0)
- ? _themeKeyLegacy
- : (Window::Theme::IsNightMode()
- ? _themeKeyNight
- : _themeKeyDay);
- DEBUG_LOG(("Theme: initial load (night: %1), "
- "key_legacy: %2, key_day: %3, key_night: %4"
- ).arg(Logs::b(Window::Theme::IsNightMode())
- ).arg(_themeKeyLegacy
- ).arg(_themeKeyDay
- ).arg(_themeKeyNight));
- if (!key) {
- if (Window::Theme::IsNightMode()) {
- DEBUG_LOG(("Theme: zero key for night mode."));
- Window::Theme::SetNightModeValue(false);
- }
- return;
- } else if (const auto path = InitialLoadThemeUsingKey(key)) {
- DEBUG_LOG(("Theme: loaded with result: %1").arg(*path));
- if (_themeKeyLegacy) {
- Window::Theme::SetNightModeValue(*path
- == Window::Theme::NightThemePath());
- (Window::Theme::IsNightMode()
- ? _themeKeyNight
- : _themeKeyDay) = base::take(_themeKeyLegacy);
- DEBUG_LOG(("Theme: now (night: %1), "
- "key_legacy: %2, key_day: %3, key_night: %4 (path: %5)"
- ).arg(Logs::b(Window::Theme::IsNightMode())
- ).arg(_themeKeyLegacy
- ).arg(_themeKeyDay
- ).arg(_themeKeyNight
- ).arg(*path));
- }
- } else {
- DEBUG_LOG(("Theme: could not load, clearing.."));
- clearTheme();
- }
- }
- bool ApplyDefaultNightMode() {
- const auto NightByDefault = Platform::IsMacStoreBuild();
- if (!NightByDefault
- || Window::Theme::IsNightMode()
- || _themeKeyDay
- || _themeKeyNight
- || _themeKeyLegacy) {
- return false;
- }
- Core::App().startSettingsAndBackground();
- Window::Theme::ToggleNightMode();
- Window::Theme::KeepApplied();
- return true;
- }
- Window::Theme::Saved readThemeAfterSwitch() {
- const auto key = Window::Theme::IsNightMode()
- ? _themeKeyNight
- : _themeKeyDay;
- return readThemeUsingKey(key);
- }
- void readLangPack() {
- FileReadDescriptor langpack;
- if (!_langPackKey || !ReadEncryptedFile(langpack, _langPackKey, _basePath, SettingsKey)) {
- return;
- }
- auto data = QByteArray();
- langpack.stream >> data;
- if (langpack.stream.status() == QDataStream::Ok) {
- Lang::GetInstance().fillFromSerialized(data, langpack.version);
- }
- }
- void writeLangPack() {
- auto langpack = Lang::GetInstance().serialize();
- if (!_langPackKey) {
- _langPackKey = GenerateKey(_basePath);
- writeSettings();
- }
- EncryptedDescriptor data(Serialize::bytearraySize(langpack));
- data.stream << langpack;
- FileWriteDescriptor file(_langPackKey, _basePath);
- file.writeEncrypted(data, SettingsKey);
- }
- void saveRecentLanguages(const std::vector<Lang::Language> &list) {
- if (list.empty()) {
- if (_languagesKey) {
- ClearKey(_languagesKey, _basePath);
- _languagesKey = 0;
- writeSettings();
- }
- return;
- }
- auto size = sizeof(qint32);
- for (const auto &language : list) {
- size += Serialize::stringSize(language.id)
- + Serialize::stringSize(language.pluralId)
- + Serialize::stringSize(language.baseId)
- + Serialize::stringSize(language.name)
- + Serialize::stringSize(language.nativeName);
- }
- if (!_languagesKey) {
- _languagesKey = GenerateKey(_basePath);
- writeSettings();
- }
- EncryptedDescriptor data(size);
- data.stream << qint32(list.size());
- for (const auto &language : list) {
- data.stream
- << language.id
- << language.pluralId
- << language.baseId
- << language.name
- << language.nativeName;
- }
- FileWriteDescriptor file(_languagesKey, _basePath);
- file.writeEncrypted(data, SettingsKey);
- }
- void pushRecentLanguage(const Lang::Language &language) {
- if (language.id.startsWith('#')) {
- return;
- }
- auto list = readRecentLanguages();
- list.erase(
- ranges::remove_if(
- list,
- [&](const Lang::Language &v) { return (v.id == language.id); }),
- end(list));
- list.insert(list.begin(), language);
- saveRecentLanguages(list);
- }
- void removeRecentLanguage(const QString &id) {
- auto list = readRecentLanguages();
- list.erase(
- ranges::remove_if(
- list,
- [&](const Lang::Language &v) { return (v.id == id); }),
- end(list));
- saveRecentLanguages(list);
- }
- std::vector<Lang::Language> readRecentLanguages() {
- FileReadDescriptor languages;
- if (!_languagesKey || !ReadEncryptedFile(languages, _languagesKey, _basePath, SettingsKey)) {
- return {};
- }
- qint32 count = 0;
- languages.stream >> count;
- if (count <= 0) {
- return {};
- }
- auto result = std::vector<Lang::Language>();
- result.reserve(count);
- for (auto i = 0; i != count; ++i) {
- auto language = Lang::Language();
- languages.stream
- >> language.id
- >> language.pluralId
- >> language.baseId
- >> language.name
- >> language.nativeName;
- result.push_back(language);
- }
- if (languages.stream.status() != QDataStream::Ok) {
- return {};
- }
- return result;
- }
- Window::Theme::Object ReadThemeContent() {
- using namespace Window::Theme;
- auto &themeKey = IsNightMode() ? _themeKeyNight : _themeKeyDay;
- if (!themeKey) {
- return Object();
- }
- FileReadDescriptor theme;
- if (!ReadEncryptedFile(theme, themeKey, _basePath, SettingsKey)) {
- return Object();
- }
- QByteArray content;
- QString pathRelative, pathAbsolute;
- theme.stream >> content >> pathRelative >> pathAbsolute;
- if (theme.stream.status() != QDataStream::Ok) {
- return Object();
- }
- auto result = Object();
- result.pathAbsolute = pathAbsolute;
- result.content = content;
- return result;
- }
- void incrementRecentHashtag(RecentHashtagPack &recent, const QString &tag) {
- auto i = recent.begin(), e = recent.end();
- for (; i != e; ++i) {
- if (i->first == tag) {
- ++i->second;
- if (qAbs(i->second) > 0x4000) {
- for (auto j = recent.begin(); j != e; ++j) {
- if (j->second > 1) {
- j->second /= 2;
- } else if (j->second > 0) {
- j->second = 1;
- }
- }
- }
- for (; i != recent.begin(); --i) {
- if (qAbs((i - 1)->second) > qAbs(i->second)) {
- break;
- }
- qSwap(*i, *(i - 1));
- }
- break;
- }
- }
- if (i == e) {
- while (recent.size() >= 64) recent.pop_back();
- recent.push_back(qMakePair(tag, 1));
- for (i = recent.end() - 1; i != recent.begin(); --i) {
- if ((i - 1)->second > i->second) {
- break;
- }
- qSwap(*i, *(i - 1));
- }
- }
- }
- bool readOldMtpData(bool remove, ReadSettingsContext &context) {
- return _readOldMtpData(remove, context);
- }
- bool readOldUserSettings(bool remove, ReadSettingsContext &context) {
- return _readOldUserSettings(remove, context);
- }
- } // namespace Local
|