/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// note: don't check for StorageFormats::ZIP, it breaks signing! void ZipPackage::checkZipEntriesWithDD()
{ if (!m_bForceRecovery)
{
ZipEnumeration entries{m_pZipFile->entries()}; while (entries.hasMoreElements())
{
ZipEntry const& rEntry{*entries.nextElement()}; if ((rEntry.nFlag & 0x08) != 0 && rEntry.nMethod == STORED)
{
uno::Reference<XPropertySet> xStream;
getByHierarchicalName(rEntry.sPath) >>= xStream;
uno::Reference<XServiceInfo> const xStreamSI{xStream, uno::UNO_QUERY_THROW}; if (!xStreamSI->supportsService("com.sun.star.packages.PackageStream"))
{
SAL_INFO("package", "entry STORED with data descriptor is folder: \"" << rEntry.sPath << "\""); throw ZipIOException(
THROW_WHERE "entry STORED with data descriptor is folder");
} if (!xStream->getPropertyValue("WasEncrypted").get<bool>())
{
SAL_INFO("package", "entry STORED with data descriptor but not encrypted: \"" << rEntry.sPath << "\""); throw ZipIOException(
THROW_WHERE "entry STORED with data descriptor but not encrypted");
}
}
}
}
}
void ZipPackage::parseManifest()
{ if ( m_nFormat != embed::StorageFormats::PACKAGE ) return;
// clamp to default SHA256 start key magic value, // c.f. ZipPackageStream::GetEncryptionKey() // trying to get key value from properties const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
pStream->SetImportedAlgorithms({
.nImportedStartKeyAlgorithm = nStartKeyAlg,
.nImportedEncryptionAlgorithm = nEncryptionAlg,
.oImportedChecksumAlgorithm = oDigestAlg, // note m_nCommonEncryptionID is not inited yet here
.nImportedDerivedKeySize = ::GetDefaultDerivedKeySize(nEncryptionAlg),
});
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 ); // also set version explicitly if (oFirstVersion && m_xRootFolder->GetVersion().isEmpty())
{
m_xRootFolder->SetVersion(*oFirstVersion);
} // 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(u"encrypted-package"sv))
{
m_bMediaTypeFallbackUsed = true;
}
}
} elseif ( !m_bForceRecovery )
{ // the mimetype stream should contain the same information as manifest.xml
OUString const mediaTypeXML(m_xRootFolder->hasByName(u"encrypted-package"sv)
? m_xRootFolder->doGetByName(u"encrypted-package").xPackageEntry->GetMediaType()
: m_xRootFolder->GetMediaType()); if (mediaTypeXML != aPackageMediatype)
{ throw ZipIOException(
THROW_WHERE "mimetype conflicts with manifest.xml, \""
+ mediaTypeXML + "\" vs. \""
+ aPackageMediatype + "\"" );
}
}
bool bODF12AndNewer = ( m_xRootFolder->GetVersion().compareTo( ODFVER_012_TEXT ) >= 0 ); if ( !m_bForceRecovery && bODF12AndNewer )
{ if ( m_bInconsistent )
{ // this is an ODF1.2 document that contains streams not referred in the manifest.xml; // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent // should be checked later throw ZipIOException(
THROW_WHERE "there are streams not referred in manifest.xml" );
} // all the streams should be encrypted with the same StartKey in ODF1.2 // TODO/LATER: in future the exception should be thrown // throw ZipIOException( THROW_WHERE "More than one Start Key Generation algorithm is specified!" );
}
// in case it is a correct ODF1.2 document, the version must be set // and the META-INF folder is reserved for package format if ( bODF12AndNewer )
m_xRootFolder->removeByName( sMeta );
}
void ZipPackage::parseContentType()
{ if ( m_nFormat != embed::StorageFormats::OFOPXML ) return;
try { static constexpr std::u16string_view aContentTypes(u"[Content_Types].xml"); // the content type must exist in OFOPXML format! if ( !m_xRootFolder->hasByName( aContentTypes ) ) throw io::IOException(THROW_WHERE "Wrong format!" );
if ( m_bForceRecovery )
{ // the PKZIP Application note version 6.2 does not allows to use '\' as separator // unfortunately it is used by some implementations, so we have to support it in recovery mode
rName = rName.replace( '\\', '/' );
}
// for now the progress handler is not used, probably it will never be // if ( aNamedValue.Name == "ProgressHandler" )
} else
{ // The URL is not acceptable throw css::uno::Exception (THROW_WHERE "Bad arguments.",
getXWeak() );
}
}
try
{ if ( m_xContentStream.is() )
{ // the stream must be seekable, if it is not it will be wrapped
m_xContentStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream, m_xContext );
m_xContentSeek.set( m_xContentStream, UNO_QUERY_THROW ); if ( !m_xContentSeek->getLength() )
bHaveZipFile = false;
} else
bHaveZipFile = false;
} catch ( css::uno::Exception& )
{ // Exception derived from uno::Exception thrown. This probably // means the file doesn't exist...we'll create it at // commitChanges time
bHaveZipFile = false;
} if ( !bHaveZipFile ) return;
if (aName == "/") // root directory. return Any ( uno::Reference( cppu::getXWeak(m_xRootFolder.get()) ) );
sal_Int32 nIndex = aName.getLength();
nStreamIndex = aName.lastIndexOf ( '/' ); bool bFolder = nStreamIndex == nIndex-1; // last character is '/'.
if ( nStreamIndex != -1 )
{ // The name contains '/'.
sDirName = aName.copy ( 0, nStreamIndex );
FolderHash::iterator aIter = m_aRecent.find ( sDirName ); if ( aIter != m_aRecent.end() )
{ // There is a cached entry for this name.
// the manifest.xml is never encrypted - so pass an empty reference
ZipOutputStream::setEntry(*pEntry); auto p = pEntry.get();
aZipOut.writeLOC(std::move(pEntry));
ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
aZipEntry.write(pBuffer->getSequence());
aZipEntry.closeEntry();
aZipOut.rawCloseEntry();
}
// Add default entries, the count must be updated manually when appending. // Add at least the standard default entries.
uno::Sequence< beans::StringPair > aDefaultsSequence
{
{ u"xml"_ustr, u"application/xml"_ustr },
{ u"rels"_ustr, u"application/vnd.openxmlformats-package.relationships+xml"_ustr },
{ u"png"_ustr, u"image/png"_ustr },
{ u"jpeg"_ustr, u"image/jpeg"_ustr },
{ u"fntdata"_ustr, u"application/x-fontdata"_ustr }
};
uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size()); auto aOverridesSequenceRange = asNonConstRange(aOverridesSequence);
sal_Int32 nOverSeqLength = 0; for (constauto& rMan : aManList)
{
OUString aType;
OSL_ENSURE( rMan[PKG_MNFST_MEDIATYPE].Name == "MediaType" && rMan[PKG_MNFST_FULLPATH].Name == "FullPath", "The mediatype sequence format is wrong!" );
rMan[PKG_MNFST_MEDIATYPE].Value >>= aType; if ( !aType.isEmpty() )
{
OUString aPath; // only nonempty type makes sense here
rMan[PKG_MNFST_FULLPATH].Value >>= aPath; //FIXME: For now we have no way of differentiating defaults from others.
aOverridesSequenceRange[nOverSeqLength].First = "/" + aPath;
aOverridesSequenceRange[nOverSeqLength].Second = aType;
++nOverSeqLength;
}
}
aOverridesSequence.realloc(nOverSeqLength);
// there is no encryption in this format currently
ZipOutputStream::setEntry(*pEntry); auto p = pEntry.get();
aZipOut.writeLOC(std::move(pEntry));
ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
aZipEntry.write(pBuffer->getSequence());
aZipEntry.closeEntry();
aZipOut.rawCloseEntry();
}
// seek back to the beginning of the temp file so we can read segments from it
m_xContentSeek->seek( 0 ); if ( m_pZipFile )
m_pZipFile->setInputStream( m_xContentStream ); else
m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, false, false,
m_nFormat == embed::StorageFormats::ZIP
? ZipFile::Checks::Default
: m_nFormat == embed::StorageFormats::OFOPXML
? ZipFile::Checks::CheckInsensitive
: ZipFile::Checks::TryCheckInsensitive);
}
uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
{ // In case the target local file does not exist or empty // write directly to it otherwise create a temporary file to write to. // If a temporary file is created it is returned back by the method. // If the data written directly, xComponentStream will be switched here
// Hand it to the ZipOutputStream:
ZipOutputStream aZipOut( xTempOut ); try
{ if ( m_nFormat == embed::StorageFormats::PACKAGE )
{ // Remove the old manifest.xml file as the // manifest will be re-generated and the // META-INF directory implicitly created if does not exist static constexpr std::u16string_view sMeta (u"META-INF");
// Write a magic file with mimetype
WriteMimetypeMagicFile( aZipOut );
} elseif ( m_nFormat == embed::StorageFormats::OFOPXML )
{ // Remove the old [Content_Types].xml file as the // file will be re-generated
if (!bIsGpgEncrypt)
{ if (m_nKeyDerivationFunctionID == xml::crypto::KDFID::PBKDF2)
{ // if there is only one KDF invocation, increase the safety margin
oPBKDF2IterationCount.emplace(m_xRootFolder->hasByName(u"encrypted-package"sv) ? 600000 : 100000);
} else
{
assert(m_nKeyDerivationFunctionID == xml::crypto::KDFID::Argon2id);
oArgon2Args.emplace(3, (1<<16), 4);
}
}
// call saveContents - it will recursively save sub-directories
m_xRootFolder->saveContents(u""_ustr, aManList, aZipOut, GetEncryptionKey(),
oPBKDF2IterationCount, oArgon2Args);
}
// Update our References to point to the new temp file if( !bUseTemp )
{ // the case when the original contents were written directly
xTempOut->flush();
// in case the stream is based on a file it will implement the following interface // the call should be used to be sure that the contents are written to the file system
uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( xTempOut, uno::UNO_QUERY ); if (asyncOutputMonitor.is() && !m_bDisableFileSync)
asyncOutputMonitor->waitForCompletion();
// no need to postpone switching to the new stream since the target was written directly
uno::Reference< io::XInputStream > xNewStream; if ( m_eMode == e_IMode_URL )
xNewStream = xSink->getStream()->getInputStream(); elseif ( m_eMode == e_IMode_XStream && m_xStream.is() )
xNewStream = m_xStream->getInputStream();
if ( xNewStream.is() )
ConnectTo( xNewStream );
}
} catch ( uno::Exception& )
{ if( bUseTemp )
{ // no information loss appears, thus no special handling is required
uno::Any aCaught( ::cppu::getCaughtException() );
// it is allowed to throw WrappedTargetException
WrappedTargetException aException; if ( aCaught >>= aException ) throw aException;
throw WrappedTargetException(
THROW_WHERE "Problem writing the original content!",
getXWeak(),
aCaught );
} else
{ // the document is written directly, although it was empty it is important to notify that the writing has failed // TODO/LATER: let the package be able to recover in this situation
OUString aErrTxt(THROW_WHERE "This package is unusable!");
embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), OUString() ); throw WrappedTargetException( aErrTxt,
getXWeak(),
Any ( aException ) );
}
}
return xResult;
}
uno::Reference< XActiveDataStreamer > ZipPackage::openOriginalForOutput()
{ // open and truncate the original file
Content aOriginalContent(
m_aURL, uno::Reference< XCommandEnvironment >(),
m_xContext );
uno::Reference< XActiveDataStreamer > xSink = new ActiveDataStreamer;
aOriginalContent.executeCommand(u"open"_ustr, Any( aArg ) );
} catch( Exception& )
{ // seems to be nonlocal file // temporary file mechanics should be used
}
}
return xSink;
}
void SAL_CALL ZipPackage::commitChanges()
{ // lock the component for the time of committing
::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
if ( m_eMode == e_IMode_XInputStream )
{
IOException aException; throw WrappedTargetException(THROW_WHERE "This package is read only!",
getXWeak(), Any ( aException ) );
} // first the writeTempFile is called, if it returns a stream the stream should be written to the target // if no stream was returned, the file was written directly, nothing should be done
uno::Reference< io::XInputStream > xTempInStream; try
{
xTempInStream = writeTempFile();
} catch (const ucb::ContentCreationException&)
{
css::uno::Any anyEx = cppu::getCaughtException(); throw WrappedTargetException(THROW_WHERE "Temporary file should be creatable!",
getXWeak(), anyEx );
} if ( xTempInStream.is() )
{
uno::Reference< io::XSeekable > xTempSeek( xTempInStream, uno::UNO_QUERY_THROW );
try
{ // connect to the temporary stream
ConnectTo( xTempInStream );
} catch( const io::IOException& )
{
css::uno::Any anyEx = cppu::getCaughtException(); throw WrappedTargetException(THROW_WHERE "Temporary file should be connectable!",
getXWeak(), anyEx );
}
if ( m_eMode == e_IMode_XStream )
{ // First truncate our output stream
uno::Reference < XOutputStream > xOutputStream;
// preparation for copy step try
{
xOutputStream = m_xStream->getOutputStream();
// Make sure we avoid a situation where the current position is // not zero, but the underlying file is truncated in the // meantime.
uno::Reference<io::XSeekable> xSeekable(xOutputStream, uno::UNO_QUERY); if (xSeekable.is())
xSeekable->seek(0);
// after successful truncation the original file contents are already lost
xTruncate->truncate();
} catch( const uno::Exception& )
{
css::uno::Any anyEx = cppu::getCaughtException(); throw WrappedTargetException(THROW_WHERE "This package is read only!",
getXWeak(), anyEx );
}
try
{ // then copy the contents of the tempfile to our output stream
::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutputStream );
xOutputStream->flush();
uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor(
xOutputStream, uno::UNO_QUERY ); if ( asyncOutputMonitor.is() ) {
asyncOutputMonitor->waitForCompletion();
}
} catch( uno::Exception& )
{ // if anything goes wrong in this block the target file becomes corrupted // so an exception should be thrown as a notification about it // and the package must disconnect from the stream
DisconnectFromTargetAndThrowException_Impl( xTempInStream );
}
} elseif ( m_eMode == e_IMode_URL )
{
uno::Reference< XOutputStream > aOrigFileStream; bool bCanBeCorrupted = false;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.32 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.