Fix GNU C++ version check
[LibreOffice.git] / package / source / zippackage / ZipPackageFolder.cxx
blobc52836379692194088c07cee2665ee610c85ea09
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ZipPackageFolder.hxx>
21 #include <ZipOutputStream.hxx>
22 #include <ZipPackageStream.hxx>
23 #include <PackageConstants.hxx>
24 #include "ZipPackageFolderEnumeration.hxx"
25 #include <com/sun/star/io/IOException.hpp>
26 #include <com/sun/star/packages/zip/ZipConstants.hpp>
27 #include <com/sun/star/packages/zip/ZipException.hpp>
28 #include <com/sun/star/embed/StorageFormats.hpp>
29 #include <comphelper/sequence.hxx>
30 #include <comphelper/servicehelper.hxx>
31 #include <cppuhelper/supportsservice.hxx>
32 #include <sal/log.hxx>
33 #include <com/sun/star/beans/PropertyValue.hpp>
35 using namespace com::sun::star;
36 using namespace com::sun::star::packages::zip::ZipConstants;
37 using namespace com::sun::star::packages::zip;
38 using namespace com::sun::star::packages;
39 using namespace com::sun::star::container;
40 using namespace com::sun::star::beans;
41 using namespace com::sun::star::lang;
42 using namespace com::sun::star::io;
43 using namespace cppu;
45 #if OSL_DEBUG_LEVEL > 0
46 #define THROW_WHERE SAL_WHERE
47 #else
48 #define THROW_WHERE ""
49 #endif
51 ZipPackageFolder::ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext,
52 sal_Int32 nFormat,
53 bool bAllowRemoveOnInsert )
55 m_xContext = xContext;
56 m_nFormat = nFormat;
57 mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
58 SetFolder ( true );
59 aEntry.nVersion = -1;
60 aEntry.nFlag = 0;
61 aEntry.nMethod = STORED;
62 aEntry.nTime = -1;
63 aEntry.nCrc = 0;
64 aEntry.nCompressedSize = 0;
65 aEntry.nSize = 0;
66 aEntry.nOffset = -1;
69 ZipPackageFolder::~ZipPackageFolder()
73 bool ZipPackageFolder::LookForUnexpectedODF12Streams(
74 std::u16string_view const aPath, bool const isWholesomeEncryption)
76 bool bHasUnexpected = false;
78 for (const auto& [rShortName, rInfo] : maContents)
80 if ( rInfo.bFolder )
82 if ( aPath == u"META-INF/" )
84 // META-INF is not allowed to contain subfolders
85 bHasUnexpected = true;
87 else if (isWholesomeEncryption && rShortName != u"META-INF")
89 bHasUnexpected = true;
91 else
93 OUString sOwnPath = aPath + rShortName + "/";
94 bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams(sOwnPath, isWholesomeEncryption);
97 else
99 if ( aPath == u"META-INF/" )
101 if ( rShortName != "manifest.xml"
102 && rShortName.indexOf( "signatures" ) == -1 )
104 // a stream from META-INF with unexpected name
105 bHasUnexpected = true;
108 // streams from META-INF with expected names are allowed not to be registered in manifest.xml
110 else if (isWholesomeEncryption && rShortName != "mimetype" && rShortName != "encrypted-package")
112 bHasUnexpected = true;
114 else if ( !rInfo.pStream->IsFromManifest() )
116 // the stream is not in META-INF and is not registered in manifest.xml,
117 // check whether it is an internal part of the package format
118 if ( !aPath.empty() || rShortName != "mimetype" )
120 // if it is not "mimetype" from the root it is not a part of the package
121 bHasUnexpected = true;
126 if (bHasUnexpected)
127 break;
130 return bHasUnexpected;
133 void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair )
135 OUString aExt;
136 if ( aPair.First.toChar() == '.' )
137 aExt = aPair.First;
138 else
139 aExt = "." + aPair.First;
141 for (const auto& [rShortName, rInfo] : maContents)
143 if ( rInfo.bFolder )
144 rInfo.pFolder->setChildStreamsTypeByExtension( aPair );
145 else
147 sal_Int32 nPathLength = rShortName.getLength();
148 sal_Int32 nExtLength = aExt.getLength();
149 if ( nPathLength >= nExtLength && rShortName.match( aExt, nPathLength - nExtLength ) )
150 rInfo.pStream->SetMediaType( aPair.Second );
155 // XNameContainer
156 void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement )
158 if (hasByName(aName))
159 throw ElementExistException(THROW_WHERE );
161 uno::Reference < XInterface > xRef;
162 if ( !(aElement >>= xRef) )
163 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
165 ZipPackageEntry* pEntry = dynamic_cast<ZipPackageFolder*>(xRef.get());
166 if (!pEntry)
167 pEntry = dynamic_cast<ZipPackageStream*>(xRef.get());
168 if (!pEntry)
169 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
171 if (pEntry->getName() != aName )
172 pEntry->setName (aName);
173 doInsertByName ( pEntry, true );
176 void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name )
178 ContentHash::iterator aIter = maContents.find ( Name );
179 if ( aIter == maContents.end() )
180 throw NoSuchElementException(THROW_WHERE );
181 maContents.erase( aIter );
183 // XEnumerationAccess
184 uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( )
186 return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents));
188 // XElementAccess
189 uno::Type SAL_CALL ZipPackageFolder::getElementType( )
191 return cppu::UnoType<XInterface>::get();
193 sal_Bool SAL_CALL ZipPackageFolder::hasElements( )
195 return !maContents.empty();
197 // XNameAccess
198 ZipContentInfo& ZipPackageFolder::doGetByName( const OUString& aName )
200 ContentHash::iterator aIter = maContents.find ( aName );
201 if ( aIter == maContents.end())
202 throw NoSuchElementException(THROW_WHERE );
203 return aIter->second;
206 uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName )
208 return uno::Any ( uno::Reference(cppu::getXWeak(doGetByName ( aName ).xPackageEntry.get())) );
210 uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( )
212 return comphelper::mapKeysToSequence(maContents);
214 sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName )
216 return maContents.find ( aName ) != maContents.end ();
218 // XNameReplace
219 void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement )
221 if ( !hasByName( aName ) )
222 throw NoSuchElementException(THROW_WHERE );
224 removeByName( aName );
225 insertByName(aName, aElement);
228 bool ZipPackageFolder::saveChild(
229 const OUString &rPath,
230 std::vector < uno::Sequence < PropertyValue > > &rManList,
231 ZipOutputStream & rZipOut,
232 const uno::Sequence < sal_Int8 >& rEncryptionKey,
233 ::std::optional<sal_Int32> const oPBKDF2IterationCount,
234 ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args)
236 uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
237 OUString sTempName = rPath + "/";
239 if ( !GetMediaType().isEmpty() )
241 auto pPropSet = aPropSet.getArray();
242 pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType";
243 pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType();
244 pPropSet[PKG_MNFST_VERSION].Name = "Version";
245 pPropSet[PKG_MNFST_VERSION].Value <<= GetVersion();
246 pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath";
247 pPropSet[PKG_MNFST_FULLPATH].Value <<= sTempName;
249 else
250 aPropSet.realloc( 0 );
252 saveContents(sTempName, rManList, rZipOut, rEncryptionKey, oPBKDF2IterationCount, oArgon2Args);
254 // folder can have a mediatype only in package format
255 if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
256 rManList.push_back( aPropSet );
258 return true;
261 void ZipPackageFolder::saveContents(
262 const OUString &rPath,
263 std::vector < uno::Sequence < PropertyValue > > &rManList,
264 ZipOutputStream & rZipOut,
265 const uno::Sequence < sal_Int8 >& rEncryptionKey,
266 ::std::optional<sal_Int32> const oPBKDF2IterationCount,
267 ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args) const
269 if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
271 // it is an empty subfolder, use workaround to store it
272 auto pTempEntry = std::make_unique<ZipEntry>(aEntry);
273 pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getLength() );
274 pTempEntry->nExtraLen = -1;
275 pTempEntry->sPath = rPath;
279 ZipOutputStream::setEntry(*pTempEntry);
280 rZipOut.writeLOC(std::move(pTempEntry));
281 rZipOut.rawCloseEntry();
283 catch ( ZipException& )
285 throw uno::RuntimeException( THROW_WHERE );
287 catch ( IOException& )
289 throw uno::RuntimeException( THROW_WHERE );
293 bool bMimeTypeStreamStored = false;
294 OUString aMimeTypeStreamName(u"mimetype"_ustr);
295 if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() )
297 // let the "mimetype" stream in root folder be stored as the first stream if it is zip format
298 ContentHash::const_iterator aIter = maContents.find ( aMimeTypeStreamName );
299 if ( aIter != maContents.end() && !(*aIter).second.bFolder )
301 bMimeTypeStreamStored = true;
302 if (!aIter->second.pStream->saveChild(rPath + aIter->first, rManList, rZipOut,
303 rEncryptionKey, oPBKDF2IterationCount, oArgon2Args))
305 throw uno::RuntimeException( THROW_WHERE );
310 for (const auto& [rShortName, rInfo] : maContents)
312 if ( !bMimeTypeStreamStored || rShortName != aMimeTypeStreamName )
314 if (rInfo.bFolder)
316 if (!rInfo.pFolder->saveChild(rPath + rShortName, rManList, rZipOut,
317 rEncryptionKey, oPBKDF2IterationCount, oArgon2Args))
319 throw uno::RuntimeException( THROW_WHERE );
322 else
324 if (!rInfo.pStream->saveChild(rPath + rShortName, rManList, rZipOut,
325 rEncryptionKey, oPBKDF2IterationCount, oArgon2Args))
327 throw uno::RuntimeException( THROW_WHERE );
334 void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
336 if ( aPropertyName == "MediaType" )
338 // TODO/LATER: activate when zip ucp is ready
339 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
340 // throw UnknownPropertyException(THROW_WHERE );
342 aValue >>= msMediaType;
344 else if ( aPropertyName == "Version" )
345 aValue >>= m_sVersion;
346 else if ( aPropertyName == "Size" )
347 aValue >>= aEntry.nSize;
348 else
349 throw UnknownPropertyException(aPropertyName);
351 uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName )
353 if ( PropertyName == "MediaType" )
355 // TODO/LATER: activate when zip ucp is ready
356 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
357 // throw UnknownPropertyException(THROW_WHERE );
359 return uno::Any ( msMediaType );
361 else if ( PropertyName == "Version" )
362 return uno::Any( m_sVersion );
363 else if ( PropertyName == "Size" )
364 return uno::Any ( aEntry.nSize );
365 else
366 throw UnknownPropertyException(PropertyName);
369 void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent )
371 if ( pEntry->IsFolder() )
372 maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageFolder*>(pEntry)));
373 else
374 maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageStream*>(pEntry)));
375 if ( bSetParent )
376 pEntry->setParent ( *this );
379 OUString ZipPackageFolder::getImplementationName()
381 return u"ZipPackageFolder"_ustr;
384 uno::Sequence< OUString > ZipPackageFolder::getSupportedServiceNames()
386 return { u"com.sun.star.packages.PackageFolder"_ustr };
389 sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName )
391 return cppu::supportsService(this, rServiceName);
395 ZipContentInfo::ZipContentInfo ( ZipPackageStream * pNewStream )
396 : xPackageEntry ( pNewStream )
397 , bFolder ( false )
398 , pStream ( pNewStream )
402 ZipContentInfo::ZipContentInfo ( ZipPackageFolder * pNewFolder )
403 : xPackageEntry ( pNewFolder )
404 , bFolder ( true )
405 , pFolder ( pNewFolder )
409 ZipContentInfo::ZipContentInfo( const ZipContentInfo& ) = default;
410 ZipContentInfo::ZipContentInfo( ZipContentInfo&& ) = default;
411 ZipContentInfo& ZipContentInfo::operator=( const ZipContentInfo& ) = default;
412 ZipContentInfo& ZipContentInfo::operator=( ZipContentInfo&& ) = default;
414 ZipContentInfo::~ZipContentInfo()
416 if ( bFolder )
417 pFolder->clearParent();
418 else
419 pStream->clearParent();
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */