From 8d52ca6be620a1cbc1374410746afc31e677dc3c Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 17 Jun 2018 09:39:03 +0100 Subject: [PATCH] Apply file type/size restrictions in export. --- .../export/data/export_data_types.cpp | 1 + .../export/data/export_data_types.h | 8 ++ .../SourceFiles/export/export_api_wrap.cpp | 121 +++++++++++++----- Telegram/SourceFiles/export/export_api_wrap.h | 20 ++- .../export/output/export_output_text.cpp | 14 +- 5 files changed, 123 insertions(+), 41 deletions(-) diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 6a46c26795cbb..cf55e70fc2c31 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -142,6 +142,7 @@ void ParseAttributes( }, [&](const MTPDdocumentAttributeAnimated &data) { result.isAnimated = true; }, [&](const MTPDdocumentAttributeSticker &data) { + result.isSticker = true; result.stickerEmoji = ParseString(data.valt); }, [&](const MTPDdocumentAttributeVideo &data) { if (data.is_round_message()) { diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 78664ef3b8b7e..8e7a07a54ae42 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -51,6 +51,12 @@ struct FileLocation { }; struct File { + enum class SkipReason { + None, + Unavailable, + FileType, + FileSize, + }; FileLocation location; int size = 0; QByteArray content; @@ -58,6 +64,7 @@ struct File { QString suggestedPath; QString relativePath; + SkipReason skipReason = SkipReason::None; }; struct Image { @@ -89,6 +96,7 @@ struct Document { Utf8String songTitle; int duration = 0; + bool isSticker = false; bool isAnimated = false; bool isVideoMessage = false; bool isVoiceMessage = false; diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index d21d3127232b3..1580e4f288599 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -26,9 +26,8 @@ constexpr auto kChatsSliceLimit = 100; constexpr auto kMessagesSliceLimit = 100; constexpr auto kFileMaxSize = 1500 * 1024 * 1024; -bool WillLoadFile(const Data::File &file) { - return file.relativePath.isEmpty() - && (!file.content.isEmpty() || file.location.dcId != 0); +bool FileIsAvailable(const Data::File &file) { + return file.relativePath.isEmpty() && (file.location.dcId != 0); } } // namespace @@ -258,17 +257,16 @@ void ApiWrap::loadNextUserpic() { Expects(_userpicsProcess != nullptr); Expects(_userpicsProcess->slice.has_value()); - const auto &list = _userpicsProcess->slice->list; + auto &list = _userpicsProcess->slice->list; while (true) { const auto index = ++_userpicsProcess->fileIndex; if (index >= list.size()) { break; } - const auto &file = list[index].image.file; - if (WillLoadFile(file)) { - loadFile( - file, - [=](const QString &path) { loadUserpicDone(path); }); + const auto ready = processFileLoad( + list[index].image.file, + [=](const QString &path) { loadUserpicDone(path); }); + if (!ready) { return; } } @@ -510,17 +508,16 @@ void ApiWrap::loadNextMessageFile() { Expects(_dialogsProcess->single->slice.has_value()); const auto process = _dialogsProcess->single.get(); - const auto &list = process->slice->list; + auto &list = process->slice->list; while (true) { const auto index = ++process->fileIndex; if (index >= list.size()) { break; } - const auto &file = list[index].file(); - if (WillLoadFile(file)) { - loadFile( - file, - [=](const QString &path) { loadMessageFileDone(path); }); + const auto ready = processFileLoad( + list[index].file(), + [=](const QString &path) { loadMessageFileDone(path); }); + if (!ready) { return; } } @@ -569,32 +566,92 @@ void ApiWrap::finishDialogs() { base::take(_dialogsProcess)->finish(); } -void ApiWrap::loadFile(const Data::File &file, FnMut done) { - Expects(_fileProcess == nullptr); +bool ApiWrap::processFileLoad( + Data::File &file, + FnMut done, + Data::Message *message) { + using SkipReason = Data::File::SkipReason; + + if (!file.relativePath.isEmpty()) { + return true; + } else if (writePreloadedFile(file)) { + return true; + } else if (!FileIsAvailable(file)) { + file.skipReason = SkipReason::Unavailable; + return true; + } + + using Type = MediaSettings::Type; + const auto type = message ? message->media.content.match( + [&](const Data::Document &data) { + if (data.isSticker) { + return Type::Sticker; + } else if (data.isVideoMessage) { + return Type::VideoMessage; + } else if (data.isVoiceMessage) { + return Type::VoiceMessage; + } else if (data.isAnimated) { + return Type::GIF; + } else if (data.isVideoFile) { + return Type::Video; + } else { + return Type::File; + } + }, [](const auto &data) { + return Type::Photo; + }) : Type::Photo; + + if ((_settings->media.types & type) != type) { + file.skipReason = SkipReason::FileType; + return true; + } else if (file.size >= _settings->media.sizeLimit) { + file.skipReason = SkipReason::FileSize; + return true; + } + loadFile(file, std::move(done)); + return false; +} + +bool ApiWrap::writePreloadedFile(Data::File &file) { Expects(_settings != nullptr); - Expects(WillLoadFile(file)); using namespace Output; - const auto relativePath = File::PrepareRelativePath( - _settings->path, - file.suggestedPath); - _fileProcess = std::make_unique( - _settings->path + relativePath); - _fileProcess->relativePath = relativePath; - _fileProcess->location = file.location; - _fileProcess->size = file.size; - _fileProcess->done = std::move(done); if (!file.content.isEmpty()) { + const auto process = prepareFileProcess(file); auto &output = _fileProcess->file; if (output.writeBlock(file.content) == File::Result::Success) { - _fileProcess->done(relativePath); - } else { - error(QString("Could not open '%1'.").arg(relativePath)); + file.relativePath = process->relativePath; + return true; } - } else { - loadFilePart(); + error(QString("Could not write '%1'.").arg(process->relativePath)); } + return false; +} + +void ApiWrap::loadFile(const Data::File &file, FnMut done) { + Expects(_fileProcess == nullptr); + Expects(FileIsAvailable(file)); + + _fileProcess = prepareFileProcess(file); + _fileProcess->done = std::move(done); + + loadFilePart(); +} + +auto ApiWrap::prepareFileProcess(const Data::File &file) const +-> std::unique_ptr { + Expects(_settings != nullptr); + + const auto relativePath = Output::File::PrepareRelativePath( + _settings->path, + file.suggestedPath); + auto result = std::make_unique( + _settings->path + relativePath); + result->relativePath = relativePath; + result->location = file.location; + result->size = file.size; + return result; } void ApiWrap::loadFilePart() { diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h index 6db4083765a8e..b0b1b40289594 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.h +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -21,6 +21,7 @@ struct SessionsList; struct DialogsInfo; struct DialogInfo; struct MessagesSlice; +struct Message; } // namespace Data struct Settings; @@ -56,6 +57,10 @@ class ApiWrap { ~ApiWrap(); private: + struct UserpicsProcess; + struct FileProcess; + struct DialogsProcess; + void startMainSession(FnMut done); void handleUserpicsSlice(const MTPphotos_Photos &result); @@ -64,8 +69,6 @@ class ApiWrap { void loadUserpicDone(const QString &relativePath); void finishUserpics(); - void requestSavedContacts(); - void requestDialogsSlice(); void appendDialogsSlice(Data::DialogsInfo &&info); void finishDialogsList(); @@ -75,10 +78,18 @@ class ApiWrap { void requestMessagesSlice(); void loadMessagesFiles(Data::MessagesSlice &&slice); void loadNextMessageFile(); + void loadMessageFileDone(const QString &relativePath); void finishMessages(); void finishDialogs(); + bool processFileLoad( + Data::File &file, + FnMut done, + Data::Message *message = nullptr); + std::unique_ptr prepareFileProcess( + const Data::File &file) const; + bool writePreloadedFile(Data::File &file); void loadFile(const Data::File &file, FnMut done); void loadFilePart(); void filePartDone(int offset, const MTPupload_File &result); @@ -99,13 +110,8 @@ class ApiWrap { std::unique_ptr _settings; MTPInputUser _user = MTP_inputUserSelf(); - struct UserpicsProcess; std::unique_ptr _userpicsProcess; - - struct FileProcess; std::unique_ptr _fileProcess; - - struct DialogsProcess; std::unique_ptr _dialogsProcess; rpl::event_stream _errors; diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index 73c4e64d0ae3b..2bb294866abe7 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -171,6 +171,13 @@ QByteArray SerializeMessage( const auto pushAction = [&](const QByteArray &action) { push("Action", action); }; + const auto pushTTL = [&]( + const QByteArray &label = "Self destruct period") { + if (const auto ttl = message.media.ttl) { + push(label, NumberToString(ttl) + " sec."); + } + }; + message.action.content.match([&](const ActionChatCreate &data) { pushActor(); pushAction("Create group"); @@ -299,11 +306,12 @@ QByteArray SerializeMessage( } message.media.content.match([&](const Photo &photo) { + pushTTL(); }, [&](const Document &data) { const auto pushPath = [&](const QByteArray &label) { push(label, FormatFilePath(data.file)); }; - if (!data.stickerEmoji.isEmpty()) { + if (data.isSticker) { pushPath("Sticker"); push("Emoji", data.stickerEmoji); } else if (data.isVideoMessage) { @@ -321,7 +329,7 @@ QByteArray SerializeMessage( } else { pushPath("File"); } - if (data.stickerEmoji.isEmpty()) { + if (!data.isSticker) { push("Mime type", data.mime); } if (data.duration) { @@ -331,6 +339,7 @@ QByteArray SerializeMessage( push("Width", NumberToString(data.width)); push("Height", NumberToString(data.height)); } + pushTTL(); }, [&](const ContactInfo &data) { push("Contact information", SerializeKeyValue({ { "First name", data.firstName }, @@ -342,6 +351,7 @@ QByteArray SerializeMessage( { "Latitude", NumberToString(data.latitude) }, { "Longitude", NumberToString(data.longitude) }, }) : QByteArray("(empty value)")); + pushTTL("Live location period"); }, [&](const Venue &data) { push("Place name", data.title); push("Address", data.address);