oox/source/core/xmlfilterbase.cxx |    4 ++-
 package/source/zipapi/ZipFile.cxx |   42 ++++++++++++++++++++++++++++++++++++--
 sfx2/source/doc/docfile.cxx       |   15 ++++++++++---
 3 files changed, 55 insertions(+), 6 deletions(-)

New commits:
commit 747463809e50c132557a95dcee6709a1fa82d760
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Thu Feb 1 20:55:40 2024 +0600
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Thu Feb 1 21:07:54 2024 +0100

    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 7afb84cad2c6..f6bda14e920a 100644
--- a/oox/source/core/xmlfilterbase.cxx
+++ b/oox/source/core/xmlfilterbase.cxx
@@ -288,8 +288,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 474b73ff53db..71fd66f08196 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>
@@ -1073,7 +1074,7 @@ sal_Int32 ZipFile::readCEN()
             if ( nTestSig != CENSIG )
                 throw ZipException("Invalid CEN header (bad signature)" );
 
-            aMemGrabber.skipBytes ( 2 );
+            sal_uInt16 versionMadeBy = aMemGrabber.ReadUInt16();
             aEntry.nVersion = aMemGrabber.ReadInt16();
             aEntry.nFlag = aMemGrabber.ReadInt16();
 
@@ -1093,7 +1094,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_uInt64 nOffset = aMemGrabber.ReadUInt32();
 
             if ( aEntry.nPathLen < 0 )
@@ -1132,6 +1134,15 @@ sal_Int32 ZipFile::readCEN()
                 throw ZipException("Integer-overflow");
 
             aMemGrabber.skipBytes(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;
         }
 
@@ -1253,6 +1264,7 @@ void ZipFile::recover()
                                                               
RTL_TEXTENCODING_UTF8 );
                                     aEntry.nPathLen = static_cast< sal_Int16 
>(aFileName.getLength());
                                 }
+                                aEntry.sPath = aEntry.sPath.replace('\', '/');
 
                                 // read 64bit header
                                 if (aEntry.nExtraLen > 0)
@@ -1294,7 +1306,33 @@ 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())
+                                        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 6fee3f23f178..670a27841414 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -1979,10 +1979,13 @@ uno::Reference<embed::XStorage> 
SfxMedium::GetScriptingStorageToSign_Impl()
             SAL_WARN_IF(!pImpl->m_xODFDecryptedInnerPackageStream.is(), 
"sfx.doc", "no inner package stream?");
             if (pImpl->m_xODFDecryptedInnerPackageStream.is())
             {
+                const SfxBoolItem* pRepairItem = 
GetItemSet().GetItem(SID_REPAIRPACKAGE, false);
+                const bool bRepairPackage = pRepairItem && 
pRepairItem->GetValue();
                 pImpl->m_xODFDecryptedInnerZipStorage =
                     
::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
                         ZIP_STORAGE_FORMAT_STRING,
-                        
pImpl->m_xODFDecryptedInnerPackageStream->getInputStream());
+                        
pImpl->m_xODFDecryptedInnerPackageStream->getInputStream(), {},
+                        bRepairPackage);
             }
         }
         return pImpl->m_xODFDecryptedInnerZipStorage;
@@ -2003,15 +2006,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& )

Reply via email to