summaryrefslogtreecommitdiffstats
path: root/package/source/zippackage/ZipPackageFolder.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--package/source/zippackage/ZipPackageFolder.cxx424
1 files changed, 424 insertions, 0 deletions
diff --git a/package/source/zippackage/ZipPackageFolder.cxx b/package/source/zippackage/ZipPackageFolder.cxx
new file mode 100644
index 000000000..7e7168805
--- /dev/null
+++ b/package/source/zippackage/ZipPackageFolder.cxx
@@ -0,0 +1,424 @@
+/* -*- 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 .
+ */
+
+#include <ZipPackageFolder.hxx>
+#include <ZipOutputStream.hxx>
+#include <ZipPackageStream.hxx>
+#include <PackageConstants.hxx>
+#include "ZipPackageFolderEnumeration.hxx"
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/packages/zip/ZipConstants.hpp>
+#include <com/sun/star/packages/zip/ZipException.hpp>
+#include <com/sun/star/embed/StorageFormats.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+using namespace com::sun::star;
+using namespace com::sun::star::packages::zip::ZipConstants;
+using namespace com::sun::star::packages::zip;
+using namespace com::sun::star::packages;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::io;
+using namespace cppu;
+
+#if OSL_DEBUG_LEVEL > 0
+#define THROW_WHERE SAL_WHERE
+#else
+#define THROW_WHERE ""
+#endif
+
+ZipPackageFolder::ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext,
+ sal_Int32 nFormat,
+ bool bAllowRemoveOnInsert )
+{
+ m_xContext = xContext;
+ m_nFormat = nFormat;
+ mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
+ SetFolder ( true );
+ aEntry.nVersion = -1;
+ aEntry.nFlag = 0;
+ aEntry.nMethod = STORED;
+ aEntry.nTime = -1;
+ aEntry.nCrc = 0;
+ aEntry.nCompressedSize = 0;
+ aEntry.nSize = 0;
+ aEntry.nOffset = -1;
+}
+
+ZipPackageFolder::~ZipPackageFolder()
+{
+}
+
+bool ZipPackageFolder::LookForUnexpectedODF12Streams( std::u16string_view aPath )
+{
+ bool bHasUnexpected = false;
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( rInfo.bFolder )
+ {
+ if ( aPath == u"META-INF/" )
+ {
+ // META-INF is not allowed to contain subfolders
+ bHasUnexpected = true;
+ }
+ else
+ {
+ OUString sOwnPath = aPath + rShortName + "/";
+ bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams( sOwnPath );
+ }
+ }
+ else
+ {
+ if ( aPath == u"META-INF/" )
+ {
+ if ( rShortName != "manifest.xml"
+ && rShortName.indexOf( "signatures" ) == -1 )
+ {
+ // a stream from META-INF with unexpected name
+ bHasUnexpected = true;
+ }
+
+ // streams from META-INF with expected names are allowed not to be registered in manifest.xml
+ }
+ else if ( !rInfo.pStream->IsFromManifest() )
+ {
+ // the stream is not in META-INF and is not registered in manifest.xml,
+ // check whether it is an internal part of the package format
+ if ( !aPath.empty() || rShortName != "mimetype" )
+ {
+ // if it is not "mimetype" from the root it is not a part of the package
+ bHasUnexpected = true;
+ }
+ }
+ }
+
+ if (bHasUnexpected)
+ break;
+ }
+
+ return bHasUnexpected;
+}
+
+void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair )
+{
+ OUString aExt;
+ if ( aPair.First.toChar() == '.' )
+ aExt = aPair.First;
+ else
+ aExt = "." + aPair.First;
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( rInfo.bFolder )
+ rInfo.pFolder->setChildStreamsTypeByExtension( aPair );
+ else
+ {
+ sal_Int32 nPathLength = rShortName.getLength();
+ sal_Int32 nExtLength = aExt.getLength();
+ if ( nPathLength >= nExtLength && rShortName.match( aExt, nPathLength - nExtLength ) )
+ rInfo.pStream->SetMediaType( aPair.Second );
+ }
+ }
+}
+
+const css::uno::Sequence < sal_Int8 > & ZipPackageFolder::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit lcl_CachedImplId;
+ return lcl_CachedImplId.getSeq();
+}
+
+ // XNameContainer
+void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ if (hasByName(aName))
+ throw ElementExistException(THROW_WHERE );
+
+ uno::Reference < XUnoTunnel > xRef;
+ aElement >>= xRef;
+ if ( !(aElement >>= xRef) )
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ ZipPackageEntry* pEntry = comphelper::getFromUnoTunnel<ZipPackageFolder>(xRef);
+ if (!pEntry)
+ pEntry = comphelper::getFromUnoTunnel<ZipPackageStream>(xRef);
+ if (!pEntry)
+ throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
+
+ if (pEntry->getName() != aName )
+ pEntry->setName (aName);
+ doInsertByName ( pEntry, true );
+}
+
+void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name )
+{
+ ContentHash::iterator aIter = maContents.find ( Name );
+ if ( aIter == maContents.end() )
+ throw NoSuchElementException(THROW_WHERE );
+ maContents.erase( aIter );
+}
+ // XEnumerationAccess
+uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( )
+{
+ return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents));
+}
+ // XElementAccess
+uno::Type SAL_CALL ZipPackageFolder::getElementType( )
+{
+ return cppu::UnoType<XUnoTunnel>::get();
+}
+sal_Bool SAL_CALL ZipPackageFolder::hasElements( )
+{
+ return !maContents.empty();
+}
+ // XNameAccess
+ZipContentInfo& ZipPackageFolder::doGetByName( const OUString& aName )
+{
+ ContentHash::iterator aIter = maContents.find ( aName );
+ if ( aIter == maContents.end())
+ throw NoSuchElementException(THROW_WHERE );
+ return aIter->second;
+}
+
+uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName )
+{
+ return uno::Any ( doGetByName ( aName ).xTunnel );
+}
+uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( )
+{
+ return comphelper::mapKeysToSequence(maContents);
+}
+sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName )
+{
+ return maContents.find ( aName ) != maContents.end ();
+}
+ // XNameReplace
+void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ if ( !hasByName( aName ) )
+ throw NoSuchElementException(THROW_WHERE );
+
+ removeByName( aName );
+ insertByName(aName, aElement);
+}
+
+bool ZipPackageFolder::saveChild(
+ const OUString &rPath,
+ std::vector < uno::Sequence < PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
+ const rtlRandomPool &rRandomPool)
+{
+ uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
+ OUString sTempName = rPath + "/";
+
+ if ( !GetMediaType().isEmpty() )
+ {
+ auto pPropSet = aPropSet.getArray();
+ pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType";
+ pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType();
+ pPropSet[PKG_MNFST_VERSION].Name = "Version";
+ pPropSet[PKG_MNFST_VERSION].Value <<= GetVersion();
+ pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath";
+ pPropSet[PKG_MNFST_FULLPATH].Value <<= sTempName;
+ }
+ else
+ aPropSet.realloc( 0 );
+
+ saveContents( sTempName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool);
+
+ // folder can have a mediatype only in package format
+ if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
+ rManList.push_back( aPropSet );
+
+ return true;
+}
+
+void ZipPackageFolder::saveContents(
+ const OUString &rPath,
+ std::vector < uno::Sequence < PropertyValue > > &rManList,
+ ZipOutputStream & rZipOut,
+ const uno::Sequence < sal_Int8 >& rEncryptionKey,
+ sal_Int32 nPBKDF2IterationCount,
+ const rtlRandomPool &rRandomPool ) const
+{
+ if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
+ {
+ // it is an empty subfolder, use workaround to store it
+ ZipEntry* pTempEntry = new ZipEntry(aEntry);
+ pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getLength() );
+ pTempEntry->nExtraLen = -1;
+ pTempEntry->sPath = rPath;
+
+ try
+ {
+ ZipOutputStream::setEntry(pTempEntry);
+ rZipOut.writeLOC(pTempEntry);
+ rZipOut.rawCloseEntry();
+ }
+ catch ( ZipException& )
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ catch ( IOException& )
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+
+ bool bMimeTypeStreamStored = false;
+ OUString aMimeTypeStreamName("mimetype");
+ if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() )
+ {
+ // let the "mimetype" stream in root folder be stored as the first stream if it is zip format
+ ContentHash::const_iterator aIter = maContents.find ( aMimeTypeStreamName );
+ if ( aIter != maContents.end() && !(*aIter).second.bFolder )
+ {
+ bMimeTypeStreamStored = true;
+ if( !aIter->second.pStream->saveChild(
+ rPath + aIter->first, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ }
+
+ for (const auto& [rShortName, rInfo] : maContents)
+ {
+ if ( !bMimeTypeStreamStored || rShortName != aMimeTypeStreamName )
+ {
+ if (rInfo.bFolder)
+ {
+ if( !rInfo.pFolder->saveChild(
+ rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ else
+ {
+ if( !rInfo.pStream->saveChild(
+ rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
+ {
+ throw uno::RuntimeException( THROW_WHERE );
+ }
+ }
+ }
+ }
+}
+
+sal_Int64 SAL_CALL ZipPackageFolder::getSomething( const uno::Sequence< sal_Int8 >& aIdentifier )
+{
+ return comphelper::getSomethingImpl(aIdentifier, this);
+}
+void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ if ( aPropertyName == "MediaType" )
+ {
+ // TODO/LATER: activate when zip ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ aValue >>= msMediaType;
+ }
+ else if ( aPropertyName == "Version" )
+ aValue >>= m_sVersion;
+ else if ( aPropertyName == "Size" )
+ aValue >>= aEntry.nSize;
+ else
+ throw UnknownPropertyException(aPropertyName);
+}
+uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName == "MediaType" )
+ {
+ // TODO/LATER: activate when zip ucp is ready
+ // if ( m_nFormat != embed::StorageFormats::PACKAGE )
+ // throw UnknownPropertyException(THROW_WHERE );
+
+ return uno::Any ( msMediaType );
+ }
+ else if ( PropertyName == "Version" )
+ return uno::Any( m_sVersion );
+ else if ( PropertyName == "Size" )
+ return uno::Any ( aEntry.nSize );
+ else
+ throw UnknownPropertyException(PropertyName);
+}
+
+void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent )
+{
+ if ( pEntry->IsFolder() )
+ maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageFolder*>(pEntry)));
+ else
+ maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageStream*>(pEntry)));
+ if ( bSetParent )
+ pEntry->setParent ( *this );
+}
+
+OUString ZipPackageFolder::getImplementationName()
+{
+ return "ZipPackageFolder";
+}
+
+uno::Sequence< OUString > ZipPackageFolder::getSupportedServiceNames()
+{
+ return { "com.sun.star.packages.PackageFolder" };
+}
+
+sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+
+ZipContentInfo::ZipContentInfo ( ZipPackageStream * pNewStream )
+: xTunnel ( pNewStream )
+, bFolder ( false )
+, pStream ( pNewStream )
+{
+}
+
+ZipContentInfo::ZipContentInfo ( ZipPackageFolder * pNewFolder )
+: xTunnel ( pNewFolder )
+, bFolder ( true )
+, pFolder ( pNewFolder )
+{
+}
+
+ZipContentInfo::ZipContentInfo( const ZipContentInfo& ) = default;
+ZipContentInfo::ZipContentInfo( ZipContentInfo&& ) = default;
+ZipContentInfo& ZipContentInfo::operator=( const ZipContentInfo& ) = default;
+ZipContentInfo& ZipContentInfo::operator=( ZipContentInfo&& ) = default;
+
+ZipContentInfo::~ZipContentInfo()
+{
+ if ( bFolder )
+ pFolder->clearParent();
+ else
+ pStream->clearParent();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */