diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 5d4d1b71b..0e529850c 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_drafts.h" #include "data/data_user.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" @@ -35,7 +37,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "window/themes/window_theme.h" #include "window/window_session_controller.h" -#include "base/flags.h" #include "data/data_session.h" #include "history/history.h" #include "facades.h" @@ -251,97 +252,24 @@ struct EncryptedDescriptor { }; struct FileWriteDescriptor { - FileWriteDescriptor(const FileKey &key, FileOptions options = FileOption::User | FileOption::Safe) - : file((options & FileOption::Safe) ? (QFileDevice&)saveFile : plainFile) { - init(toFilePart(key), options); - } - FileWriteDescriptor(const QString &name, FileOptions options = FileOption::User | FileOption::Safe) - : file((options & FileOption::Safe) ? (QFileDevice&)saveFile : plainFile) { - init(name, options); - } - void init(const QString &name, FileOptions options) { - if (options & FileOption::User) { - if (!_userWorking()) return; - } else { - if (!_working()) return; - } + FileWriteDescriptor( + const FileKey &key, + FileOptions options = FileOption::User | FileOption::Safe); + FileWriteDescriptor( + const QString &name, + FileOptions options = FileOption::User | FileOption::Safe); + ~FileWriteDescriptor(); - const auto base = ((options & FileOption::User) ? _userBasePath : _basePath) + name; - if (options & FileOption::Safe) { - toDelete = base; - saveFile.setFileName(base + 's'); - } else { - plainFile.setFileName(base + '0'); - } - if (file.open(QIODevice::WriteOnly)) { - file.write(tdfMagic, tdfMagicLen); - qint32 version = AppVersion; - file.write((const char*)&version, sizeof(version)); + void init(const QString &name, FileOptions options); + bool writeData(const QByteArray &data); + bool writeEncrypted( + EncryptedDescriptor &data, + const MTP::AuthKeyPtr &key = LocalKey); + void finish(); - stream.setDevice(&file); - stream.setVersion(QDataStream::Qt_5_1); - } - } - bool writeData(const QByteArray &data) { - if (!file.isOpen()) return false; - - 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()); - dataSize += sizeof(len) + data.size(); - - return true; - } - static 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; - } - bool writeEncrypted(EncryptedDescriptor &data, const MTP::AuthKeyPtr &key = LocalKey) { - return writeData(prepareEncrypted(data, key)); - } - void finish() { - if (!file.isOpen()) return; - - stream.setDevice(nullptr); - - md5.feed(&dataSize, sizeof(dataSize)); - qint32 version = AppVersion; - md5.feed(&version, sizeof(version)); - md5.feed(tdfMagic, tdfMagicLen); - file.write((const char*)md5.result(), 0x10); - - if (saveFile.isOpen()) { - saveFile.commit(); - } else { - plainFile.close(); - } - - if (!toDelete.isEmpty()) { - QFile::remove(toDelete + '0'); - QFile::remove(toDelete + '1'); - } - } QFile plainFile; QSaveFile saveFile; - QFileDevice &file; + not_null file; QDataStream stream; QString toDelete; @@ -349,11 +277,141 @@ struct FileWriteDescriptor { HashMd5 md5; int32 dataSize = 0; - ~FileWriteDescriptor() { - finish(); - } }; +[[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, + FileOptions options) +: file((options & FileOption::Safe) ? (QFileDevice*)&saveFile : &plainFile) { + init(toFilePart(key), options); +} + +FileWriteDescriptor::FileWriteDescriptor( + const QString &name, + FileOptions options) +: file((options & FileOption::Safe) ? (QFileDevice*)&saveFile : &plainFile) { + init(name, options); +} + +FileWriteDescriptor::~FileWriteDescriptor() { + finish(); +} + +void FileWriteDescriptor::init(const QString &name, FileOptions options) { + if (options & FileOption::User) { + if (!_userWorking()) return; + } else { + if (!_working()) return; + } + + const auto base = ((options & FileOption::User) ? _userBasePath : _basePath) + name; + saveFile.setFileName(base + 's'); + plainFile.setFileName(base + '0'); + if (options & FileOption::Safe) { + toDelete = base; + } + if (!file->open(QIODevice::WriteOnly)) { + LOG(("Storage Error: Could not open '%1' for writing.").arg(file->fileName())); + if (!(options & FileOption::Safe)) { + return; + } + file = &plainFile; + LOG(("Storage Info: Trying to fallback to '%1'.").arg(file->fileName())); + if (!file->open(QIODevice::WriteOnly)) { + LOG(("Storage Error: Could not open '%1' for safe writing.").arg(file->fileName())); + return; + } + } + file->write(tdfMagic, tdfMagicLen); + qint32 version = AppVersion; + file->write((const char*)&version, sizeof(version)); + + stream.setDevice(file); + stream.setVersion(QDataStream::Qt_5_1); +} + +bool FileWriteDescriptor::writeData(const QByteArray &data) { + if (!file->isOpen()) { + return false; + } + + 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()); + dataSize += sizeof(len) + data.size(); + + return true; +} + +bool FileWriteDescriptor::writeEncrypted( + EncryptedDescriptor &data, + const MTP::AuthKeyPtr &key) { + return writeData(PrepareEncrypted(data, key)); +} + +void FileWriteDescriptor::finish() { + if (!file->isOpen()) { + return; + } + + stream.setDevice(nullptr); + + md5.feed(&dataSize, sizeof(dataSize)); + qint32 version = AppVersion; + md5.feed(&version, sizeof(version)); + md5.feed(tdfMagic, tdfMagicLen); + file->write((const char*)md5.result(), 0x10); + + if (saveFile.isOpen()) { + if (!saveFile.commit()) { + LOG(("Storage Error: Could not commit safe writing in '%1'." + ).arg(saveFile.fileName())); + } + } else { + plainFile.close(); + if (!toDelete.isEmpty()) { + QFile::remove(toDelete + '1'); + if (!base::Platform::RenameWithOverwrite( + plainFile.fileName(), + saveFile.fileName())) { + LOG(("Storage Error: Could not rename '%1' to '%2'" + ).arg(plainFile.fileName() + ).arg(saveFile.fileName())); + } + } + } + + if (!toDelete.isEmpty()) { + QFile::remove(toDelete + '0'); + QFile::remove(toDelete + '1'); + } +} + bool readFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe) { if (options & FileOption::User) { if (!_userWorking()) return false; @@ -2482,7 +2540,7 @@ void _writeMap(WriteMapWhen when) { EncryptedDescriptor passKeyData(kLocalKeySize); LocalKey->write(passKeyData.stream); - _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, PassKey); + _passKeyEncrypted = PrepareEncrypted(passKeyData, PassKey); } map.writeData(_passKeySalt); map.writeData(_passKeyEncrypted); @@ -2868,7 +2926,7 @@ void setPasscode(const QByteArray &passcode) { EncryptedDescriptor passKeyData(kLocalKeySize); LocalKey->write(passKeyData.stream); - _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, PassKey); + _passKeyEncrypted = PrepareEncrypted(passKeyData, PassKey); _mapChanged = true; _writeMap(WriteMapWhen::Now); diff --git a/Telegram/lib_base b/Telegram/lib_base index 7de25679d..05b4e4e2a 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 7de25679dd68f3fb7d2faee77ff5311f4d9587d6 +Subproject commit 05b4e4e2a813b2888858acde80f67c4eafffdbce