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 <string.h>
21
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>
41
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;
50 using namespace cppu;
51
52 #if OSL_DEBUG_LEVEL > 0
53 #define THROW_WHERE SAL_WHERE
54 #else
55 #define THROW_WHERE ""
56 #endif
57
58 namespace { struct lcl_CachedImplId : public rtl::Static< cppu::OImplementationId, lcl_CachedImplId > {}; }
59
ZipPackageFolder(const css::uno::Reference<css::uno::XComponentContext> & xContext,sal_Int32 nFormat,bool bAllowRemoveOnInsert)60 ZipPackageFolder::ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext,
61 sal_Int32 nFormat,
62 bool bAllowRemoveOnInsert )
63 {
64 m_xContext = xContext;
65 m_nFormat = nFormat;
66 mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
67 SetFolder ( true );
68 aEntry.nVersion = -1;
69 aEntry.nFlag = 0;
70 aEntry.nMethod = STORED;
71 aEntry.nTime = -1;
72 aEntry.nCrc = 0;
73 aEntry.nCompressedSize = 0;
74 aEntry.nSize = 0;
75 aEntry.nOffset = -1;
76 }
77
~ZipPackageFolder()78 ZipPackageFolder::~ZipPackageFolder()
79 {
80 }
81
LookForUnexpectedODF12Streams(const OUString & aPath)82 bool ZipPackageFolder::LookForUnexpectedODF12Streams( const OUString& aPath )
83 {
84 bool bHasUnexpected = false;
85
86 for (const auto& [rShortName, rxInfo] : maContents)
87 {
88 const ZipContentInfo &rInfo = *rxInfo;
89
90 if ( rInfo.bFolder )
91 {
92 if ( aPath == "META-INF/" )
93 {
94 // META-INF is not allowed to contain subfolders
95 bHasUnexpected = true;
96 }
97 else
98 {
99 OUString sOwnPath = aPath + rShortName + "/";
100 bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams( sOwnPath );
101 }
102 }
103 else
104 {
105 if ( aPath == "META-INF/" )
106 {
107 if ( rShortName != "manifest.xml"
108 && rShortName.indexOf( "signatures" ) == -1 )
109 {
110 // a stream from META-INF with unexpected name
111 bHasUnexpected = true;
112 }
113
114 // streams from META-INF with expected names are allowed not to be registered in manifest.xml
115 }
116 else if ( !rInfo.pStream->IsFromManifest() )
117 {
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" )
121 {
122 // if it is not "mimetype" from the root it is not a part of the package
123 bHasUnexpected = true;
124 }
125 }
126 }
127
128 if (bHasUnexpected)
129 break;
130 }
131
132 return bHasUnexpected;
133 }
134
setChildStreamsTypeByExtension(const beans::StringPair & aPair)135 void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair )
136 {
137 OUString aExt;
138 if ( aPair.First.toChar() == '.' )
139 aExt = aPair.First;
140 else
141 aExt = "." + aPair.First;
142
143 for (const auto& [rShortName, rxInfo] : maContents)
144 {
145 const ZipContentInfo &rInfo = *rxInfo;
146
147 if ( rInfo.bFolder )
148 rInfo.pFolder->setChildStreamsTypeByExtension( aPair );
149 else
150 {
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 );
155 }
156 }
157 }
158
getUnoTunnelId()159 css::uno::Sequence < sal_Int8 > ZipPackageFolder::getUnoTunnelId()
160 {
161 return lcl_CachedImplId::get().getImplementationId();
162 }
163
164 // XNameContainer
insertByName(const OUString & aName,const uno::Any & aElement)165 void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement )
166 {
167 if (hasByName(aName))
168 throw ElementExistException(THROW_WHERE );
169
170 uno::Reference < XUnoTunnel > xRef;
171 aElement >>= xRef;
172 if ( !(aElement >>= xRef) )
173 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
174
175 sal_Int64 nTest;
176 ZipPackageEntry *pEntry;
177 if ( ( nTest = xRef->getSomething ( ZipPackageFolder::getUnoTunnelId() ) ) != 0 )
178 {
179 ZipPackageFolder *pFolder = reinterpret_cast < ZipPackageFolder * > ( nTest );
180 pEntry = static_cast < ZipPackageEntry * > ( pFolder );
181 }
182 else if ( ( nTest = xRef->getSomething ( ZipPackageStream::getUnoTunnelId() ) ) != 0 )
183 {
184 ZipPackageStream *pStream = reinterpret_cast < ZipPackageStream * > ( nTest );
185 pEntry = static_cast < ZipPackageEntry * > ( pStream );
186 }
187 else
188 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
189
190 if (pEntry->getName() != aName )
191 pEntry->setName (aName);
192 doInsertByName ( pEntry, true );
193 }
194
removeByName(const OUString & Name)195 void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name )
196 {
197 ContentHash::iterator aIter = maContents.find ( Name );
198 if ( aIter == maContents.end() )
199 throw NoSuchElementException(THROW_WHERE );
200 maContents.erase( aIter );
201 }
202 // XEnumerationAccess
createEnumeration()203 uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( )
204 {
205 return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents));
206 }
207 // XElementAccess
getElementType()208 uno::Type SAL_CALL ZipPackageFolder::getElementType( )
209 {
210 return cppu::UnoType<XUnoTunnel>::get();
211 }
hasElements()212 sal_Bool SAL_CALL ZipPackageFolder::hasElements( )
213 {
214 return !maContents.empty();
215 }
216 // XNameAccess
doGetByName(const OUString & aName)217 ZipContentInfo& ZipPackageFolder::doGetByName( const OUString& aName )
218 {
219 ContentHash::iterator aIter = maContents.find ( aName );
220 if ( aIter == maContents.end())
221 throw NoSuchElementException(THROW_WHERE );
222 return *aIter->second;
223 }
224
getByName(const OUString & aName)225 uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName )
226 {
227 return uno::makeAny ( doGetByName ( aName ).xTunnel );
228 }
getElementNames()229 uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( )
230 {
231 return comphelper::mapKeysToSequence(maContents);
232 }
hasByName(const OUString & aName)233 sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName )
234 {
235 return maContents.find ( aName ) != maContents.end ();
236 }
237 // XNameReplace
replaceByName(const OUString & aName,const uno::Any & aElement)238 void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement )
239 {
240 if ( !hasByName( aName ) )
241 throw NoSuchElementException(THROW_WHERE );
242
243 removeByName( aName );
244 insertByName(aName, aElement);
245 }
246
saveChild(const OUString & rPath,std::vector<uno::Sequence<PropertyValue>> & rManList,ZipOutputStream & rZipOut,const uno::Sequence<sal_Int8> & rEncryptionKey,sal_Int32 nPBKDF2IterationCount,const rtlRandomPool & rRandomPool)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)
254 {
255 const OUString sMediaTypeProperty ("MediaType");
256 const OUString sVersionProperty ("Version");
257 const OUString sFullPathProperty ("FullPath");
258
259 uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
260 OUString sTempName = rPath + "/";
261
262 if ( !GetMediaType().isEmpty() )
263 {
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;
270 }
271 else
272 aPropSet.realloc( 0 );
273
274 saveContents( sTempName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool);
275
276 // folder can have a mediatype only in package format
277 if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) )
278 rManList.push_back( aPropSet );
279
280 return true;
281 }
282
saveContents(const OUString & rPath,std::vector<uno::Sequence<PropertyValue>> & rManList,ZipOutputStream & rZipOut,const uno::Sequence<sal_Int8> & rEncryptionKey,sal_Int32 nPBKDF2IterationCount,const rtlRandomPool & rRandomPool) const283 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
290 {
291 if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
292 {
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;
298
299 try
300 {
301 ZipOutputStream::setEntry(pTempEntry);
302 rZipOut.writeLOC(pTempEntry);
303 rZipOut.rawCloseEntry();
304 }
305 catch ( ZipException& )
306 {
307 throw uno::RuntimeException( THROW_WHERE );
308 }
309 catch ( IOException& )
310 {
311 throw uno::RuntimeException( THROW_WHERE );
312 }
313 }
314
315 bool bMimeTypeStreamStored = false;
316 OUString aMimeTypeStreamName("mimetype");
317 if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() )
318 {
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 )
322 {
323 bMimeTypeStreamStored = true;
324 if( !aIter->second->pStream->saveChild(
325 rPath + aIter->first, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
326 {
327 throw uno::RuntimeException( THROW_WHERE );
328 }
329 }
330 }
331
332 for (const auto& [rShortName, rxInfo] : maContents)
333 {
334 const ZipContentInfo &rInfo = *rxInfo;
335
336 if ( !bMimeTypeStreamStored || rShortName != aMimeTypeStreamName )
337 {
338 if (rInfo.bFolder)
339 {
340 if( !rInfo.pFolder->saveChild(
341 rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
342 {
343 throw uno::RuntimeException( THROW_WHERE );
344 }
345 }
346 else
347 {
348 if( !rInfo.pStream->saveChild(
349 rPath + rShortName, rManList, rZipOut, rEncryptionKey, nPBKDF2IterationCount, rRandomPool ))
350 {
351 throw uno::RuntimeException( THROW_WHERE );
352 }
353 }
354 }
355 }
356 }
357
getSomething(const uno::Sequence<sal_Int8> & aIdentifier)358 sal_Int64 SAL_CALL ZipPackageFolder::getSomething( const uno::Sequence< sal_Int8 >& aIdentifier )
359 {
360 sal_Int64 nMe = 0;
361 if ( isUnoTunnelId<ZipPackageFolder>(aIdentifier) )
362 nMe = reinterpret_cast < sal_Int64 > ( this );
363 return nMe;
364 }
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)365 void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
366 {
367 if ( aPropertyName == "MediaType" )
368 {
369 // TODO/LATER: activate when zip ucp is ready
370 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
371 // throw UnknownPropertyException(THROW_WHERE );
372
373 aValue >>= msMediaType;
374 }
375 else if ( aPropertyName == "Version" )
376 aValue >>= m_sVersion;
377 else if ( aPropertyName == "Size" )
378 aValue >>= aEntry.nSize;
379 else
380 throw UnknownPropertyException(aPropertyName);
381 }
getPropertyValue(const OUString & PropertyName)382 uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName )
383 {
384 if ( PropertyName == "MediaType" )
385 {
386 // TODO/LATER: activate when zip ucp is ready
387 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
388 // throw UnknownPropertyException(THROW_WHERE );
389
390 return uno::makeAny ( msMediaType );
391 }
392 else if ( PropertyName == "Version" )
393 return uno::makeAny( m_sVersion );
394 else if ( PropertyName == "Size" )
395 return uno::makeAny ( aEntry.nSize );
396 else
397 throw UnknownPropertyException(PropertyName);
398 }
399
doInsertByName(ZipPackageEntry * pEntry,bool bSetParent)400 void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent )
401 {
402 if ( pEntry->IsFolder() )
403 maContents[pEntry->getName()] = std::make_unique<ZipContentInfo>(static_cast<ZipPackageFolder*>(pEntry));
404 else
405 maContents[pEntry->getName()] = std::make_unique<ZipContentInfo>(static_cast<ZipPackageStream*>(pEntry));
406 if ( bSetParent )
407 pEntry->setParent ( *this );
408 }
409
getImplementationName()410 OUString ZipPackageFolder::getImplementationName()
411 {
412 return "ZipPackageFolder";
413 }
414
getSupportedServiceNames()415 uno::Sequence< OUString > ZipPackageFolder::getSupportedServiceNames()
416 {
417 uno::Sequence< OUString > aNames { "com.sun.star.packages.PackageFolder" };
418 return aNames;
419 }
420
supportsService(OUString const & rServiceName)421 sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName )
422 {
423 return cppu::supportsService(this, rServiceName);
424 }
425
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
427