1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
;
45 #if OSL_DEBUG_LEVEL > 0
46 #define THROW_WHERE SAL_WHERE
48 #define THROW_WHERE ""
51 ZipPackageFolder::ZipPackageFolder( const css::uno::Reference
< css::uno::XComponentContext
>& xContext
,
53 bool bAllowRemoveOnInsert
)
55 m_xContext
= xContext
;
57 mbAllowRemoveOnInsert
= bAllowRemoveOnInsert
;
61 aEntry
.nMethod
= STORED
;
64 aEntry
.nCompressedSize
= 0;
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
)
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;
93 OUString sOwnPath
= aPath
+ rShortName
+ "/";
94 bHasUnexpected
= rInfo
.pFolder
->LookForUnexpectedODF12Streams(sOwnPath
, isWholesomeEncryption
);
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;
130 return bHasUnexpected
;
133 void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair
& aPair
)
136 if ( aPair
.First
.toChar() == '.' )
139 aExt
= "." + aPair
.First
;
141 for (const auto& [rShortName
, rInfo
] : maContents
)
144 rInfo
.pFolder
->setChildStreamsTypeByExtension( aPair
);
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
);
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());
167 pEntry
= dynamic_cast<ZipPackageStream
*>(xRef
.get());
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
));
189 uno::Type SAL_CALL
ZipPackageFolder::getElementType( )
191 return cppu::UnoType
<XInterface
>::get();
193 sal_Bool SAL_CALL
ZipPackageFolder::hasElements( )
195 return !maContents
.empty();
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 ();
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
;
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
);
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
)
316 if (!rInfo
.pFolder
->saveChild(rPath
+ rShortName
, rManList
, rZipOut
,
317 rEncryptionKey
, oPBKDF2IterationCount
, oArgon2Args
))
319 throw uno::RuntimeException( THROW_WHERE
);
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
;
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
);
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
)));
374 maContents
.emplace(pEntry
->getName(), ZipContentInfo(static_cast<ZipPackageStream
*>(pEntry
)));
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
)
398 , pStream ( pNewStream
)
402 ZipContentInfo::ZipContentInfo ( ZipPackageFolder
* pNewFolder
)
403 : xPackageEntry ( pNewFolder
)
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()
417 pFolder
->clearParent();
419 pStream
->clearParent();
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */