From dfaf2af5cc4d32b30a5e6b6f71a0d99c067047f3 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Mon, 13 Oct 2025 22:19:24 +0200 Subject: [PATCH] add is_decodable to files --- src/odr/exceptions.cpp | 6 ++++++ src/odr/exceptions.hpp | 10 ++++++++++ src/odr/file.cpp | 4 ++++ src/odr/file.hpp | 2 ++ src/odr/internal/abstract/file.hpp | 2 ++ src/odr/internal/cfb/cfb_file.cpp | 2 ++ src/odr/internal/cfb/cfb_file.hpp | 2 ++ src/odr/internal/common/image_file.cpp | 8 +++++++- src/odr/internal/common/image_file.hpp | 2 ++ src/odr/internal/csv/csv_file.cpp | 2 ++ src/odr/internal/csv/csv_file.hpp | 2 ++ src/odr/internal/json/json_file.cpp | 2 ++ src/odr/internal/json/json_file.hpp | 2 ++ src/odr/internal/odf/odf_file.cpp | 11 +++++++++-- src/odr/internal/odf/odf_file.hpp | 2 ++ src/odr/internal/oldms/oldms_file.cpp | 4 +++- src/odr/internal/oldms/oldms_file.hpp | 2 ++ src/odr/internal/oldms_wvware/wvware_oldms_file.cpp | 4 +++- src/odr/internal/oldms_wvware/wvware_oldms_file.hpp | 2 ++ src/odr/internal/ooxml/ooxml_file.cpp | 11 +++++++++-- src/odr/internal/ooxml/ooxml_file.hpp | 2 ++ src/odr/internal/pdf/pdf_file.cpp | 2 ++ src/odr/internal/pdf/pdf_file.hpp | 2 ++ src/odr/internal/pdf_poppler/poppler_pdf_file.cpp | 2 ++ src/odr/internal/pdf_poppler/poppler_pdf_file.hpp | 2 ++ src/odr/internal/svm/svm_file.cpp | 8 +++++++- src/odr/internal/svm/svm_file.hpp | 2 ++ src/odr/internal/text/text_file.cpp | 2 ++ src/odr/internal/text/text_file.hpp | 2 ++ src/odr/internal/zip/zip_file.cpp | 2 ++ src/odr/internal/zip/zip_file.hpp | 2 ++ 31 files changed, 102 insertions(+), 8 deletions(-) diff --git a/src/odr/exceptions.cpp b/src/odr/exceptions.cpp index 77b0fd8a6..a382ca202 100644 --- a/src/odr/exceptions.cpp +++ b/src/odr/exceptions.cpp @@ -129,4 +129,10 @@ NotEncryptedError::NotEncryptedError() InvalidPath::InvalidPath(const std::string &message) : std::runtime_error("invalid path: " + message) {} +UnsupportedFileEncoding::UnsupportedFileEncoding(const std::string &message) + : std::runtime_error("unsupported file encoding: " + message) {} + +FileEncryptedError::FileEncryptedError() + : std::runtime_error("file encrypted error") {} + } // namespace odr diff --git a/src/odr/exceptions.hpp b/src/odr/exceptions.hpp index 9bbb29f1f..54704e910 100644 --- a/src/odr/exceptions.hpp +++ b/src/odr/exceptions.hpp @@ -220,4 +220,14 @@ struct InvalidPath final : std::runtime_error { explicit InvalidPath(const std::string &message); }; +/// @brief Unsupported file encoding +struct UnsupportedFileEncoding final : std::runtime_error { + explicit UnsupportedFileEncoding(const std::string &message); +}; + +/// @brief File is encrypted +struct FileEncryptedError final : std::runtime_error { + explicit FileEncryptedError(); +}; + } // namespace odr diff --git a/src/odr/file.cpp b/src/odr/file.cpp index 43b650878..b598b8123 100644 --- a/src/odr/file.cpp +++ b/src/odr/file.cpp @@ -125,6 +125,10 @@ DecodedFile DecodedFile::decrypt(const std::string &password) const { return DecodedFile(m_impl->decrypt(password)); } +bool DecodedFile::is_decodable() const noexcept { + return m_impl->is_decodable(); +} + bool DecodedFile::is_text_file() const { return std::dynamic_pointer_cast(m_impl) != nullptr; diff --git a/src/odr/file.hpp b/src/odr/file.hpp index 64fd76457..37c1f14ab 100644 --- a/src/odr/file.hpp +++ b/src/odr/file.hpp @@ -200,6 +200,8 @@ class DecodedFile { [[nodiscard]] EncryptionState encryption_state() const; [[nodiscard]] DecodedFile decrypt(const std::string &password) const; + [[nodiscard]] bool is_decodable() const noexcept; + [[nodiscard]] bool is_text_file() const; [[nodiscard]] bool is_image_file() const; [[nodiscard]] bool is_archive_file() const; diff --git a/src/odr/internal/abstract/file.hpp b/src/odr/internal/abstract/file.hpp index 9f25783e6..9906fb69e 100644 --- a/src/odr/internal/abstract/file.hpp +++ b/src/odr/internal/abstract/file.hpp @@ -50,6 +50,8 @@ class DecodedFile { (void)password; return nullptr; } + + [[nodiscard]] virtual bool is_decodable() const noexcept = 0; }; class TextFile : public DecodedFile { diff --git a/src/odr/internal/cfb/cfb_file.cpp b/src/odr/internal/cfb/cfb_file.cpp index 896aa026f..0e9677793 100644 --- a/src/odr/internal/cfb/cfb_file.cpp +++ b/src/odr/internal/cfb/cfb_file.cpp @@ -26,6 +26,8 @@ DecoderEngine CfbFile::decoder_engine() const noexcept { return DecoderEngine::odr; } +bool CfbFile::is_decodable() const noexcept { return true; } + std::shared_ptr CfbFile::archive() const { return std::make_shared(m_cfb); } diff --git a/src/odr/internal/cfb/cfb_file.hpp b/src/odr/internal/cfb/cfb_file.hpp index a6fab7c26..14300105c 100644 --- a/src/odr/internal/cfb/cfb_file.hpp +++ b/src/odr/internal/cfb/cfb_file.hpp @@ -27,6 +27,8 @@ class CfbFile final : public abstract::ArchiveFile { [[nodiscard]] FileMeta file_meta() const noexcept override; [[nodiscard]] DecoderEngine decoder_engine() const noexcept override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr archive() const override; private: diff --git a/src/odr/internal/common/image_file.cpp b/src/odr/internal/common/image_file.cpp index 9cc751bad..475c015ec 100644 --- a/src/odr/internal/common/image_file.cpp +++ b/src/odr/internal/common/image_file.cpp @@ -1,5 +1,7 @@ #include +#include + namespace odr::internal { ImageFile::ImageFile(std::shared_ptr file, @@ -18,6 +20,10 @@ DecoderEngine ImageFile::decoder_engine() const noexcept { return DecoderEngine::odr; } -std::shared_ptr ImageFile::image() const { return {}; } +bool ImageFile::is_decodable() const noexcept { return false; } + +std::shared_ptr ImageFile::image() const { + throw UnsupportedFileEncoding("generally unsupported"); +} } // namespace odr::internal diff --git a/src/odr/internal/common/image_file.hpp b/src/odr/internal/common/image_file.hpp index 0c0c84c32..87a7b8178 100644 --- a/src/odr/internal/common/image_file.hpp +++ b/src/odr/internal/common/image_file.hpp @@ -14,6 +14,8 @@ class ImageFile final : public abstract::ImageFile { [[nodiscard]] FileMeta file_meta() const noexcept override; [[nodiscard]] DecoderEngine decoder_engine() const noexcept override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr image() const override; private: diff --git a/src/odr/internal/csv/csv_file.cpp b/src/odr/internal/csv/csv_file.cpp index 981b06317..82c61e18f 100644 --- a/src/odr/internal/csv/csv_file.cpp +++ b/src/odr/internal/csv/csv_file.cpp @@ -28,4 +28,6 @@ DecoderEngine CsvFile::decoder_engine() const noexcept { return DecoderEngine::odr; } +bool CsvFile::is_decodable() const noexcept { return false; } + } // namespace odr::internal::csv diff --git a/src/odr/internal/csv/csv_file.hpp b/src/odr/internal/csv/csv_file.hpp index b8c91762c..838af7b94 100644 --- a/src/odr/internal/csv/csv_file.hpp +++ b/src/odr/internal/csv/csv_file.hpp @@ -18,6 +18,8 @@ class CsvFile final : public abstract::TextFile { [[nodiscard]] FileMeta file_meta() const noexcept override; [[nodiscard]] DecoderEngine decoder_engine() const noexcept override; + [[nodiscard]] bool is_decodable() const noexcept override; + private: std::shared_ptr m_file; std::string m_charset; diff --git a/src/odr/internal/json/json_file.cpp b/src/odr/internal/json/json_file.cpp index cfe2d3bc4..76dbbaaf7 100644 --- a/src/odr/internal/json/json_file.cpp +++ b/src/odr/internal/json/json_file.cpp @@ -28,4 +28,6 @@ DecoderEngine JsonFile::decoder_engine() const noexcept { return DecoderEngine::odr; } +bool JsonFile::is_decodable() const noexcept { return false; } + } // namespace odr::internal::json diff --git a/src/odr/internal/json/json_file.hpp b/src/odr/internal/json/json_file.hpp index 6f0510173..5a7c4c973 100644 --- a/src/odr/internal/json/json_file.hpp +++ b/src/odr/internal/json/json_file.hpp @@ -18,6 +18,8 @@ class JsonFile final : public abstract::TextFile { [[nodiscard]] FileMeta file_meta() const noexcept override; [[nodiscard]] DecoderEngine decoder_engine() const noexcept override; + [[nodiscard]] bool is_decodable() const noexcept override; + private: std::shared_ptr m_file; std::string m_charset; diff --git a/src/odr/internal/odf/odf_file.cpp b/src/odr/internal/odf/odf_file.cpp index 022cd98d2..f1fbfa148 100644 --- a/src/odr/internal/odf/odf_file.cpp +++ b/src/odr/internal/odf/odf_file.cpp @@ -77,8 +77,15 @@ OpenDocumentFile::decrypt(const std::string &password) const { return decrypted_file; } +bool OpenDocumentFile::is_decodable() const noexcept { + return m_encryption_state != EncryptionState::encrypted; +} + std::shared_ptr OpenDocumentFile::document() const { - // TODO throw if encrypted + if (m_encryption_state == EncryptionState::encrypted) { + throw FileEncryptedError(); + } + switch (file_type()) { case FileType::opendocument_text: return std::make_shared(m_file_meta.type, DocumentType::text, @@ -93,7 +100,7 @@ std::shared_ptr OpenDocumentFile::document() const { return std::make_shared(m_file_meta.type, DocumentType::drawing, m_filesystem); default: - throw UnsupportedOperation(); + throw UnsupportedFileType(file_type()); } } diff --git a/src/odr/internal/odf/odf_file.hpp b/src/odr/internal/odf/odf_file.hpp index 99db21696..d2075a84c 100644 --- a/src/odr/internal/odf/odf_file.hpp +++ b/src/odr/internal/odf/odf_file.hpp @@ -37,6 +37,8 @@ class OpenDocumentFile final : public virtual abstract::DocumentFile { [[nodiscard]] std::shared_ptr decrypt(const std::string &password) const override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr document() const override; private: diff --git a/src/odr/internal/oldms/oldms_file.cpp b/src/odr/internal/oldms/oldms_file.cpp index 77c85d6f6..75bd15130 100644 --- a/src/odr/internal/oldms/oldms_file.cpp +++ b/src/odr/internal/oldms/oldms_file.cpp @@ -84,8 +84,10 @@ LegacyMicrosoftFile::decrypt(const std::string &password) const { "odrcore does not support decryption of legacy Microsoft files"); } +bool LegacyMicrosoftFile::is_decodable() const noexcept { return false; } + std::shared_ptr LegacyMicrosoftFile::document() const { - throw UnsupportedOperation( + throw UnsupportedFileEncoding( "odrcore does not support reading legacy Microsoft files"); } diff --git a/src/odr/internal/oldms/oldms_file.hpp b/src/odr/internal/oldms/oldms_file.hpp index d79ada6d6..0206f084e 100644 --- a/src/odr/internal/oldms/oldms_file.hpp +++ b/src/odr/internal/oldms/oldms_file.hpp @@ -34,6 +34,8 @@ class LegacyMicrosoftFile final : public abstract::DocumentFile { [[nodiscard]] std::shared_ptr decrypt(const std::string &password) const override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr document() const override; private: diff --git a/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp b/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp index fb963bd39..f5f813b86 100644 --- a/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp +++ b/src/odr/internal/oldms_wvware/wvware_oldms_file.cpp @@ -136,9 +136,11 @@ WvWareLegacyMicrosoftFile::decrypt(const std::string &password) const { return decrypted; } +bool WvWareLegacyMicrosoftFile::is_decodable() const noexcept { return false; } + std::shared_ptr WvWareLegacyMicrosoftFile::document() const { - return {}; // TODO throw + throw UnsupportedFileEncoding("generally unsupported"); } wvParseStruct &WvWareLegacyMicrosoftFile::parse_struct() const { diff --git a/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp b/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp index 191e70055..ea183036b 100644 --- a/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp +++ b/src/odr/internal/oldms_wvware/wvware_oldms_file.hpp @@ -35,6 +35,8 @@ class WvWareLegacyMicrosoftFile final : public abstract::DocumentFile { [[nodiscard]] std::shared_ptr decrypt(const std::string &password) const override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr document() const override; [[nodiscard]] wvParseStruct &parse_struct() const; diff --git a/src/odr/internal/ooxml/ooxml_file.cpp b/src/odr/internal/ooxml/ooxml_file.cpp index b4489aa47..52cff006f 100644 --- a/src/odr/internal/ooxml/ooxml_file.cpp +++ b/src/odr/internal/ooxml/ooxml_file.cpp @@ -84,8 +84,15 @@ OfficeOpenXmlFile::decrypt(const std::string &password) const { return decrypted; } +bool OfficeOpenXmlFile::is_decodable() const noexcept { + return m_encryption_state != EncryptionState::encrypted; +} + std::shared_ptr OfficeOpenXmlFile::document() const { - // TODO throw if encrypted + if (m_encryption_state == EncryptionState::encrypted) { + throw FileEncryptedError(); + } + switch (file_type()) { case FileType::office_open_xml_document: return std::make_shared(m_filesystem); @@ -94,7 +101,7 @@ std::shared_ptr OfficeOpenXmlFile::document() const { case FileType::office_open_xml_workbook: return std::make_shared(m_filesystem); default: - throw UnsupportedOperation(); + throw UnsupportedFileType(file_type()); } } diff --git a/src/odr/internal/ooxml/ooxml_file.hpp b/src/odr/internal/ooxml/ooxml_file.hpp index 13b123fab..77fe81295 100644 --- a/src/odr/internal/ooxml/ooxml_file.hpp +++ b/src/odr/internal/ooxml/ooxml_file.hpp @@ -36,6 +36,8 @@ class OfficeOpenXmlFile final : public abstract::DocumentFile { [[nodiscard]] std::shared_ptr decrypt(const std::string &password) const override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr document() const override; private: diff --git a/src/odr/internal/pdf/pdf_file.cpp b/src/odr/internal/pdf/pdf_file.cpp index 7ad8efbe9..44eb8572e 100644 --- a/src/odr/internal/pdf/pdf_file.cpp +++ b/src/odr/internal/pdf/pdf_file.cpp @@ -29,4 +29,6 @@ PdfFile::decrypt(const std::string &password) const { return nullptr; } +bool PdfFile::is_decodable() const noexcept { return false; } + } // namespace odr::internal diff --git a/src/odr/internal/pdf/pdf_file.hpp b/src/odr/internal/pdf/pdf_file.hpp index 4b3605dd5..02d4d77dc 100644 --- a/src/odr/internal/pdf/pdf_file.hpp +++ b/src/odr/internal/pdf/pdf_file.hpp @@ -18,6 +18,8 @@ class PdfFile final : public abstract::PdfFile { [[nodiscard]] std::shared_ptr decrypt(const std::string &password) const override; + [[nodiscard]] bool is_decodable() const noexcept override; + private: std::shared_ptr m_file; FileMeta m_file_meta; diff --git a/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp b/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp index 87b14aa8a..51c8c957a 100644 --- a/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp +++ b/src/odr/internal/pdf_poppler/poppler_pdf_file.cpp @@ -98,6 +98,8 @@ PopplerPdfFile::decrypt(const std::string &password) const { return decrypted_file; } +bool PopplerPdfFile::is_decodable() const noexcept { return false; } + PDFDoc &PopplerPdfFile::pdf_doc() const { return *m_pdf_doc; } } // namespace odr::internal diff --git a/src/odr/internal/pdf_poppler/poppler_pdf_file.hpp b/src/odr/internal/pdf_poppler/poppler_pdf_file.hpp index 6d393f98a..c30163578 100644 --- a/src/odr/internal/pdf_poppler/poppler_pdf_file.hpp +++ b/src/odr/internal/pdf_poppler/poppler_pdf_file.hpp @@ -23,6 +23,8 @@ class PopplerPdfFile final : public abstract::PdfFile { [[nodiscard]] std::shared_ptr decrypt(const std::string &password) const override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] PDFDoc &pdf_doc() const; private: diff --git a/src/odr/internal/svm/svm_file.cpp b/src/odr/internal/svm/svm_file.cpp index 72b64e768..c82b4e727 100644 --- a/src/odr/internal/svm/svm_file.cpp +++ b/src/odr/internal/svm/svm_file.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -35,6 +37,10 @@ DecoderEngine SvmFile::decoder_engine() const noexcept { return DecoderEngine::odr; } -std::shared_ptr SvmFile::image() const { return {}; } +bool SvmFile::is_decodable() const noexcept { return false; } + +std::shared_ptr SvmFile::image() const { + throw UnsupportedFileEncoding("generally unsupported"); +} } // namespace odr::internal::svm diff --git a/src/odr/internal/svm/svm_file.hpp b/src/odr/internal/svm/svm_file.hpp index ab89673b6..6d033c00c 100644 --- a/src/odr/internal/svm/svm_file.hpp +++ b/src/odr/internal/svm/svm_file.hpp @@ -21,6 +21,8 @@ class SvmFile final : public abstract::ImageFile { [[nodiscard]] FileMeta file_meta() const noexcept override; [[nodiscard]] DecoderEngine decoder_engine() const noexcept override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr image() const override; private: diff --git a/src/odr/internal/text/text_file.cpp b/src/odr/internal/text/text_file.cpp index c5db55030..433379830 100644 --- a/src/odr/internal/text/text_file.cpp +++ b/src/odr/internal/text/text_file.cpp @@ -26,4 +26,6 @@ DecoderEngine TextFile::decoder_engine() const noexcept { return DecoderEngine::odr; } +bool TextFile::is_decodable() const noexcept { return false; } + } // namespace odr::internal::text diff --git a/src/odr/internal/text/text_file.hpp b/src/odr/internal/text/text_file.hpp index 2284ea244..7361dec50 100644 --- a/src/odr/internal/text/text_file.hpp +++ b/src/odr/internal/text/text_file.hpp @@ -19,6 +19,8 @@ class TextFile final : public abstract::TextFile { [[nodiscard]] FileMeta file_meta() const noexcept override; [[nodiscard]] DecoderEngine decoder_engine() const noexcept override; + [[nodiscard]] bool is_decodable() const noexcept override; + private: std::shared_ptr m_file; std::string m_charset; diff --git a/src/odr/internal/zip/zip_file.cpp b/src/odr/internal/zip/zip_file.cpp index 47e3478f3..dc0f82d1f 100644 --- a/src/odr/internal/zip/zip_file.cpp +++ b/src/odr/internal/zip/zip_file.cpp @@ -27,6 +27,8 @@ DecoderEngine ZipFile::decoder_engine() const noexcept { return DecoderEngine::odr; } +bool ZipFile::is_decodable() const noexcept { return true; } + std::shared_ptr ZipFile::archive() const { return std::make_shared(m_zip); } diff --git a/src/odr/internal/zip/zip_file.hpp b/src/odr/internal/zip/zip_file.hpp index acfda0a05..5755d3cdc 100644 --- a/src/odr/internal/zip/zip_file.hpp +++ b/src/odr/internal/zip/zip_file.hpp @@ -28,6 +28,8 @@ class ZipFile final : public abstract::ArchiveFile { [[nodiscard]] FileMeta file_meta() const noexcept override; [[nodiscard]] DecoderEngine decoder_engine() const noexcept override; + [[nodiscard]] bool is_decodable() const noexcept override; + [[nodiscard]] std::shared_ptr archive() const override; private: