/src/libreoffice/package/source/zippackage/ZipPackageFolder.cxx
Line | Count | Source |
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> |
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> |
34 | | |
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; |
44 | | |
45 | | ZipPackageFolder::ZipPackageFolder( const css::uno::Reference < css::uno::XComponentContext >& xContext, |
46 | | sal_Int32 nFormat, |
47 | | bool bAllowRemoveOnInsert ) |
48 | 311k | { |
49 | 311k | m_xContext = xContext; |
50 | 311k | m_nFormat = nFormat; |
51 | 311k | mbAllowRemoveOnInsert = bAllowRemoveOnInsert; |
52 | 311k | SetFolder ( true ); |
53 | 311k | aEntry.nVersion = -1; |
54 | 311k | aEntry.nFlag = 0; |
55 | 311k | aEntry.nMethod = STORED; |
56 | 311k | aEntry.nTime = -1; |
57 | 311k | aEntry.nCrc = 0; |
58 | 311k | aEntry.nCompressedSize = 0; |
59 | 311k | aEntry.nSize = 0; |
60 | 311k | aEntry.nOffset = -1; |
61 | 311k | } |
62 | | |
63 | | ZipPackageFolder::~ZipPackageFolder() |
64 | 310k | { |
65 | 310k | } |
66 | | |
67 | | bool ZipPackageFolder::LookForUnexpectedODF12Streams( |
68 | | std::u16string_view const aPath, bool const isWholesomeEncryption) |
69 | 0 | { |
70 | 0 | bool bHasUnexpected = false; |
71 | |
|
72 | 0 | for (const auto& [rShortName, rInfo] : maContents) |
73 | 0 | { |
74 | 0 | if ( rInfo.bFolder ) |
75 | 0 | { |
76 | 0 | if ( aPath == u"META-INF/" ) |
77 | 0 | { |
78 | | // META-INF is not allowed to contain subfolders |
79 | 0 | bHasUnexpected = true; |
80 | 0 | } |
81 | 0 | else if (isWholesomeEncryption && rShortName != u"META-INF") |
82 | 0 | { |
83 | 0 | bHasUnexpected = true; |
84 | 0 | } |
85 | 0 | else |
86 | 0 | { |
87 | 0 | OUString sOwnPath = aPath + rShortName + "/"; |
88 | 0 | bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams(sOwnPath, isWholesomeEncryption); |
89 | 0 | } |
90 | 0 | } |
91 | 0 | else |
92 | 0 | { |
93 | 0 | if ( aPath == u"META-INF/" ) |
94 | 0 | { |
95 | 0 | if ( rShortName != "manifest.xml" |
96 | 0 | && rShortName.indexOf( "signatures" ) == -1 ) |
97 | 0 | { |
98 | | // a stream from META-INF with unexpected name |
99 | 0 | bHasUnexpected = true; |
100 | 0 | } |
101 | | |
102 | | // streams from META-INF with expected names are allowed not to be registered in manifest.xml |
103 | 0 | } |
104 | 0 | else if (isWholesomeEncryption && rShortName != "mimetype" && rShortName != "encrypted-package") |
105 | 0 | { |
106 | 0 | bHasUnexpected = true; |
107 | 0 | } |
108 | 0 | else if ( !rInfo.pStream->IsFromManifest() ) |
109 | 0 | { |
110 | | // the stream is not in META-INF and is not registered in manifest.xml, |
111 | | // check whether it is an internal part of the package format |
112 | 0 | if ( !aPath.empty() || rShortName != "mimetype" ) |
113 | 0 | { |
114 | | // if it is not "mimetype" from the root it is not a part of the package |
115 | 0 | bHasUnexpected = true; |
116 | 0 | } |
117 | 0 | } |
118 | 0 | } |
119 | |
|
120 | 0 | if (bHasUnexpected) |
121 | 0 | break; |
122 | 0 | } |
123 | |
|
124 | 0 | return bHasUnexpected; |
125 | 0 | } |
126 | | |
127 | | void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair ) |
128 | 126k | { |
129 | 126k | OUString aExt; |
130 | 126k | if ( aPair.First.toChar() == '.' ) |
131 | 0 | aExt = aPair.First; |
132 | 126k | else |
133 | 126k | aExt = "." + aPair.First; |
134 | | |
135 | 126k | for (const auto& [rShortName, rInfo] : maContents) |
136 | 381k | { |
137 | 381k | if ( rInfo.bFolder ) |
138 | 114k | rInfo.pFolder->setChildStreamsTypeByExtension( aPair ); |
139 | 267k | else |
140 | 267k | { |
141 | 267k | sal_Int32 nPathLength = rShortName.getLength(); |
142 | 267k | sal_Int32 nExtLength = aExt.getLength(); |
143 | 267k | if ( nPathLength >= nExtLength && rShortName.match( aExt, nPathLength - nExtLength ) ) |
144 | 81.1k | rInfo.pStream->SetMediaType( aPair.Second ); |
145 | 267k | } |
146 | 381k | } |
147 | 126k | } |
148 | | |
149 | | // XNameContainer |
150 | | void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement ) |
151 | 0 | { |
152 | 0 | if (hasByName(aName)) |
153 | 0 | throw ElementExistException(); |
154 | | |
155 | 0 | uno::Reference < XInterface > xRef; |
156 | 0 | if ( !(aElement >>= xRef) ) |
157 | 0 | throw IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 0 ); |
158 | | |
159 | 0 | ZipPackageEntry* pEntry = dynamic_cast<ZipPackageFolder*>(xRef.get()); |
160 | 0 | if (!pEntry) |
161 | 0 | pEntry = dynamic_cast<ZipPackageStream*>(xRef.get()); |
162 | 0 | if (!pEntry) |
163 | 0 | throw IllegalArgumentException(u""_ustr, uno::Reference< uno::XInterface >(), 0 ); |
164 | | |
165 | 0 | if (pEntry->getName() != aName ) |
166 | 0 | pEntry->setName (aName); |
167 | 0 | doInsertByName ( pEntry, true ); |
168 | 0 | } |
169 | | |
170 | | void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name ) |
171 | 0 | { |
172 | 0 | return removeByName(std::u16string_view(Name)); |
173 | 0 | } |
174 | | |
175 | | void ZipPackageFolder::removeByName( std::u16string_view aName ) |
176 | 11.0k | { |
177 | 11.0k | ContentHash::iterator aIter = maContents.find ( aName ); |
178 | 11.0k | if ( aIter == maContents.end() ) |
179 | 0 | throw NoSuchElementException(); |
180 | 11.0k | maContents.erase( aIter ); |
181 | 11.0k | } |
182 | | // XEnumerationAccess |
183 | | uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( ) |
184 | 240k | { |
185 | 240k | return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents)); |
186 | 240k | } |
187 | | // XElementAccess |
188 | | uno::Type SAL_CALL ZipPackageFolder::getElementType( ) |
189 | 0 | { |
190 | 0 | return cppu::UnoType<XInterface>::get(); |
191 | 0 | } |
192 | | sal_Bool SAL_CALL ZipPackageFolder::hasElements( ) |
193 | 0 | { |
194 | 0 | return !maContents.empty(); |
195 | 0 | } |
196 | | // XNameAccess |
197 | | ZipContentInfo& ZipPackageFolder::doGetByName( std::u16string_view aName ) |
198 | 446k | { |
199 | 446k | ContentHash::iterator aIter = maContents.find ( aName ); |
200 | 446k | if ( aIter == maContents.end()) |
201 | 0 | throw NoSuchElementException(); |
202 | 446k | return aIter->second; |
203 | 446k | } |
204 | | |
205 | | uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName ) |
206 | 221k | { |
207 | 221k | return getByName(std::u16string_view(aName)); |
208 | 221k | } |
209 | | uno::Any ZipPackageFolder::getByName( std::u16string_view aName ) |
210 | 335k | { |
211 | 335k | return uno::Any ( uno::Reference(cppu::getXWeak(doGetByName ( aName ).xPackageEntry.get())) ); |
212 | 335k | } |
213 | | uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( ) |
214 | 0 | { |
215 | 0 | return comphelper::mapKeysToSequence(maContents); |
216 | 0 | } |
217 | | sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName ) |
218 | 481k | { |
219 | 481k | return hasByName( std::u16string_view( aName )); |
220 | 481k | } |
221 | | bool ZipPackageFolder::hasByName( std::u16string_view aName ) |
222 | 1.29M | { |
223 | 1.29M | return maContents.find ( aName ) != maContents.end (); |
224 | 1.29M | } |
225 | | // XNameReplace |
226 | | void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement ) |
227 | 0 | { |
228 | 0 | if ( !hasByName( aName ) ) |
229 | 0 | throw NoSuchElementException(); |
230 | | |
231 | 0 | removeByName( aName ); |
232 | 0 | insertByName(aName, aElement); |
233 | 0 | } |
234 | | |
235 | | bool ZipPackageFolder::saveChild( |
236 | | const OUString &rPath, |
237 | | std::vector < uno::Sequence < PropertyValue > > &rManList, |
238 | | ZipOutputStream & rZipOut, |
239 | | const uno::Sequence < sal_Int8 >& rEncryptionKey, |
240 | | ::std::optional<sal_Int32> const oPBKDF2IterationCount, |
241 | | ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args) |
242 | 0 | { |
243 | 0 | uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST); |
244 | 0 | OUString sTempName = rPath + "/"; |
245 | |
|
246 | 0 | if ( !GetMediaType().isEmpty() ) |
247 | 0 | { |
248 | 0 | auto pPropSet = aPropSet.getArray(); |
249 | 0 | pPropSet[PKG_MNFST_MEDIATYPE].Name = "MediaType"; |
250 | 0 | pPropSet[PKG_MNFST_MEDIATYPE].Value <<= GetMediaType(); |
251 | 0 | pPropSet[PKG_MNFST_VERSION].Name = "Version"; |
252 | 0 | pPropSet[PKG_MNFST_VERSION].Value <<= GetVersion(); |
253 | 0 | pPropSet[PKG_MNFST_FULLPATH].Name = "FullPath"; |
254 | 0 | pPropSet[PKG_MNFST_FULLPATH].Value <<= sTempName; |
255 | 0 | } |
256 | 0 | else |
257 | 0 | aPropSet.realloc( 0 ); |
258 | |
|
259 | 0 | saveContents(sTempName, rManList, rZipOut, rEncryptionKey, oPBKDF2IterationCount, oArgon2Args); |
260 | | |
261 | | // folder can have a mediatype only in package format |
262 | 0 | if ( aPropSet.hasElements() && ( m_nFormat == embed::StorageFormats::PACKAGE ) ) |
263 | 0 | rManList.push_back( aPropSet ); |
264 | |
|
265 | 0 | return true; |
266 | 0 | } |
267 | | |
268 | | void ZipPackageFolder::saveContents( |
269 | | const OUString &rPath, |
270 | | std::vector < uno::Sequence < PropertyValue > > &rManList, |
271 | | ZipOutputStream & rZipOut, |
272 | | const uno::Sequence < sal_Int8 >& rEncryptionKey, |
273 | | ::std::optional<sal_Int32> const oPBKDF2IterationCount, |
274 | | ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> const oArgon2Args) const |
275 | 0 | { |
276 | 0 | if ( maContents.empty() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML ) |
277 | 0 | { |
278 | | // it is an empty subfolder, use workaround to store it |
279 | 0 | auto pTempEntry = std::make_unique<ZipEntry>(aEntry); |
280 | 0 | pTempEntry->nPathLen = static_cast<sal_Int16>( OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getLength() ); |
281 | 0 | pTempEntry->nExtraLen = -1; |
282 | 0 | pTempEntry->sPath = rPath; |
283 | |
|
284 | 0 | try |
285 | 0 | { |
286 | 0 | ZipOutputStream::setEntry(*pTempEntry); |
287 | 0 | rZipOut.writeLOC(std::move(pTempEntry)); |
288 | 0 | rZipOut.rawCloseEntry(); |
289 | 0 | } |
290 | 0 | catch ( ZipException& ) |
291 | 0 | { |
292 | 0 | throw uno::RuntimeException(); |
293 | 0 | } |
294 | 0 | catch ( IOException& ) |
295 | 0 | { |
296 | 0 | throw uno::RuntimeException(); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | 0 | bool bMimeTypeStreamStored = false; |
301 | 0 | OUString aMimeTypeStreamName(u"mimetype"_ustr); |
302 | 0 | if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() ) |
303 | 0 | { |
304 | | // let the "mimetype" stream in root folder be stored as the first stream if it is zip format |
305 | 0 | ContentHash::const_iterator aIter = maContents.find ( aMimeTypeStreamName ); |
306 | 0 | if ( aIter != maContents.end() && !(*aIter).second.bFolder ) |
307 | 0 | { |
308 | 0 | bMimeTypeStreamStored = true; |
309 | 0 | if (!aIter->second.pStream->saveChild(rPath + aIter->first, rManList, rZipOut, |
310 | 0 | rEncryptionKey, oPBKDF2IterationCount, oArgon2Args)) |
311 | 0 | { |
312 | 0 | throw uno::RuntimeException(); |
313 | 0 | } |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | 0 | for (const auto& [rShortName, rInfo] : maContents) |
318 | 0 | { |
319 | 0 | if ( !bMimeTypeStreamStored || rShortName != aMimeTypeStreamName ) |
320 | 0 | { |
321 | 0 | if (rInfo.bFolder) |
322 | 0 | { |
323 | 0 | if (!rInfo.pFolder->saveChild(rPath + rShortName, rManList, rZipOut, |
324 | 0 | rEncryptionKey, oPBKDF2IterationCount, oArgon2Args)) |
325 | 0 | { |
326 | 0 | throw uno::RuntimeException(); |
327 | 0 | } |
328 | 0 | } |
329 | 0 | else |
330 | 0 | { |
331 | 0 | if (!rInfo.pStream->saveChild(rPath + rShortName, rManList, rZipOut, |
332 | 0 | rEncryptionKey, oPBKDF2IterationCount, oArgon2Args)) |
333 | 0 | { |
334 | 0 | throw uno::RuntimeException(); |
335 | 0 | } |
336 | 0 | } |
337 | 0 | } |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) |
342 | 0 | { |
343 | 0 | if ( aPropertyName == "MediaType" ) |
344 | 0 | { |
345 | | // TODO/LATER: activate when zip ucp is ready |
346 | | // if ( m_nFormat != embed::StorageFormats::PACKAGE ) |
347 | | // throw UnknownPropertyException(); |
348 | |
|
349 | 0 | aValue >>= msMediaType; |
350 | 0 | } |
351 | 0 | else if ( aPropertyName == "Version" ) |
352 | 0 | aValue >>= m_sVersion; |
353 | 0 | else if ( aPropertyName == "Size" ) |
354 | 0 | aValue >>= aEntry.nSize; |
355 | 0 | else |
356 | 0 | throw UnknownPropertyException(aPropertyName); |
357 | 0 | } |
358 | | uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName ) |
359 | 53.8k | { |
360 | 53.8k | if ( PropertyName == "MediaType" ) |
361 | 26.9k | { |
362 | | // TODO/LATER: activate when zip ucp is ready |
363 | | // if ( m_nFormat != embed::StorageFormats::PACKAGE ) |
364 | | // throw UnknownPropertyException(); |
365 | | |
366 | 26.9k | return uno::Any ( msMediaType ); |
367 | 26.9k | } |
368 | 26.9k | else if ( PropertyName == "Version" ) |
369 | 26.9k | return uno::Any( m_sVersion ); |
370 | 0 | else if ( PropertyName == "Size" ) |
371 | 0 | return uno::Any ( aEntry.nSize ); |
372 | 0 | else |
373 | 0 | throw UnknownPropertyException(PropertyName); |
374 | 53.8k | } |
375 | | |
376 | | void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, bool bSetParent ) |
377 | 481k | { |
378 | 481k | if ( pEntry->IsFolder() ) |
379 | 143k | maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageFolder*>(pEntry))); |
380 | 338k | else |
381 | 338k | maContents.emplace(pEntry->getName(), ZipContentInfo(static_cast<ZipPackageStream*>(pEntry))); |
382 | 481k | if ( bSetParent ) |
383 | 0 | pEntry->setParent ( *this ); |
384 | 481k | } |
385 | | |
386 | | OUString ZipPackageFolder::getImplementationName() |
387 | 0 | { |
388 | 0 | return u"ZipPackageFolder"_ustr; |
389 | 0 | } |
390 | | |
391 | | uno::Sequence< OUString > ZipPackageFolder::getSupportedServiceNames() |
392 | 0 | { |
393 | 0 | return { u"com.sun.star.packages.PackageFolder"_ustr }; |
394 | 0 | } |
395 | | |
396 | | sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName ) |
397 | 0 | { |
398 | 0 | return cppu::supportsService(this, rServiceName); |
399 | 0 | } |
400 | | |
401 | | |
402 | | ZipContentInfo::ZipContentInfo ( ZipPackageStream * pNewStream ) |
403 | 338k | : xPackageEntry ( pNewStream ) |
404 | 338k | , bFolder ( false ) |
405 | 338k | , pStream ( pNewStream ) |
406 | 338k | { |
407 | 338k | } |
408 | | |
409 | | ZipContentInfo::ZipContentInfo ( ZipPackageFolder * pNewFolder ) |
410 | 143k | : xPackageEntry ( pNewFolder ) |
411 | 143k | , bFolder ( true ) |
412 | 143k | , pFolder ( pNewFolder ) |
413 | 143k | { |
414 | 143k | } |
415 | | |
416 | 0 | ZipContentInfo::ZipContentInfo( const ZipContentInfo& ) = default; |
417 | 481k | ZipContentInfo::ZipContentInfo( ZipContentInfo&& ) = default; |
418 | 0 | ZipContentInfo& ZipContentInfo::operator=( const ZipContentInfo& ) = default; |
419 | 0 | ZipContentInfo& ZipContentInfo::operator=( ZipContentInfo&& ) = default; |
420 | | |
421 | | ZipContentInfo::~ZipContentInfo() |
422 | 963k | { |
423 | 963k | if ( bFolder ) |
424 | 286k | pFolder->clearParent(); |
425 | 677k | else |
426 | 677k | pStream->clearParent(); |
427 | 963k | } |
428 | | |
429 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |