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 .
22 #include <ZipPackageFolder.hxx>
23 #include <ZipFile.hxx>
24 #include <ZipOutputStream.hxx>
25 #include <ZipPackageStream.hxx>
26 #include <PackageConstants.hxx>
27 #include "ZipPackageFolderEnumeration.hxx"
28 #include <com/sun/star/packages/zip/ZipConstants.hpp>
29 #include <com/sun/star/embed/StorageFormats.hpp>
30 #include <comphelper/sequence.hxx>
31 #include <comphelper/servicehelper.hxx>
32 #include <cppuhelper/supportsservice.hxx>
33 #include <cppuhelper/typeprovider.hxx>
34 #include <osl/diagnose.h>
35 #include <sal/log.hxx>
36 #include <rtl/digest.h>
37 #include "ContentInfo.hxx"
38 #include <com/sun/star/beans/PropertyValue.hpp>
39 #include <EncryptedDataHeader.hxx>
40 #include <rtl/instance.hxx>
42 using namespace com::sun::star
;
43 using namespace com::sun::star::packages::zip::ZipConstants
;
44 using namespace com::sun::star::packages::zip
;
45 using namespace com::sun::star::packages
;
46 using namespace com::sun::star::container
;
47 using namespace com::sun::star::beans
;
48 using namespace com::sun::star::lang
;
49 using namespace com::sun::star::io
;
52 #if OSL_DEBUG_LEVEL > 0
53 #define THROW_WHERE SAL_WHERE
55 #define THROW_WHERE ""
58 namespace { struct lcl_CachedImplId
: public rtl::Static
< cppu::OImplementationId
, lcl_CachedImplId
> {}; }
60 ZipPackageFolder::ZipPackageFolder( const css::uno::Reference
< css::uno::XComponentContext
>& xContext
,
62 bool bAllowRemoveOnInsert
)
64 m_xContext
= xContext
;
66 mbAllowRemoveOnInsert
= bAllowRemoveOnInsert
;
70 aEntry
.nMethod
= STORED
;
73 aEntry
.nCompressedSize
= 0;
78 ZipPackageFolder::~ZipPackageFolder()
82 bool ZipPackageFolder::LookForUnexpectedODF12Streams( const OUString
& aPath
)
84 bool bHasUnexpected
= false;
86 for (const auto& [rShortName
, rxInfo
] : maContents
)
88 const ZipContentInfo
&rInfo
= *rxInfo
;
92 if ( aPath
== "META-INF/" )
94 // META-INF is not allowed to contain subfolders
95 bHasUnexpected
= true;
99 OUString sOwnPath
= aPath
+ rShortName
+ "/";
100 bHasUnexpected
= rInfo
.pFolder
->LookForUnexpectedODF12Streams( sOwnPath
);
105 if ( aPath
== "META-INF/" )
107 if ( rShortName
!= "manifest.xml"
108 && rShortName
.indexOf( "signatures" ) == -1 )
110 // a stream from META-INF with unexpected name
111 bHasUnexpected
= true;
114 // streams from META-INF with expected names are allowed not to be registered in manifest.xml
116 else if ( !rInfo
.pStream
->IsFromManifest() )
118 // the stream is not in META-INF and is not registered in manifest.xml,
119 // check whether it is an internal part of the package format
120 if ( !aPath
.isEmpty() || rShortName
!= "mimetype" )
122 // if it is not "mimetype" from the root it is not a part of the package
123 bHasUnexpected
= true;
132 return bHasUnexpected
;
135 void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair
& aPair
)
138 if ( aPair
.First
.toChar() == '.' )
141 aExt
= "." + aPair
.First
;
143 for (const auto& [rShortName
, rxInfo
] : maContents
)
145 const ZipContentInfo
&rInfo
= *rxInfo
;
148 rInfo
.pFolder
->setChildStreamsTypeByExtension( aPair
);
151 sal_Int32 nPathLength
= rShortName
.getLength();
152 sal_Int32 nExtLength
= aExt
.getLength();
153 if ( nPathLength
>= nExtLength
&& rShortName
.match( aExt
, nPathLength
- nExtLength
) )
154 rInfo
.pStream
->SetMediaType( aPair
.Second
);
159 css::uno::Sequence
< sal_Int8
> ZipPackageFolder::getUnoTunnelId()
161 return lcl_CachedImplId::get().getImplementationId();
165 void SAL_CALL
ZipPackageFolder::insertByName( const OUString
& aName
, const uno::Any
& aElement
)
167 if (hasByName(aName
))
168 throw ElementExistException(THROW_WHERE
);
170 uno::Reference
< XUnoTunnel
> xRef
;
172 if ( !(aElement
>>= xRef
) )
173 throw IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 0 );
176 ZipPackageEntry
*pEntry
;
177 if ( ( nTest
= xRef
->getSomething ( ZipPackageFolder::getUnoTunnelId() ) ) != 0 )
179 ZipPackageFolder
*pFolder
= reinterpret_cast < ZipPackageFolder
* > ( nTest
);
180 pEntry
= static_cast < ZipPackageEntry
* > ( pFolder
);
182 else if ( ( nTest
= xRef
->getSomething ( ZipPackageStream::getUnoTunnelId() ) ) != 0 )
184 ZipPackageStream
*pStream
= reinterpret_cast < ZipPackageStream
* > ( nTest
);
185 pEntry
= static_cast < ZipPackageEntry
* > ( pStream
);
188 throw IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 0 );
190 if (pEntry
->getName() != aName
)
191 pEntry
->setName (aName
);
192 doInsertByName ( pEntry
, true );
195 void SAL_CALL
ZipPackageFolder::removeByName( const OUString
& Name
)
197 ContentHash::iterator aIter
= maContents
.find ( Name
);
198 if ( aIter
== maContents
.end() )
199 throw NoSuchElementException(THROW_WHERE
);
200 maContents
.erase( aIter
);
202 // XEnumerationAccess
203 uno::Reference
< XEnumeration
> SAL_CALL
ZipPackageFolder::createEnumeration( )
205 return uno::Reference
< XEnumeration
> (new ZipPackageFolderEnumeration(maContents
));
208 uno::Type SAL_CALL
ZipPackageFolder::getElementType( )
210 return cppu::UnoType
<XUnoTunnel
>::get();
212 sal_Bool SAL_CALL
ZipPackageFolder::hasElements( )
214 return !maContents
.empty();
217 ZipContentInfo
& ZipPackageFolder::doGetByName( const OUString
& aName
)
219 ContentHash::iterator aIter
= maContents
.find ( aName
);
220 if ( aIter
== maContents
.end())
221 throw NoSuchElementException(THROW_WHERE
);
222 return *aIter
->second
;
225 uno::Any SAL_CALL
ZipPackageFolder::getByName( const OUString
& aName
)
227 return uno::makeAny ( doGetByName ( aName
).xTunnel
);
229 uno::Sequence
< OUString
> SAL_CALL
ZipPackageFolder::getElementNames( )
231 return comphelper::mapKeysToSequence(maContents
);
233 sal_Bool SAL_CALL
ZipPackageFolder::hasByName( const OUString
& aName
)
235 return maContents
.find ( aName
) != maContents
.end ();
238 void SAL_CALL
ZipPackageFolder::replaceByName( const OUString
& aName
, const uno::Any
& aElement
)
240 if ( !hasByName( aName
) )
241 throw NoSuchElementException(THROW_WHERE
);
243 removeByName( aName
);
244 insertByName(aName
, aElement
);
247 bool ZipPackageFolder::saveChild(
248 const OUString
&rPath
,
249 std::vector
< uno::Sequence
< PropertyValue
> > &rManList
,
250 ZipOutputStream
& rZipOut
,
251 const uno::Sequence
< sal_Int8
>& rEncryptionKey
,
252 sal_Int32 nPBKDF2IterationCount
,
253 const rtlRandomPool
&rRandomPool
)
255 const OUString
sMediaTypeProperty ("MediaType");
256 const OUString
sVersionProperty ("Version");
257 const OUString
sFullPathProperty ("FullPath");
259 uno::Sequence
< PropertyValue
> aPropSet (PKG_SIZE_NOENCR_MNFST
);
260 OUString sTempName
= rPath
+ "/";
262 if ( !GetMediaType().isEmpty() )
264 aPropSet
[PKG_MNFST_MEDIATYPE
].Name
= sMediaTypeProperty
;
265 aPropSet
[PKG_MNFST_MEDIATYPE
].Value
<<= GetMediaType();
266 aPropSet
[PKG_MNFST_VERSION
].Name
= sVersionProperty
;
267 aPropSet
[PKG_MNFST_VERSION
].Value
<<= GetVersion();
268 aPropSet
[PKG_MNFST_FULLPATH
].Name
= sFullPathProperty
;
269 aPropSet
[PKG_MNFST_FULLPATH
].Value
<<= sTempName
;
272 aPropSet
.realloc( 0 );
274 saveContents( sTempName
, rManList
, rZipOut
, rEncryptionKey
, nPBKDF2IterationCount
, rRandomPool
);
276 // folder can have a mediatype only in package format
277 if ( aPropSet
.hasElements() && ( m_nFormat
== embed::StorageFormats::PACKAGE
) )
278 rManList
.push_back( aPropSet
);
283 void ZipPackageFolder::saveContents(
284 const OUString
&rPath
,
285 std::vector
< uno::Sequence
< PropertyValue
> > &rManList
,
286 ZipOutputStream
& rZipOut
,
287 const uno::Sequence
< sal_Int8
>& rEncryptionKey
,
288 sal_Int32 nPBKDF2IterationCount
,
289 const rtlRandomPool
&rRandomPool
) const
291 if ( maContents
.empty() && !rPath
.isEmpty() && m_nFormat
!= embed::StorageFormats::OFOPXML
)
293 // it is an empty subfolder, use workaround to store it
294 ZipEntry
* pTempEntry
= new ZipEntry(aEntry
);
295 pTempEntry
->nPathLen
= static_cast<sal_Int16
>( OUStringToOString( rPath
, RTL_TEXTENCODING_UTF8
).getLength() );
296 pTempEntry
->nExtraLen
= -1;
297 pTempEntry
->sPath
= rPath
;
301 ZipOutputStream::setEntry(pTempEntry
);
302 rZipOut
.writeLOC(pTempEntry
);
303 rZipOut
.rawCloseEntry();
305 catch ( ZipException
& )
307 throw uno::RuntimeException( THROW_WHERE
);
309 catch ( IOException
& )
311 throw uno::RuntimeException( THROW_WHERE
);
315 bool bMimeTypeStreamStored
= false;
316 OUString
aMimeTypeStreamName("mimetype");
317 if ( m_nFormat
== embed::StorageFormats::ZIP
&& rPath
.isEmpty() )
319 // let the "mimetype" stream in root folder be stored as the first stream if it is zip format
320 ContentHash::const_iterator aIter
= maContents
.find ( aMimeTypeStreamName
);
321 if ( aIter
!= maContents
.end() && !(*aIter
).second
->bFolder
)
323 bMimeTypeStreamStored
= true;
324 if( !aIter
->second
->pStream
->saveChild(
325 rPath
+ aIter
->first
, rManList
, rZipOut
, rEncryptionKey
, nPBKDF2IterationCount
, rRandomPool
))
327 throw uno::RuntimeException( THROW_WHERE
);
332 for (const auto& [rShortName
, rxInfo
] : maContents
)
334 const ZipContentInfo
&rInfo
= *rxInfo
;
336 if ( !bMimeTypeStreamStored
|| rShortName
!= aMimeTypeStreamName
)
340 if( !rInfo
.pFolder
->saveChild(
341 rPath
+ rShortName
, rManList
, rZipOut
, rEncryptionKey
, nPBKDF2IterationCount
, rRandomPool
))
343 throw uno::RuntimeException( THROW_WHERE
);
348 if( !rInfo
.pStream
->saveChild(
349 rPath
+ rShortName
, rManList
, rZipOut
, rEncryptionKey
, nPBKDF2IterationCount
, rRandomPool
))
351 throw uno::RuntimeException( THROW_WHERE
);
358 sal_Int64 SAL_CALL
ZipPackageFolder::getSomething( const uno::Sequence
< sal_Int8
>& aIdentifier
)
361 if ( isUnoTunnelId
<ZipPackageFolder
>(aIdentifier
) )
362 nMe
= reinterpret_cast < sal_Int64
> ( this );
365 void SAL_CALL
ZipPackageFolder::setPropertyValue( const OUString
& aPropertyName
, const uno::Any
& aValue
)
367 if ( aPropertyName
== "MediaType" )
369 // TODO/LATER: activate when zip ucp is ready
370 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
371 // throw UnknownPropertyException(THROW_WHERE );
373 aValue
>>= msMediaType
;
375 else if ( aPropertyName
== "Version" )
376 aValue
>>= m_sVersion
;
377 else if ( aPropertyName
== "Size" )
378 aValue
>>= aEntry
.nSize
;
380 throw UnknownPropertyException(aPropertyName
);
382 uno::Any SAL_CALL
ZipPackageFolder::getPropertyValue( const OUString
& PropertyName
)
384 if ( PropertyName
== "MediaType" )
386 // TODO/LATER: activate when zip ucp is ready
387 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
388 // throw UnknownPropertyException(THROW_WHERE );
390 return uno::makeAny ( msMediaType
);
392 else if ( PropertyName
== "Version" )
393 return uno::makeAny( m_sVersion
);
394 else if ( PropertyName
== "Size" )
395 return uno::makeAny ( aEntry
.nSize
);
397 throw UnknownPropertyException(PropertyName
);
400 void ZipPackageFolder::doInsertByName ( ZipPackageEntry
*pEntry
, bool bSetParent
)
402 if ( pEntry
->IsFolder() )
403 maContents
[pEntry
->getName()] = std::make_unique
<ZipContentInfo
>(static_cast<ZipPackageFolder
*>(pEntry
));
405 maContents
[pEntry
->getName()] = std::make_unique
<ZipContentInfo
>(static_cast<ZipPackageStream
*>(pEntry
));
407 pEntry
->setParent ( *this );
410 OUString
ZipPackageFolder::getImplementationName()
412 return "ZipPackageFolder";
415 uno::Sequence
< OUString
> ZipPackageFolder::getSupportedServiceNames()
417 uno::Sequence
< OUString
> aNames
{ "com.sun.star.packages.PackageFolder" };
421 sal_Bool SAL_CALL
ZipPackageFolder::supportsService( OUString
const & rServiceName
)
423 return cppu::supportsService(this, rServiceName
);
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */