filter/Library_filterconfig.mk | 1 filter/source/config/cache/typedetection.cxx | 109 ++++++++++++++++++++ filter/source/storagefilterdetect/filterdetect.cxx | 50 ++++----- include/oox/helper/zipstorage.hxx | 3 oox/source/core/filterdetect.cxx | 14 +- oox/source/core/xmlfilterbase.cxx | 8 + oox/source/dump/pptxdumper.cxx | 2 oox/source/helper/zipstorage.cxx | 4 package/source/xstor/xstorage.cxx | 6 + package/source/xstor/xstorage.hxx | 1 package/source/zipapi/ZipFile.cxx | 45 +++++++- package/source/zippackage/ZipPackage.cxx | 4 sc/qa/unit/data/xlsx/tdf131575.xlsx |binary sc/qa/unit/data/xlsx/tdf76115.xlsx |binary sc/qa/unit/subsequent_filters_test2.cxx | 33 ++++++ sfx2/source/doc/docfile.cxx | 46 ++++---- sw/qa/extras/odfimport/data/unreferenced_stream.odt |binary sw/qa/extras/odfimport/odfimport.cxx | 16 ++ 18 files changed, 282 insertions(+), 60 deletions(-)
New commits: commit c63fd0899374515738b1afb330f541f35b8948de Author: Xisco Fauli <xiscofa...@libreoffice.org> AuthorDate: Thu Feb 1 10:59:09 2024 +0100 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 17:49:57 2024 +0600 tdf#76115: sc_subsequent_filters: Add unittest Change-Id: I355eef6b0f145da8aacdd3b395ce3dcbecfb3e42 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162870 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sc/qa/unit/data/xlsx/tdf76115.xlsx b/sc/qa/unit/data/xlsx/tdf76115.xlsx new file mode 100644 index 000000000000..ebc6126dcbde Binary files /dev/null and b/sc/qa/unit/data/xlsx/tdf76115.xlsx differ diff --git a/sc/qa/unit/subsequent_filters_test2.cxx b/sc/qa/unit/subsequent_filters_test2.cxx index 29eebbececdd..892f5c64446c 100644 --- a/sc/qa/unit/subsequent_filters_test2.cxx +++ b/sc/qa/unit/subsequent_filters_test2.cxx @@ -193,6 +193,7 @@ public: void testTdf151818_SmartArtFontColor(); void testNamedTableRef(); void testTdf131575(); + void testTdf76115(); CPPUNIT_TEST_SUITE(ScFiltersTest2); @@ -315,6 +316,7 @@ public: CPPUNIT_TEST(testTdf151818_SmartArtFontColor); CPPUNIT_TEST(testNamedTableRef); CPPUNIT_TEST(testTdf131575); + CPPUNIT_TEST(testTdf76115); CPPUNIT_TEST_SUITE_END(); }; @@ -3069,6 +3071,20 @@ void ScFiltersTest2::testTdf131575() CPPUNIT_ASSERT_EQUAL(OUString("ETAT DES SORTIES"), pDoc->GetString(1, 0, 0)); } +void ScFiltersTest2::testTdf76115() +{ + // It expectedly fails to load normally + CPPUNIT_ASSERT_ASSERTION_FAIL(createScDoc("xlsx/tdf76115.xlsx")); + + // importing it must succeed with RepairPackage set to true. + uno::Sequence<beans::PropertyValue> aParams + = { comphelper::makePropertyValue("RepairPackage", true) }; + mxComponent = loadFromDesktop(createFileURL(u"xlsx/tdf76115.xlsx"), {}, aParams); + ScDocument* pDoc = getScDoc(); + + CPPUNIT_ASSERT_EQUAL(OUString("Filial"), pDoc->GetString(0, 0, 0)); +} + CPPUNIT_TEST_SUITE_REGISTRATION(ScFiltersTest2); CPPUNIT_PLUGIN_IMPLEMENT(); commit 68da1c50a89cb2f3d8087273725b756a2c575566 Author: Xisco Fauli <xiscofa...@libreoffice.org> AuthorDate: Thu Feb 1 11:22:32 2024 +0100 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 17:40:32 2024 +0600 tdf#131575: sc_subsequent_filters: Add unittest Change-Id: I6f4bcc3da028dc9b2cd86ed06e309bed8d808ca9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162872 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sc/qa/unit/data/xlsx/tdf131575.xlsx b/sc/qa/unit/data/xlsx/tdf131575.xlsx new file mode 100644 index 000000000000..307d7dea5cf8 Binary files /dev/null and b/sc/qa/unit/data/xlsx/tdf131575.xlsx differ diff --git a/sc/qa/unit/subsequent_filters_test2.cxx b/sc/qa/unit/subsequent_filters_test2.cxx index 177fcc773ed7..29eebbececdd 100644 --- a/sc/qa/unit/subsequent_filters_test2.cxx +++ b/sc/qa/unit/subsequent_filters_test2.cxx @@ -56,6 +56,7 @@ #include <com/sun/star/text/WritingMode2.hpp> #include <com/sun/star/text/XTextRange.hpp> +#include <comphelper/propertyvalue.hxx> #include <comphelper/scopeguard.hxx> #include <tools/UnitConversion.hxx> #include <unotools/syslocaleoptions.hxx> @@ -191,6 +192,7 @@ public: void testTdf83671_SmartArt_import2(); void testTdf151818_SmartArtFontColor(); void testNamedTableRef(); + void testTdf131575(); CPPUNIT_TEST_SUITE(ScFiltersTest2); @@ -312,6 +314,7 @@ public: CPPUNIT_TEST(testTdf83671_SmartArt_import2); CPPUNIT_TEST(testTdf151818_SmartArtFontColor); CPPUNIT_TEST(testNamedTableRef); + CPPUNIT_TEST(testTdf131575); CPPUNIT_TEST_SUITE_END(); }; @@ -3052,6 +3055,20 @@ void ScFiltersTest2::testNamedTableRef() } } +void ScFiltersTest2::testTdf131575() +{ + // It expectedly fails to load normally + CPPUNIT_ASSERT_ASSERTION_FAIL(createScDoc("xlsx/tdf131575.xlsx")); + + // importing it must succeed with RepairPackage set to true. + uno::Sequence<beans::PropertyValue> aParams + = { comphelper::makePropertyValue("RepairPackage", true) }; + mxComponent = loadFromDesktop(createFileURL(u"xlsx/tdf131575.xlsx"), {}, aParams); + ScDocument* pDoc = getScDoc(); + + CPPUNIT_ASSERT_EQUAL(OUString("ETAT DES SORTIES"), pDoc->GetString(1, 0, 0)); +} + CPPUNIT_TEST_SUITE_REGISTRATION(ScFiltersTest2); CPPUNIT_PLUGIN_IMPLEMENT(); commit 0224086c0574ea332db1ea6ce5b3455e99e1190c Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Thu Feb 1 20:55:40 2024 +0600 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 17:26:33 2024 +0600 tdf#154587: allow directory entries in ZIP packages The problem in the bugdoc was the directory entries. These entries are valid in ZIP packages (even if not common); they may be useful to e.g. define per-directory permissions (ACLs). In normal mode, ZipFile reads central directory; there we can read if the entry has FAT file attributes; and then, if the entry is a directory. Then it is OK to skip it. In repair mode, central directory is not used, local file headers don't contain a "directory" flag. A workaround is used, checking if there are entries that represent directories of other entries. Also this change fixes some places that didn't pass the recovery flag correctly. Change-Id: I324671841a2c4d0f279b03801d95c8f2eeb99b46 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162888 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx index d4838bb25db6..ddd227340cbf 100644 --- a/oox/source/core/xmlfilterbase.cxx +++ b/oox/source/core/xmlfilterbase.cxx @@ -285,8 +285,10 @@ void XmlFilterBase::importDocumentProperties() rtl::Reference< ::oox::core::FilterDetect > xDetector( new ::oox::core::FilterDetect( xContext ) ); xInputStream = xDetector->extractUnencryptedPackage( aMediaDesc ); Reference< XComponent > xModel = getModel(); + const bool repairPackage = aMediaDesc.getUnpackedValueOrDefault("RepairPackage", false); Reference< XStorage > xDocumentStorage ( - ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( OFOPXML_STORAGE_FORMAT_STRING, xInputStream ) ); + ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( + OFOPXML_STORAGE_FORMAT_STRING, xInputStream, {}, repairPackage)); Reference< XInterface > xTemp = xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.OOXMLDocumentPropertiesImporter", xContext); diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx index d5c6411e48a6..f3e9bb7b2b65 100644 --- a/package/source/zipapi/ZipFile.cxx +++ b/package/source/zipapi/ZipFile.cxx @@ -38,6 +38,7 @@ #include <rtl/digest.h> #include <sal/log.hxx> #include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> #include <osl/diagnose.h> #include <algorithm> @@ -949,7 +950,7 @@ sal_Int32 ZipFile::readCEN() if ( nTestSig != CENSIG ) throw ZipException("Invalid CEN header (bad signature)" ); - aMemGrabber.skipBytes ( 2 ); + sal_uInt16 versionMadeBy = static_cast<sal_uInt16>(aMemGrabber.ReadInt16()); aEntry.nVersion = aMemGrabber.ReadInt16(); aEntry.nFlag = aMemGrabber.ReadInt16(); @@ -969,7 +970,8 @@ sal_Int32 ZipFile::readCEN() aEntry.nPathLen = aMemGrabber.ReadInt16(); aEntry.nExtraLen = aMemGrabber.ReadInt16(); nCommentLen = aMemGrabber.ReadInt16(); - aMemGrabber.skipBytes ( 8 ); + aMemGrabber.skipBytes ( 4 ); + sal_uInt32 externalFileAttributes = aMemGrabber.ReadUInt32(); sal_uInt32 nOffset = aMemGrabber.ReadUInt32(); // FIXME64: need to read the 64bit header instead @@ -1006,6 +1008,15 @@ sal_Int32 ZipFile::readCEN() throw ZipException("Zip entry has an invalid name." ); aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen ); + + // Is this a FAT-compatible empty entry? + if (aEntry.nSize == 0 && (versionMadeBy & 0xff00) == 0) + { + constexpr sal_uInt32 FILE_ATTRIBUTE_DIRECTORY = 16; + if (externalFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; // This is a directory entry, not a stream - skip it + } + aEntries[aEntry.sPath] = aEntry; } @@ -1101,6 +1112,7 @@ void ZipFile::recover() RTL_TEXTENCODING_UTF8 ); aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength()); } + aEntry.sPath = aEntry.sPath.replace('\', '/'); aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen; @@ -1111,7 +1123,36 @@ void ZipFile::recover() aEntry.nSize = 0; } + // Do not add this entry, if it is empty and is a directory of + // an already existing entry + if (aEntry.nSize == 0 && aEntry.nCompressedSize == 0 + && std::find_if( + aEntries.begin(), aEntries.end(), + [path = OUString(aEntry.sPath + "/")](const auto& r) + { return r.first.startsWith(path); }) + != aEntries.end()) + { + nPos += 4; + continue; + } + aEntries.emplace( aEntry.sPath, aEntry ); + + // Drop any "directory" entry corresponding to this one's path; + // since we don't use central directory, we don't see external + // file attributes, so sanitize here + sal_Int32 i = 0; + for (OUString subdir = aEntry.sPath.getToken(0, '/', i); i >= 0; + subdir += OUString::Concat("/") + + o3tl::getToken(aEntry.sPath, 0, '/', i)) + { + if (auto it = aEntries.find(subdir); it != aEntries.end()) + { + // if not empty, let it fail later in ZipPackage::getZipFileContents + if (it->second.nSize == 0 && it->second.nCompressedSize == 0) + aEntries.erase(it); + } + } } } } diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 0a0e1c35e91c..e3b9a1e18fd2 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -1884,15 +1884,21 @@ uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( b try { + const SfxBoolItem* pRepairItem = GetItemSet()->GetItem(SID_REPAIRPACKAGE, false); + const bool bRepairPackage = pRepairItem && pRepairItem->GetValue(); // we can not sign document if there is no stream // should it be possible at all? if ( !bReadOnly && pImpl->xStream.is() ) { - pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream ); + pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( + ZIP_STORAGE_FORMAT_STRING, pImpl->xStream, css::embed::ElementModes::READWRITE, + {}, bRepairPackage); } else if ( pImpl->xInputStream.is() ) { - pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream ); + pImpl->m_xZipStorage + = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( + ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream, {}, bRepairPackage); } } catch( const uno::Exception& ) commit 78063ca395433afbe053d388a2e228e876ccfdfd Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Thu Dec 14 12:56:03 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 15:57:04 2024 +0600 tdf#131575: in repair mode, match names ASCII case-insensitively It would work for the bugdoc in tdf#131575; but not if the wrong case is the only problem in the package, because then there would be no repairment mode active. An alternative could be to use case insensitive match always, but that looks wrong. Change-Id: Ie405d37e1dc639482bd2608e4479de5b707a07d4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160761 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160783 diff --git a/package/source/xstor/xstorage.cxx b/package/source/xstor/xstorage.cxx index 31aa1924048b..651f8b9a1628 100644 --- a/package/source/xstor/xstorage.cxx +++ b/package/source/xstor/xstorage.cxx @@ -404,6 +404,8 @@ void OStorage_Impl::OpenOwnPackage() aArguments.realloc( ++nArgNum ); pArguments = aArguments.getArray(); pArguments[nArgNum-1] <<= aNamedValue; + if (rProp.Name == "RepairPackage") + rProp.Value >>= m_bRepairPackage; } else if ( rProp.Name == "Password" ) { @@ -1265,6 +1267,10 @@ SotElement_Impl* OStorage_Impl::FindElement( const OUString& rName ) ReadContents(); auto mapIt = m_aChildrenMap.find(rName); + if (mapIt == m_aChildrenMap.end() && m_bRepairPackage) + mapIt = std::find_if(m_aChildrenMap.begin(), m_aChildrenMap.end(), + [&rName](const auto& pair) + { return rName.equalsIgnoreAsciiCase(pair.first); }); if (mapIt == m_aChildrenMap.end()) return nullptr; for (auto pElement : mapIt->second) diff --git a/package/source/xstor/xstorage.hxx b/package/source/xstor/xstorage.hxx index 1fefb8f4671a..ce91543d3660 100644 --- a/package/source/xstor/xstorage.hxx +++ b/package/source/xstor/xstorage.hxx @@ -121,6 +121,7 @@ struct OStorage_Impl bool m_bIsRoot; // marks this storage as root storages that manages all commits and reverts bool m_bListCreated; + bool m_bRepairPackage = false; /// Count of registered modification listeners oslInterlockedCount m_nModifiedListenerCount; commit 557aca2460d93ed41ece9b2f786643a57bb9e9ff Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Thu Dec 14 12:54:15 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 15:34:21 2024 +0600 Related: tdf#76115 Also pass RepairPackage to FilterDetect And drop the default argument value from ZipStorage ctor. Always pass it explicitly. Change-Id: I8bcf78dc4db7763567f9d6873841d75c328ede7e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160760 Tested-by: Mike Kaganski <mike.kagan...@collabora.com> Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160782 Tested-by: Jenkins diff --git a/include/oox/helper/zipstorage.hxx b/include/oox/helper/zipstorage.hxx index dabb714d7db8..dd56a1f75a71 100644 --- a/include/oox/helper/zipstorage.hxx +++ b/include/oox/helper/zipstorage.hxx @@ -44,7 +44,7 @@ public: explicit ZipStorage( const css::uno::Reference< css::uno::XComponentContext >& rxContext, const css::uno::Reference< css::io::XInputStream >& rxInStream, - bool bRepairStorage = false ); + bool bRepairStorage ); explicit ZipStorage( const css::uno::Reference< css::uno::XComponentContext >& rxContext, diff --git a/oox/source/core/filterdetect.cxx b/oox/source/core/filterdetect.cxx index db711770832c..7f6140264530 100644 --- a/oox/source/core/filterdetect.cxx +++ b/oox/source/core/filterdetect.cxx @@ -268,9 +268,9 @@ FilterDetect::~FilterDetect() namespace { -bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm ) +bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm, bool bRepairPackage ) { - ZipStorage aZipStorage( rxContext, rxInStrm ); + ZipStorage aZipStorage(rxContext, rxInStrm, bRepairPackage); return aZipStorage.isStorage(); } @@ -315,9 +315,10 @@ comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( co Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDescriptor ) const { + const bool bRepairPackage(rMediaDescriptor.getUnpackedValueOrDefault("RepairPackage", false)); // try the plain input stream Reference<XInputStream> xInputStream( rMediaDescriptor[ MediaDescriptor::PROP_INPUTSTREAM ], UNO_QUERY ); - if( !xInputStream.is() || lclIsZipPackage( mxContext, xInputStream ) ) + if (!xInputStream.is() || lclIsZipPackage(mxContext, xInputStream, bRepairPackage)) return xInputStream; // check if a temporary file is passed in the 'ComponentData' property @@ -325,7 +326,7 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript if( xDecrypted.is() ) { Reference<XInputStream> xDecryptedInputStream = xDecrypted->getInputStream(); - if( lclIsZipPackage( mxContext, xDecryptedInputStream ) ) + if (lclIsZipPackage(mxContext, xDecryptedInputStream, bRepairPackage)) return xDecryptedInputStream; } @@ -379,7 +380,7 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript rMediaDescriptor.setComponentDataEntry( "DecryptedPackage", Any( xTempStream ) ); Reference<XInputStream> xDecryptedInputStream = xTempStream->getInputStream(); - if( lclIsZipPackage( mxContext, xDecryptedInputStream ) ) + if (lclIsZipPackage(mxContext, xDecryptedInputStream, bRepairPackage)) return xDecryptedInputStream; } } diff --git a/oox/source/dump/pptxdumper.cxx b/oox/source/dump/pptxdumper.cxx index c65b792063b2..5a386359d477 100644 --- a/oox/source/dump/pptxdumper.cxx +++ b/oox/source/dump/pptxdumper.cxx @@ -113,7 +113,7 @@ Dumper::Dumper( const Reference< XComponentContext >& rxContext, const Reference { if( rxContext.is() && rxInStrm.is() ) { - StorageRef xStrg = std::make_shared<ZipStorage>( rxContext, rxInStrm ); + StorageRef xStrg = std::make_shared<ZipStorage>( rxContext, rxInStrm, false ); ConfigRef xCfg = std::make_shared<Config>( DUMP_PPTX_CONFIG_ENVVAR, rxContext, xStrg, rSysFileName ); DumperBase::construct( xCfg ); } commit 2224d1bbe3c0d9015e7c5bd102866902d45ea70a Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Thu Dec 14 11:51:42 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 15:08:00 2024 +0600 tdf#76115: pass RepairPackage property from media descriptor to ZipStorage See commit 86c682273d907c77404637c89e584047de1c1099. Change-Id: I51a3beb00f635554ac73cc9ea957e18fb8e84349 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160757 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160781 diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx index c2911a756047..d4838bb25db6 100644 --- a/oox/source/core/xmlfilterbase.cxx +++ b/oox/source/core/xmlfilterbase.cxx @@ -1012,7 +1012,9 @@ bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor ) StorageRef XmlFilterBase::implCreateStorage( const Reference< XInputStream >& rxInStream ) const { - return std::make_shared<ZipStorage>( getComponentContext(), rxInStream ); + return std::make_shared<ZipStorage>( + getComponentContext(), rxInStream, + getMediaDescriptor().getUnpackedValueOrDefault("RepairPackage", false)); } StorageRef XmlFilterBase::implCreateStorage( const Reference< XStream >& rxOutStream ) const commit cdd1c424ed6a6c822467af9c95f3734363215da2 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Thu Feb 1 09:30:50 2024 +0600 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 14:42:24 2024 +0600 tdf#159474: fix handling of pre-set RepairPackage property StorageFilterDetect::detect had a wrong logic here, only honoring repair request in case it haappened interactively inside it. The pre-existing RepairPackage set to true was treated as "do not try to repair". Change-Id: I3fb63a5d72097a79977e8ed734f8e69dd2ea999a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162858 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/filter/source/storagefilterdetect/filterdetect.cxx b/filter/source/storagefilterdetect/filterdetect.cxx index c73355e02a9d..1d0d7557d273 100644 --- a/filter/source/storagefilterdetect/filterdetect.cxx +++ b/filter/source/storagefilterdetect/filterdetect.cxx @@ -121,44 +121,44 @@ OUString SAL_CALL StorageFilterDetect::detect(uno::Sequence<beans::PropertyValue if ( ( aWrap.TargetException >>= aZipException ) && !aRequestedTypeName.isEmpty() ) { // The package is a broken one. - uno::Reference< task::XInteractionHandler > xInteraction = - aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INTERACTIONHANDLER, uno::Reference< task::XInteractionHandler >() ); - - if ( xInteraction.is() ) + INetURLObject aParser( + aMediaDesc.getUnpackedValueOrDefault(MediaDescriptor::PROP_URL, OUString())); + OUString aDocumentTitle = aParser.getName(INetURLObject::LAST_SEGMENT, true, + INetURLObject::DecodeMechanism::WithCharset); + bool bRepairPackage = aMediaDesc.getUnpackedValueOrDefault("RepairPackage", false); + // fdo#46310 Don't ask to repair if the user rejected it once. + if (!bRepairPackage && aMediaDesc.getUnpackedValueOrDefault("RepairAllowed", true)) { - INetURLObject aParser( aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_URL, OUString() ) ); - OUString aDocumentTitle = aParser.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); - bool bRepairPackage = aMediaDesc.getUnpackedValueOrDefault( "RepairPackage", false ); - // fdo#46310 Don't try to repair if the user rejected it once. - bool bRepairAllowed = aMediaDesc.getUnpackedValueOrDefault( "RepairAllowed", true ); + uno::Reference< task::XInteractionHandler > xInteraction = + aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INTERACTIONHANDLER, uno::Reference< task::XInteractionHandler >() ); - if ( !bRepairPackage && bRepairAllowed ) + if ( xInteraction.is() ) { // Ask the user whether he wants to try to repair. - RequestPackageReparation aRequest( aDocumentTitle ); - xInteraction->handle( aRequest.GetRequest() ); - - if ( aRequest.isApproved() ) - { - aTypeName = aRequestedTypeName; - // lok: we want to overwrite file in jail, so don't use template flag - const bool bIsLOK = comphelper::LibreOfficeKit::isActive(); - aMediaDesc[MediaDescriptor::PROP_DOCUMENTTITLE] <<= aDocumentTitle; - aMediaDesc[MediaDescriptor::PROP_ASTEMPLATE] <<= !bIsLOK; - aMediaDesc["RepairPackage"] <<= true; - } - else + RequestPackageReparation aRequest(aDocumentTitle); + xInteraction->handle(aRequest.GetRequest()); + bRepairPackage = aRequest.isApproved(); + if (!bRepairPackage) { // Repair either not allowed or not successful. NotifyBrokenPackage aNotifyRequest( aDocumentTitle ); xInteraction->handle( aNotifyRequest.GetRequest() ); aMediaDesc["RepairAllowed"] <<= false; } - - // Write the changes back. - aMediaDesc >> rDescriptor; } } + if (bRepairPackage) + { + aTypeName = aRequestedTypeName; + // lok: we want to overwrite file in jail, so don't use template flag + const bool bIsLOK = comphelper::LibreOfficeKit::isActive(); + aMediaDesc[MediaDescriptor::PROP_DOCUMENTTITLE] <<= aDocumentTitle; + aMediaDesc[MediaDescriptor::PROP_ASTEMPLATE] <<= !bIsLOK; + aMediaDesc["RepairPackage"] <<= true; + } + + // Write the changes back. + aMediaDesc >> rDescriptor; } } catch( uno::RuntimeException& ) diff --git a/sw/qa/extras/odfimport/data/unreferenced_stream.odt b/sw/qa/extras/odfimport/data/unreferenced_stream.odt new file mode 100644 index 000000000000..0cdba0d48529 Binary files /dev/null and b/sw/qa/extras/odfimport/data/unreferenced_stream.odt differ diff --git a/sw/qa/extras/odfimport/odfimport.cxx b/sw/qa/extras/odfimport/odfimport.cxx index 6370d8dc4e8c..7a0ee371b13d 100644 --- a/sw/qa/extras/odfimport/odfimport.cxx +++ b/sw/qa/extras/odfimport/odfimport.cxx @@ -43,6 +43,7 @@ #include <com/sun/star/style/ParagraphAdjust.hpp> #include <comphelper/propertysequence.hxx> +#include <comphelper/propertyvalue.hxx> #include <editeng/boxitem.hxx> #include <vcl/scheduler.hxx> @@ -1404,5 +1405,20 @@ CPPUNIT_TEST_FIXTURE(Test, testEmptyTrailingSpans) CPPUNIT_ASSERT_DOUBLES_EQUAL(184, height2, 1); // allow a bit of room for rounding just in case } +CPPUNIT_TEST_FIXTURE(Test, testBrokenPackage_Tdf159474) +{ + // Given an invalid ODF having a stream not referenced in manifest.xml + const OUString url = createFileURL(u"unreferenced_stream.odt"); + // It expectedly fails to load normally: + CPPUNIT_ASSERT_ASSERTION_FAIL(loadFromDesktop(url, {}, {})); + // importing it must succeed with RepairPackage set to true. + mxComponent + = loadFromDesktop(url, {}, { comphelper::makePropertyValue("RepairPackage", true) }); + // The document imports in repair mode; the original broken package is used as a template, + // and the loaded document has no URL: + CPPUNIT_ASSERT(uno::Reference<frame::XModel>(mxComponent, uno::UNO_QUERY_THROW)->getURL().isEmpty()); + CPPUNIT_ASSERT_EQUAL(OUString("Empty document"), getParagraph(1)->getString()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit a9091bcfbafa1324bac8771056a42fbe690e0f28 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Tue Nov 28 08:44:23 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 14:12:39 2024 +0600 lok: don't use template flag in more places Similar to commit e2ee3dd61ab8ea5d970d8da5df3233e7bba5909e (lok: add broken package interaction handler, 2023-10-02), all places of broken package handling should allow replacing the original document. Change-Id: I8450dfc0ab60444fa08014cc952ac857998d35ee Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160009 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/filter/source/config/cache/typedetection.cxx b/filter/source/config/cache/typedetection.cxx index dca10ad94cb1..bc0a9af92c83 100644 --- a/filter/source/config/cache/typedetection.cxx +++ b/filter/source/config/cache/typedetection.cxx @@ -40,6 +40,7 @@ #include <comphelper/diagnose_ex.hxx> #include <tools/urlobj.hxx> #include <comphelper/fileurl.hxx> +#include <comphelper/lok.hxx> #include <comphelper/sequence.hxx> #include <utility> @@ -952,8 +953,10 @@ OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& r if (aRequest.isApproved()) { + // lok: we want to overwrite file in jail, so don't use template flag + const bool bIsLOK = comphelper::LibreOfficeKit::isActive(); rDescriptor[utl::MediaDescriptor::PROP_DOCUMENTTITLE] <<= aDocumentTitle; - rDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true; + rDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= !bIsLOK; rDescriptor["RepairPackage"] <<= true; } else diff --git a/filter/source/storagefilterdetect/filterdetect.cxx b/filter/source/storagefilterdetect/filterdetect.cxx index 8312726e11da..c73355e02a9d 100644 --- a/filter/source/storagefilterdetect/filterdetect.cxx +++ b/filter/source/storagefilterdetect/filterdetect.cxx @@ -141,8 +141,10 @@ OUString SAL_CALL StorageFilterDetect::detect(uno::Sequence<beans::PropertyValue if ( aRequest.isApproved() ) { aTypeName = aRequestedTypeName; + // lok: we want to overwrite file in jail, so don't use template flag + const bool bIsLOK = comphelper::LibreOfficeKit::isActive(); aMediaDesc[MediaDescriptor::PROP_DOCUMENTTITLE] <<= aDocumentTitle; - aMediaDesc[MediaDescriptor::PROP_ASTEMPLATE] <<= true; + aMediaDesc[MediaDescriptor::PROP_ASTEMPLATE] <<= !bIsLOK; aMediaDesc["RepairPackage"] <<= true; } else commit dfd7f6dea66b04e65704a96a701ba1ad91af927f Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Mon Nov 27 12:21:30 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 13:41:20 2024 +0600 Related: tdf#96401 Set PROP_ASTEMPLATE for broken ZIP package Same as in StorageFilterDetect::detect. It would prompt user on save to provide a new file name, instead of silently rewriting (possibly recovered with errors) document. Change-Id: I8ede6d01e2d482f409e8b3f7452deb1e4bd02a85 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159985 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/filter/source/config/cache/typedetection.cxx b/filter/source/config/cache/typedetection.cxx index 954a4fd24226..dca10ad94cb1 100644 --- a/filter/source/config/cache/typedetection.cxx +++ b/filter/source/config/cache/typedetection.cxx @@ -951,7 +951,11 @@ OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& r xInteraction->handle(aRequest.GetRequest()); if (aRequest.isApproved()) + { + rDescriptor[utl::MediaDescriptor::PROP_DOCUMENTTITLE] <<= aDocumentTitle; + rDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE] <<= true; rDescriptor["RepairPackage"] <<= true; + } else rDescriptor["RepairAllowed"] <<= false; // Do not ask again } commit 09989e73256d1561d4d38fdd4675f3bfcd4a6c49 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Sun Nov 26 20:18:34 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Fri Feb 2 13:11:30 2024 +0600 tdf#96401: allow to detect a broken ZIP package In deep detection, first check if it's a broken ZIP package. If it is, set the RepairPackage media descriptor property to true. Pass the RepairPackage value to the OOXML filter detection. Change-Id: Ic958283f3cce92ac29ce93ac330cc9e409e3eb78 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159976 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/filter/Library_filterconfig.mk b/filter/Library_filterconfig.mk index 38251aa313f0..7d9318781eb3 100644 --- a/filter/Library_filterconfig.mk +++ b/filter/Library_filterconfig.mk @@ -41,6 +41,7 @@ $(eval $(call gb_Library_use_libraries,filterconfig,\ cppu \ sal \ salhelper \ + sfx \ i18nlangtag \ )) diff --git a/filter/source/config/cache/typedetection.cxx b/filter/source/config/cache/typedetection.cxx index 0aaa2ffa6c3d..954a4fd24226 100644 --- a/filter/source/config/cache/typedetection.cxx +++ b/filter/source/config/cache/typedetection.cxx @@ -25,9 +25,14 @@ #include <com/sun/star/util/URLTransformer.hpp> #include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/StorageFormats.hpp> #include <com/sun/star/io/XInputStream.hpp> #include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/packages/zip/ZipIOException.hpp> #include <com/sun/star/task/XInteractionHandler.hpp> + +#include <sfx2/brokenpackageint.hxx> #include <o3tl/string_view.hxx> #include <tools/wldcrd.hxx> #include <sal/log.hxx> @@ -853,6 +858,50 @@ void TypeDetection::impl_getAllFormatTypes( } +static bool isBrokenZIP(const css::uno::Reference<css::io::XInputStream>& xStream, + const css::uno::Reference<css::uno::XComponentContext>& xContext) +{ + std::vector<css::uno::Any> aArguments{ + css::uno::Any(xStream), + css::uno::Any(css::beans::NamedValue("AllowRemoveOnInsert", css::uno::Any(false))), + css::uno::Any(css::beans::NamedValue("StorageFormat", + css::uno::Any(css::embed::StorageFormats::ZIP))), + }; + try + { + // If this is a broken ZIP package, or not a ZIP, this would throw ZipIOException + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.packages.comp.ZipPackage", comphelper::containerToSequence(aArguments), + xContext); + } + catch (const css::packages::zip::ZipIOException&) + { + // Now test if repair will succeed + aArguments.emplace_back(css::beans::NamedValue("RepairPackage", css::uno::Any(true))); + try + { + // If this is a broken ZIP package that can be repaired, this would succeed, + // and the result will be not empty + if (css::uno::Reference<css::beans::XPropertySet> xPackage{ + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.packages.comp.ZipPackage", + comphelper::containerToSequence(aArguments), xContext), + css::uno::UNO_QUERY }) + if (bool bHasElements; xPackage->getPropertyValue("HasElements") >>= bHasElements) + return bHasElements; + } + catch (const css::uno::Exception&) + { + } + } + catch (const css::uno::Exception&) + { + } + // The package is either not broken, or is not a repairable ZIP + return false; +} + + OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& rDescriptor , const FlatDetection& lFlatTypes , bool bAllowDeep , @@ -862,6 +911,59 @@ OUString TypeDetection::impl_detectTypeFlatAndDeep( utl::MediaDescriptor& r // a set and a not set value. rLastChance.clear(); + // tdf#96401: First of all, check if this is a broken ZIP package. Not doing this here would + // make some filters silently not recognize their content in broken packages, and some filters + // show a warning and mistakenly claim own content based on user choice. + if (bAllowDeep && !rDescriptor.getUnpackedValueOrDefault("RepairPackage", false) + && rDescriptor.getUnpackedValueOrDefault("RepairAllowed", true) + && rDescriptor.find(utl::MediaDescriptor::PROP_INTERACTIONHANDLER) != rDescriptor.end()) + { + try + { + impl_openStream(rDescriptor); + if (auto xStream = rDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_INPUTSTREAM, + css::uno::Reference<css::io::XInputStream>())) + { + css::uno::Reference<css::uno::XComponentContext> xContext; + + // SAFE -> + { + osl::ClearableMutexGuard aLock(m_aMutex); + xContext = m_xContext; + } + // <- SAFE + + if (isBrokenZIP(xStream, xContext)) + { + if (css::uno::Reference<css::task::XInteractionHandler> xInteraction{ + rDescriptor.getValue(utl::MediaDescriptor::PROP_INTERACTIONHANDLER), + css::uno::UNO_QUERY }) + { + INetURLObject aURL(rDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_URL, OUString())); + OUString aDocumentTitle + = aURL.getName(INetURLObject::LAST_SEGMENT, true, + INetURLObject::DecodeMechanism::WithCharset); + + // Ask the user whether they wants to try to repair + RequestPackageReparation aRequest(aDocumentTitle); + xInteraction->handle(aRequest.GetRequest()); + + if (aRequest.isApproved()) + rDescriptor["RepairPackage"] <<= true; + else + rDescriptor["RepairAllowed"] <<= false; // Do not ask again + } + } + } + } + catch (const css::uno::Exception&) + { + // No problem + } + } + // step over all possible types for this URL. // solutions: // a) no types => no detection diff --git a/include/oox/helper/zipstorage.hxx b/include/oox/helper/zipstorage.hxx index dec4b483ea3f..dabb714d7db8 100644 --- a/include/oox/helper/zipstorage.hxx +++ b/include/oox/helper/zipstorage.hxx @@ -43,7 +43,8 @@ class ZipStorage final : public StorageBase public: explicit ZipStorage( const css::uno::Reference< css::uno::XComponentContext >& rxContext, - const css::uno::Reference< css::io::XInputStream >& rxInStream ); + const css::uno::Reference< css::io::XInputStream >& rxInStream, + bool bRepairStorage = false ); explicit ZipStorage( const css::uno::Reference< css::uno::XComponentContext >& rxContext, diff --git a/oox/source/core/filterdetect.cxx b/oox/source/core/filterdetect.cxx index 6f22d612aa13..db711770832c 100644 --- a/oox/source/core/filterdetect.cxx +++ b/oox/source/core/filterdetect.cxx @@ -427,7 +427,8 @@ OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq Reference< XInputStream > xInputStream( extractUnencryptedPackage( aMediaDescriptor ), UNO_SET_THROW ); // stream must be a ZIP package - ZipStorage aZipStorage( mxContext, xInputStream ); + ZipStorage aZipStorage(mxContext, xInputStream, + aMediaDescriptor.getUnpackedValueOrDefault("RepairPackage", false)); if( aZipStorage.isStorage() ) { // create the fast parser, register the XML namespaces, set document handler diff --git a/oox/source/helper/zipstorage.cxx b/oox/source/helper/zipstorage.cxx index 10f7d79c25f1..db73b14bdd6c 100644 --- a/oox/source/helper/zipstorage.cxx +++ b/oox/source/helper/zipstorage.cxx @@ -38,7 +38,7 @@ using namespace ::com::sun::star::io; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::uno; -ZipStorage::ZipStorage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStream ) : +ZipStorage::ZipStorage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStream, bool bRepairStorage ) : StorageBase( rxInStream, false ) { OSL_ENSURE( rxContext.is(), "ZipStorage::ZipStorage - missing component context" ); @@ -61,7 +61,7 @@ ZipStorage::ZipStorage( const Reference< XComponentContext >& rxContext, const R implementation of relations handling. */ mxStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( - ZIP_STORAGE_FORMAT_STRING, rxInStream, rxContext, false); + ZIP_STORAGE_FORMAT_STRING, rxInStream, rxContext, bRepairStorage); } catch (Exception const&) { diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx index 6316498ef0b8..a0e1a4c1b72c 100644 --- a/package/source/zippackage/ZipPackage.cxx +++ b/package/source/zippackage/ZipPackage.cxx @@ -1814,6 +1814,10 @@ Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName ) { return Any(m_bMediaTypeFallbackUsed); } + else if (PropertyName == "HasElements") + { + return Any(m_pZipFile && m_pZipFile->entries().hasMoreElements()); + } throw UnknownPropertyException(PropertyName); } void SAL_CALL ZipPackage::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ ) commit 8925680370ce952c56e4debd23793c0fbf693044 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Sun Nov 26 00:23:39 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Wed Jan 31 17:38:56 2024 +0600 *DocumentLockFile ctor may throw ... e.g., when passed URL is invalid, as seen after opening https://bugs.documentfoundation.org/attachment.cgi?id=121217 using "Office Open XML Text" filter, which sets "RepairPackage" in the media descriptor, which makes the opened file unnamed. Since the code is called from SfxMedium dtor, this crashed on closing this file. Change-Id: I95f6b16f1421abd55e6739ed431878bb72cf3a8e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159964 Tested-by: Mike Kaganski <mike.kagan...@collabora.com> Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 8690da98913e..0a0e1c35e91c 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -3212,23 +3212,21 @@ void SfxMedium::UnlockFile( bool bReleaseLockStream ) if ( !pImpl->m_bLocked ) return; - ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName ); - try { - pImpl->m_bLocked = false; - // TODO/LATER: A warning could be shown in case the file is not the own one - aLockFile.RemoveFile(); - } - catch( const io::WrongFormatException& ) - { + ::svt::DocumentLockFile aLockFile(pImpl->m_aLogicName); + try + { + pImpl->m_bLocked = false; + // TODO/LATER: A warning could be shown in case the file is not the own one + aLockFile.RemoveFile(); + } + catch (const io::WrongFormatException&) { // erase the empty or corrupt file aLockFile.RemoveFileDirectly(); } - catch( const uno::Exception& ) - {} } catch( const uno::Exception& ) {} @@ -3236,23 +3234,21 @@ void SfxMedium::UnlockFile( bool bReleaseLockStream ) if(!pImpl->m_bMSOLockFileCreated) return; - ::svt::MSODocumentLockFile aMSOLockFile( pImpl->m_aLogicName ); - try { - pImpl->m_bLocked = false; - // TODO/LATER: A warning could be shown in case the file is not the own one - aMSOLockFile.RemoveFile(); - } - catch( const io::WrongFormatException& ) - { + ::svt::MSODocumentLockFile aMSOLockFile(pImpl->m_aLogicName); + try + { + pImpl->m_bLocked = false; + // TODO/LATER: A warning could be shown in case the file is not the own one + aMSOLockFile.RemoveFile(); + } + catch (const io::WrongFormatException&) { // erase the empty or corrupt file aMSOLockFile.RemoveFileDirectly(); } - catch( const uno::Exception& ) - {} } catch( const uno::Exception& ) {}