package/source/manifest/ManifestExport.cxx |   33 ++++++++++++++++++++++++++---
 package/source/zipapi/ZipFile.cxx          |    3 +-
 package/source/zippackage/ZipPackage.cxx   |   23 +++++++++++++++-----
 svx/source/xml/xmlxtexp.cxx                |    6 +++++
 4 files changed, 56 insertions(+), 9 deletions(-)

New commits:
commit 91f35f22f0447769c08ca89e27a39b40df18fffa
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Wed Dec 13 21:53:55 2023 +0100
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Thu Dec 14 15:38:13 2023 +0100

    tdf#105844 package: remove root document from manifest ...
    
    ... for ODF wholesome encryption.
    
      4.3  <manifest:file-entry>
      For directories, the manifest file should contain a <manifest:file-entry> 
element only if a directory contains a document or a sub document.
    
    Because the "encrypted-package" is not a document but a package, we
    should probably omit the file-entry for the root document.
    
    ZipPackage::writeTempFile() always generates the root document becuase
    it's needed for GPG properties, and ManifestExport filters it out.
    
    A bit tricky to implement, because there isn't a clean distinction
    between the package and the root document/storage in the package module,
    in particular there's no other place than the root storage to store the
    MediaType property.
    
    Change-Id: Id7e72a64e2faa074dce80cd5fefb2fa189e2e3ee
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160717
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/package/source/manifest/ManifestExport.cxx 
b/package/source/manifest/ManifestExport.cxx
index 950e47f49741..cf60614ccd58 100644
--- a/package/source/manifest/ManifestExport.cxx
+++ b/package/source/manifest/ManifestExport.cxx
@@ -81,6 +81,7 @@ ManifestExport::ManifestExport( uno::Reference< 
xml::sax::XDocumentHandler > con
     // find the mediatype of the document if any
     OUString aDocMediaType;
     OUString aDocVersion;
+    bool isWholesomeEncryption(false);
     const uno::Sequence<beans::PropertyValue>* pRootFolderPropSeq = nullptr;
     for (const uno::Sequence < beans::PropertyValue >& rSequence : rManList)
     {
@@ -109,12 +110,27 @@ ManifestExport::ManifestExport( uno::Reference< 
xml::sax::XDocumentHandler > con
 
         if ( aPath == "/" )
         {
+            assert(aDocMediaType.isEmpty());
+            // unfortunately no aMediaType in some cases where non-documents
+            // are stored as StorageFormats::PACKAGE instead of sensible
+            // StorageFormats::ZIP, such as SvxXMLXTableExportComponent and
+            // SwXMLTextBlocks, which results in an empty "mimetype" etc but
+            // can't be easily fixed; try to exclude these cases by checking
+            // for aVersion, but of course then forgetting to set both version
+            // and type on an actual document can't be found :(
+            assert(!aMediaType.isEmpty() || aVersion.isEmpty());
             aDocMediaType = aMediaType;
             aDocVersion = aVersion;
             pRootFolderPropSeq = &rSequence;
-            break;
+        }
+
+        if (aPath == "encrypted-package")
+        {
+            isWholesomeEncryption = true;
+            assert(aDocMediaType.isEmpty() || aDocMediaType == aMediaType);
         }
     }
+    assert(pRootFolderPropSeq);
 
     bool bProvideDTD = false;
     bool bAcceptNonemptyVersion = false;
@@ -293,7 +309,12 @@ ManifestExport::ManifestExport( uno::Reference< 
xml::sax::XDocumentHandler > con
     // now write individual file entries
     for (const uno::Sequence<beans::PropertyValue>& rSequence : rManList)
     {
+        if (&rSequence == pRootFolderPropSeq && isWholesomeEncryption)
+        {
+            continue; // no root document, but embedded package => omit
+        }
         rtl::Reference<::comphelper::AttributeList> pAttrList = new 
::comphelper::AttributeList;
+        OUString fullPath;
         OUString aString;
         const uno::Any *pVector = nullptr, *pSalt = nullptr, *pIterationCount 
= nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptAlg = nullptr, 
*pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
         for (const beans::PropertyValue& rValue : rSequence)
@@ -312,8 +333,8 @@ ManifestExport::ManifestExport( uno::Reference< 
xml::sax::XDocumentHandler > con
             }
             else if (rValue.Name == sFullPathProperty )
             {
-                rValue.Value >>= aString;
-                pAttrList->AddAttribute ( ATTRIBUTE_FULL_PATH, aString );
+                rValue.Value >>= fullPath;
+                pAttrList->AddAttribute(ATTRIBUTE_FULL_PATH, fullPath);
             }
             else if (rValue.Name == sSizeProperty )
             {
@@ -338,6 +359,11 @@ ManifestExport::ManifestExport( uno::Reference< 
xml::sax::XDocumentHandler > con
             else if (rValue.Name == sDerivedKeySizeProperty )
                 pDerivedKeySize = &rValue.Value;
         }
+        assert(!fullPath.isEmpty());
+        if (isWholesomeEncryption)
+        {   // there may be signatures in META-INF too
+            assert(fullPath == "encrypted-package" || 
fullPath.startsWith("META-INF/"));
+        }
 
         xHandler->ignorableWhitespace ( sWhiteSpace );
         xHandler->startElement( ELEMENT_FILE_ENTRY , pAttrList);
@@ -390,6 +416,7 @@ ManifestExport::ManifestExport( uno::Reference< 
xml::sax::XDocumentHandler > con
             }
             else if (nEncAlgID == xml::crypto::CipherID::AES_GCM_W3C)
             {
+                assert(bStoreStartKeyGeneration || pKeyInfoProperty);
                 SAL_WARN_IF(nDerivedKeySize != 32, "package.manifest", 
"Unexpected key size is provided!");
                 if (nDerivedKeySize != 32)
                 {
diff --git a/package/source/zipapi/ZipFile.cxx 
b/package/source/zipapi/ZipFile.cxx
index 664cfa7f1e17..bdcd8610be60 100644
--- a/package/source/zipapi/ZipFile.cxx
+++ b/package/source/zipapi/ZipFile.cxx
@@ -647,7 +647,8 @@ uno::Reference< XInputStream > 
ZipFile::createStreamForZipEntry(
 #ifndef EMSCRIPTEN
     static const sal_Int32 nThreadingThreshold = 10000;
 
-    if( xSrcStream->available() > nThreadingThreshold )
+    // "encrypted-package" is the only data stream, no point in threading it
+    if (rEntry.sPath != "encrypted-package" && nThreadingThreshold < 
xSrcStream->available())
         xBufStream = new XBufferedThreadedStream(xSrcStream, 
xSrcStream->getSize());
     else
 #endif
diff --git a/package/source/zippackage/ZipPackage.cxx 
b/package/source/zippackage/ZipPackage.cxx
index 1bae902c177a..f95731f368d1 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -417,25 +417,36 @@ void ZipPackage::parseManifest()
             }
         }
 
-        if ( !bManifestParsed )
+        if (!bManifestParsed || m_xRootFolder->GetMediaType().isEmpty())
         {
             // the manifest.xml could not be successfully parsed, this is an 
inconsistent package
             if ( aPackageMediatype.startsWith("application/vnd.") )
             {
                 // accept only types that look similar to own mediatypes
                 m_xRootFolder->SetMediaType( aPackageMediatype );
-                m_bMediaTypeFallbackUsed = true;
+                // if there is an encrypted inner package, there is no root
+                // document, because instead there is a package, and it is not
+                // an error
+                if (!m_xRootFolder->hasByName("encrypted-package"))
+                {
+                    m_bMediaTypeFallbackUsed = true;
+                }
             }
         }
         else if ( !m_bForceRecovery )
         {
-            // the mimetype stream should contain the information from 
manifest.xml
-            if ( m_xRootFolder->GetMediaType() != aPackageMediatype )
+            // the mimetype stream should contain the same information as 
manifest.xml
+            OUString const 
mediaTypeXML(m_xRootFolder->hasByName("encrypted-package")
+                ? 
m_xRootFolder->doGetByName("encrypted-package").xPackageEntry->GetMediaType()
+                : m_xRootFolder->GetMediaType());
+            if (mediaTypeXML != aPackageMediatype)
+            {
                 throw ZipIOException(
                     THROW_WHERE
                     "mimetype conflicts with manifest.xml, \""
-                    + m_xRootFolder->GetMediaType() + "\" vs. \""
+                    + mediaTypeXML + "\" vs. \""
                     + aPackageMediatype + "\"" );
+            }
         }
 
         m_xRootFolder->removeByName( sMimetype );
@@ -1269,6 +1280,8 @@ uno::Reference< io::XInputStream > 
ZipPackage::writeTempFile()
         static constexpr OUStringLiteral sFullPath(u"FullPath");
         const bool bIsGpgEncrypt = m_aGpgProps.hasElements();
 
+        // note: this is always created here (needed for GPG), possibly
+        // filtered out later in ManifestExport
         if ( m_nFormat == embed::StorageFormats::PACKAGE )
         {
             uno::Sequence < PropertyValue > aPropSeq(
diff --git a/svx/source/xml/xmlxtexp.cxx b/svx/source/xml/xmlxtexp.cxx
index bd03967bd832..214d976b7d71 100644
--- a/svx/source/xml/xmlxtexp.cxx
+++ b/svx/source/xml/xmlxtexp.cxx
@@ -227,7 +227,13 @@ bool SvxXMLXTableExportComponent::save(
         if( !bToStorage || !xStorage.is() )
         { // local URL -> SfxMedium route
             if( bSaveAsStorage )
+            {
+                // ideally this should use a ZIP_STORAGE_FORMAT_STRING storage
+                // but changing it to that could cause problems loading the
+                // file with an old version of LO that expects to find in the
+                // user profile a PACKAGE_STORAGE_FORMAT_STRING storage
                 xSubStorage = ::comphelper::OStorageHelper::GetStorageFromURL( 
rURL, eCreate );
+            }
             else
             {
                 pMedium.reset(new SfxMedium( rURL, StreamMode::WRITE | 
StreamMode::TRUNC ));

Reply via email to