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.stahl@allotropia.de>
(cherry picked from commit 91f35f22f0447769c08ca89e27a39b40df18fffa)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160792
diff --git a/package/source/manifest/ManifestExport.cxx b/package/source/manifest/ManifestExport.cxx
index 15290ba..a15ae01 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 664cfa7..bdcd861 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 884cfa1..459b998 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 bd03967..214d976 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 ));