LibreOffice Module package (master) 1
ZipPackageFolder.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <ZipPackageFolder.hxx>
21#include <ZipOutputStream.hxx>
22#include <ZipPackageStream.hxx>
23#include <PackageConstants.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>
32#include <sal/log.hxx>
33#include <com/sun/star/beans/PropertyValue.hpp>
34
35using namespace com::sun::star;
36using namespace com::sun::star::packages::zip::ZipConstants;
37using namespace com::sun::star::packages::zip;
38using namespace com::sun::star::packages;
39using namespace com::sun::star::container;
40using namespace com::sun::star::beans;
41using namespace com::sun::star::lang;
42using namespace com::sun::star::io;
43using namespace cppu;
44
45#if OSL_DEBUG_LEVEL > 0
46#define THROW_WHERE SAL_WHERE
47#else
48#define THROW_WHERE ""
49#endif
50
51ZipPackageFolder::ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext,
52 sal_Int32 nFormat,
53 bool bAllowRemoveOnInsert )
54{
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;
67}
68
70{
71}
72
73bool ZipPackageFolder::LookForUnexpectedODF12Streams( std::u16string_view aPath )
74{
75 bool bHasUnexpected = false;
76
77 for (const auto& [rShortName, rInfo] : maContents)
78 {
79 if ( rInfo.bFolder )
80 {
81 if ( aPath == u"META-INF/" )
82 {
83 // META-INF is not allowed to contain subfolders
84 bHasUnexpected = true;
85 }
86 else
87 {
88 OUString sOwnPath = aPath + rShortName + "/";
89 bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams( sOwnPath );
90 }
91 }
92 else
93 {
94 if ( aPath == u"META-INF/" )
95 {
96 if ( rShortName != "manifest.xml"
97 && rShortName.indexOf( "signatures" ) == -1 )
98 {
99 // a stream from META-INF with unexpected name
100 bHasUnexpected = true;
101 }
102
103 // streams from META-INF with expected names are allowed not to be registered in manifest.xml
104 }
105 else if ( !rInfo.pStream->IsFromManifest() )
106 {
107 // the stream is not in META-INF and is not registered in manifest.xml,
108 // check whether it is an internal part of the package format
109 if ( !aPath.empty() || rShortName != "mimetype" )
110 {
111 // if it is not "mimetype" from the root it is not a part of the package
112 bHasUnexpected = true;
113 }
114 }
115 }
116
117 if (bHasUnexpected)
118 break;
119 }
120
121 return bHasUnexpected;
122}
123
124void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair )
125{
126 OUString aExt;
127 if ( aPair.First.toChar() == '.' )
128 aExt = aPair.First;
129 else
130 aExt = "." + aPair.First;
131
132 for (const auto& [rShortName, rInfo] : maContents)
133 {
134 if ( rInfo.bFolder )
135 rInfo.pFolder->setChildStreamsTypeByExtension( aPair );
136 else
137 {
138 sal_Int32 nPathLength = rShortName.getLength();
139 sal_Int32 nExtLength = aExt.getLength();
140 if ( nPathLength >= nExtLength && rShortName.match( aExt, nPathLength - nExtLength ) )
141 rInfo.pStream->SetMediaType( aPair.Second );
142 }
143 }
144}
145
146 // XNameContainer
147void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement )
148{
149 if (hasByName(aName))
150 throw ElementExistException(THROW_WHERE );
151
152 uno::Reference < XInterface > xRef;
153 aElement >>= xRef;
154 if ( !(aElement >>= xRef) )
155 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
156
157 ZipPackageEntry* pEntry = dynamic_cast<ZipPackageFolder*>(xRef.get());
158 if (!pEntry)
159 pEntry = dynamic_cast<ZipPackageStream*>(xRef.get());
160 if (!pEntry)
161 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
162
163 if (pEntry->getName() != aName )
164 pEntry->setName (aName);
165 doInsertByName ( pEntry, true );
166}
167
168void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name )
169{
170 ContentHash::iterator aIter = maContents.find ( Name );
171 if ( aIter == maContents.end() )
172 throw NoSuchElementException(THROW_WHERE );
173 maContents.erase( aIter );
174}
175 // XEnumerationAccess
176uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( )
177{
178 return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents));
179}
180 // XElementAccess
182{
184}
186{
187 return !maContents.empty();
188}
189 // XNameAccess
191{
192 ContentHash::iterator aIter = maContents.find ( aName );
193 if ( aIter == maContents.end())
194 throw NoSuchElementException(THROW_WHERE );
195 return aIter->second;
196}
197
198uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName )
199{
200 return uno::Any ( uno::Reference(cppu::getXWeak(doGetByName ( aName ).xPackageEntry.get())) );
201}
202uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( )
203{
205}
206sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName )
207{
208 return maContents.find ( aName ) != maContents.end ();
209}
210 // XNameReplace
211void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement )
212{
213 if ( !hasByName( aName ) )
214 throw NoSuchElementException(THROW_WHERE );
215
217 insertByName(aName, aElement);
218}
219
221 const OUString &rPath,
222 std::vector < uno::Sequence < PropertyValue > > &rManList,
223 ZipOutputStream & rZipOut,
224 const uno::Sequence < sal_Int8 >& rEncryptionKey,
225 sal_Int32 nPBKDF2IterationCount,
226 const rtlRandomPool &rRandomPool)
227{
228 uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
229 OUString sTempName = rPath + "/";
230
231 if ( !GetMediaType().isEmpty() )
232 {
233 auto pPropSet = aPropSet.getArray();
234 pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType";
235 pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType();
236 pPropSet[PKG_MNFST_VERSION].Name = "Version";
237 pPropSet[PKG_MNFST_VERSION].Value <<= GetVersion();
238 pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath";
239 pPropSet[PKG_MNFST_FULLPATH].Value <<= sTempName;
240 }
241 else
242 aPropSet.realloc( 0 );
243
244 saveContents( sTempName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool);
245
246 // folder can have a mediatype only in package format
247 if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
248 rManList.push_back( aPropSet );
249
250 return true;
251}
252
254 const OUString &rPath,
255 std::vector < uno::Sequence < PropertyValue > > &rManList,
256 ZipOutputStream & rZipOut,
257 const uno::Sequence < sal_Int8 >& rEncryptionKey,
258 sal_Int32 nPBKDF2IterationCount,
259 const rtlRandomPool &rRandomPool ) const
260{
261 if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
262 {
263 // it is an empty subfolder, use workaround to store it
264 ZipEntry* pTempEntry = new ZipEntry(aEntry);
265 pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getLength() );
266 pTempEntry->nExtraLen = -1;
267 pTempEntry->sPath = rPath;
268
269 try
270 {
271 ZipOutputStream::setEntry(pTempEntry);
272 rZipOut.writeLOC(pTempEntry);
273 rZipOut.rawCloseEntry();
274 }
275 catch ( ZipException& )
276 {
277 throw uno::RuntimeException( THROW_WHERE );
278 }
279 catch ( IOException& )
280 {
281 throw uno::RuntimeException( THROW_WHERE );
282 }
283 }
284
285 bool bMimeTypeStreamStored = false;
286 OUString aMimeTypeStreamName("mimetype");
287 if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() )
288 {
289 // let the "mimetype" stream in root folder be stored as the first stream if it is zip format
290 ContentHash::const_iterator aIter = maContents.find ( aMimeTypeStreamName );
291 if ( aIter != maContents.end() && !(*aIter).second.bFolder )
292 {
293 bMimeTypeStreamStored = true;
294 if( !aIter->second.pStream->saveChild(
295 rPath + aIter->first, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
296 {
297 throw uno::RuntimeException( THROW_WHERE );
298 }
299 }
300 }
301
302 for (const auto& [rShortName, rInfo] : maContents)
303 {
304 if ( !bMimeTypeStreamStored || rShortName != aMimeTypeStreamName )
305 {
306 if (rInfo.bFolder)
307 {
308 if( !rInfo.pFolder->saveChild(
309 rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
310 {
311 throw uno::RuntimeException( THROW_WHERE );
312 }
313 }
314 else
315 {
316 if( !rInfo.pStream->saveChild(
317 rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
318 {
319 throw uno::RuntimeException( THROW_WHERE );
320 }
321 }
322 }
323 }
324}
325
326void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
327{
328 if ( aPropertyName == "MediaType" )
329 {
330 // TODO/LATER: activate when zip ucp is ready
331 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
332 // throw UnknownPropertyException(THROW_WHERE );
333
334 aValue >>= msMediaType;
335 }
336 else if ( aPropertyName == "Version" )
337 aValue >>= m_sVersion;
338 else if ( aPropertyName == "Size" )
339 aValue >>= aEntry.nSize;
340 else
341 throw UnknownPropertyException(aPropertyName);
342}
343uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName )
344{
345 if ( PropertyName == "MediaType" )
346 {
347 // TODO/LATER: activate when zip ucp is ready
348 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
349 // throw UnknownPropertyException(THROW_WHERE );
350
351 return uno::Any ( msMediaType );
352 }
353 else if ( PropertyName == "Version" )
354 return uno::Any( m_sVersion );
355 else if ( PropertyName == "Size" )
356 return uno::Any ( aEntry.nSize );
357 else
358 throw UnknownPropertyException(PropertyName);
359}
360
361void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent )
362{
363 if ( pEntry->IsFolder() )
364 maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageFolder*>(pEntry)));
365 else
366 maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageStream*>(pEntry)));
367 if ( bSetParent )
368 pEntry->setParent ( *this );
369}
370
372{
373 return "ZipPackageFolder";
374}
375
377{
378 return { "com.sun.star.packages.PackageFolder" };
379}
380
381sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName )
382{
383 return cppu::supportsService(this, rServiceName);
384}
385
386
388: xPackageEntry ( pNewStream )
389, bFolder ( false )
390, pStream ( pNewStream )
391{
392}
393
395: xPackageEntry ( pNewFolder )
396, bFolder ( true )
397, pFolder ( pNewFolder )
398{
399}
400
405
407{
408 if ( bFolder )
409 pFolder->clearParent();
410 else
411 pStream->clearParent();
412}
413
414/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
#define PKG_SIZE_NOENCR_MNFST
#define PKG_MNFST_MEDIATYPE
#define PKG_MNFST_VERSION
#define PKG_MNFST_FULLPATH
void * rtlRandomPool
#define THROW_WHERE
void writeLOC(ZipEntry *pEntry, bool bEncrypt=false)
void rawCloseEntry(bool bEncrypt=false)
static void setEntry(ZipEntry *pEntry)
virtual OUString SAL_CALL getName() override
bool IsFolder() const
virtual void SAL_CALL setParent(const css::uno::Reference< css::uno::XInterface > &Parent) override
virtual void SAL_CALL setName(const OUString &aName) override
virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override
void setChildStreamsTypeByExtension(const css::beans::StringPair &aPair)
virtual css::uno::Any SAL_CALL getByName(const OUString &aName) override
virtual void SAL_CALL insertByName(const OUString &aName, const css::uno::Any &aElement) override
virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration() override
virtual sal_Bool SAL_CALL hasByName(const OUString &aName) override
virtual void SAL_CALL replaceByName(const OUString &aName, const css::uno::Any &aElement) override
void doInsertByName(ZipPackageEntry *pEntry, bool bSetParent)
virtual void SAL_CALL removeByName(const OUString &Name) override
virtual ~ZipPackageFolder() override
virtual OUString SAL_CALL getImplementationName() override
virtual bool saveChild(const OUString &rPath, std::vector< css::uno::Sequence< css::beans::PropertyValue > > &rManList, ZipOutputStream &rZipOut, const css::uno::Sequence< sal_Int8 > &rEncryptionKey, sal_Int32 nPBKDF2IterationCount, const rtlRandomPool &rRandomPool) override
ZipContentInfo & doGetByName(const OUString &aName)
void saveContents(const OUString &rPath, std::vector< css::uno::Sequence< css::beans::PropertyValue > > &rManList, ZipOutputStream &rZipOut, const css::uno::Sequence< sal_Int8 > &rEncryptionKey, sal_Int32 nPBKDF2IterationCount, const rtlRandomPool &rRandomPool) const
bool LookForUnexpectedODF12Streams(std::u16string_view aPath)
virtual void SAL_CALL setPropertyValue(const OUString &aPropertyName, const css::uno::Any &aValue) override
virtual css::uno::Any SAL_CALL getPropertyValue(const OUString &PropertyName) override
ContentHash maContents
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
virtual css::uno::Type SAL_CALL getElementType() override
ZipPackageFolder(const css::uno::Reference< css::uno::XComponentContext > &xContext, sal_Int32 nFormat, bool bAllowRemoveOnInsert)
const OUString & GetVersion() const
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual sal_Bool SAL_CALL hasElements() override
css::uno::Type const & get()
float u
OUString aName
css::uno::Sequence< typename M::key_type > mapKeysToSequence(M const &map)
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
sal_uInt16 const m_nFormat
ZipPackageFolder * pFolder
ZipContentInfo & operator=(const ZipContentInfo &)
ZipContentInfo(ZipPackageStream *pNewStream)
ZipPackageStream * pStream
sal_Int16 nExtraLen
Definition: ZipEntry.hxx:35
OUString sPath
Definition: ZipEntry.hxx:36
sal_Int16 nPathLen
Definition: ZipEntry.hxx:34
OUString Name
unsigned char sal_Bool