Fix GNU C++ version check
[LibreOffice.git] / package / source / zippackage / ZipPackage.cxx
blob9d6aa47e319b693d3fa7a601cb82648585784511
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <ZipPackage.hxx>
21 #include "ZipPackageSink.hxx"
22 #include <ZipEnumeration.hxx>
23 #include <ZipPackageStream.hxx>
24 #include <ZipPackageFolder.hxx>
25 #include <ZipOutputEntry.hxx>
26 #include <ZipOutputStream.hxx>
27 #include <ZipPackageBuffer.hxx>
28 #include <ZipFile.hxx>
29 #include <PackageConstants.hxx>
30 #include <com/sun/star/beans/PropertyValue.hpp>
31 #include <com/sun/star/packages/zip/ZipConstants.hpp>
32 #include <com/sun/star/packages/zip/ZipException.hpp>
33 #include <com/sun/star/packages/zip/ZipIOException.hpp>
34 #include <com/sun/star/packages/manifest/ManifestReader.hpp>
35 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
36 #include <com/sun/star/io/TempFile.hpp>
37 #include <com/sun/star/io/XStream.hpp>
38 #include <com/sun/star/io/XInputStream.hpp>
39 #include <com/sun/star/io/XOutputStream.hpp>
40 #include <com/sun/star/io/XTruncate.hpp>
41 #include <com/sun/star/io/XSeekable.hpp>
42 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
43 #include <com/sun/star/container/XNameContainer.hpp>
44 #include <officecfg/Office/Common.hxx>
45 #include <comphelper/fileurl.hxx>
46 #include <comphelper/processfactory.hxx>
47 #include <ucbhelper/content.hxx>
48 #include <cppuhelper/exc_hlp.hxx>
49 #include <com/sun/star/ucb/ContentCreationException.hpp>
50 #include <com/sun/star/ucb/TransferInfo.hpp>
51 #include <com/sun/star/ucb/NameClash.hpp>
52 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
53 #include <com/sun/star/ucb/OpenMode.hpp>
54 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
55 #include <com/sun/star/io/XActiveDataStreamer.hpp>
56 #include <com/sun/star/embed/UseBackupException.hpp>
57 #include <com/sun/star/embed/StorageFormats.hpp>
58 #include <com/sun/star/beans/NamedValue.hpp>
59 #include <com/sun/star/xml/crypto/DigestID.hpp>
60 #include <com/sun/star/xml/crypto/KDFID.hpp>
61 #include <cppuhelper/implbase.hxx>
62 #include <rtl/uri.hxx>
63 #include <rtl/random.h>
64 #include <o3tl/string_view.hxx>
65 #include <osl/diagnose.h>
66 #include <sal/log.hxx>
67 #include <unotools/streamwrap.hxx>
68 #include <unotools/tempfile.hxx>
69 #include <com/sun/star/io/XAsyncOutputMonitor.hpp>
71 #include <string_view>
73 #include <comphelper/seekableinput.hxx>
74 #include <comphelper/storagehelper.hxx>
75 #include <comphelper/ofopxmlhelper.hxx>
76 #include <comphelper/documentconstants.hxx>
77 #include <comphelper/sequenceashashmap.hxx>
78 #include <cppuhelper/supportsservice.hxx>
79 #include <comphelper/sequence.hxx>
80 #include <comphelper/servicehelper.hxx>
81 #include <utility>
83 using namespace osl;
84 using namespace cppu;
85 using namespace ucbhelper;
86 using namespace com::sun::star;
87 using namespace com::sun::star::io;
88 using namespace com::sun::star::uno;
89 using namespace com::sun::star::ucb;
90 using namespace com::sun::star::util;
91 using namespace com::sun::star::lang;
92 using namespace com::sun::star::task;
93 using namespace com::sun::star::beans;
94 using namespace com::sun::star::packages;
95 using namespace com::sun::star::container;
96 using namespace com::sun::star::packages::zip;
97 using namespace com::sun::star::packages::manifest;
98 using namespace com::sun::star::packages::zip::ZipConstants;
100 #if OSL_DEBUG_LEVEL > 0
101 #define THROW_WHERE SAL_WHERE
102 #else
103 #define THROW_WHERE ""
104 #endif
106 namespace {
108 class ActiveDataStreamer : public ::cppu::WeakImplHelper< XActiveDataStreamer >
110 uno::Reference< XStream > mStream;
111 public:
113 virtual uno::Reference< XStream > SAL_CALL getStream() override
114 { return mStream; }
116 virtual void SAL_CALL setStream( const uno::Reference< XStream >& stream ) override
117 { mStream = stream; }
120 class DummyInputStream : public ::cppu::WeakImplHelper< XInputStream >
122 virtual sal_Int32 SAL_CALL readBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
123 { return 0; }
125 virtual sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
126 { return 0; }
128 virtual void SAL_CALL skipBytes( sal_Int32 ) override
131 virtual sal_Int32 SAL_CALL available() override
132 { return 0; }
134 virtual void SAL_CALL closeInput() override
138 } // namespace
140 sal_Int32 GetDefaultDerivedKeySize(sal_Int32 const nCipherID)
142 switch (nCipherID)
144 case css::xml::crypto::CipherID::BLOWFISH_CFB_8:
145 return 16;
146 case css::xml::crypto::CipherID::AES_CBC_W3C_PADDING:
147 case css::xml::crypto::CipherID::AES_GCM_W3C:
148 return 32;
149 default:
150 O3TL_UNREACHABLE;
154 ZipPackage::ZipPackage ( uno::Reference < XComponentContext > xContext )
155 : m_aMutexHolder( new comphelper::RefCountedMutex )
156 , m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1 )
157 , m_oChecksumDigestID( xml::crypto::DigestID::SHA1_1K )
158 , m_nKeyDerivationFunctionID(xml::crypto::KDFID::PBKDF2)
159 , m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8 )
160 , m_bHasEncryptedEntries ( false )
161 , m_bHasNonEncryptedEntries ( false )
162 , m_bInconsistent ( false )
163 , m_bForceRecovery ( false )
164 , m_bMediaTypeFallbackUsed ( false )
165 , m_nFormat( embed::StorageFormats::PACKAGE ) // package is the default format
166 , m_bAllowRemoveOnInsert( true )
167 , m_eMode ( e_IMode_None )
168 , m_xContext(std::move( xContext ))
170 m_xRootFolder = new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
173 ZipPackage::~ZipPackage()
177 bool ZipPackage::isLocalFile() const
179 return comphelper::isFileUrl(m_aURL);
182 // note: don't check for StorageFormats::ZIP, it breaks signing!
183 void ZipPackage::checkZipEntriesWithDD()
185 if (!m_bForceRecovery)
187 ZipEnumeration entries{m_pZipFile->entries()};
188 while (entries.hasMoreElements())
190 ZipEntry const& rEntry{*entries.nextElement()};
191 if ((rEntry.nFlag & 0x08) != 0 && rEntry.nMethod == STORED)
193 uno::Reference<XPropertySet> xStream;
194 getByHierarchicalName(rEntry.sPath) >>= xStream;
195 uno::Reference<XServiceInfo> const xStreamSI{xStream, uno::UNO_QUERY_THROW};
196 if (!xStreamSI->supportsService("com.sun.star.packages.PackageStream"))
198 SAL_INFO("package", "entry STORED with data descriptor is folder: \"" << rEntry.sPath << "\"");
199 throw ZipIOException(
200 THROW_WHERE
201 "entry STORED with data descriptor is folder");
203 if (!xStream->getPropertyValue("WasEncrypted").get<bool>())
205 SAL_INFO("package", "entry STORED with data descriptor but not encrypted: \"" << rEntry.sPath << "\"");
206 throw ZipIOException(
207 THROW_WHERE
208 "entry STORED with data descriptor but not encrypted");
215 void ZipPackage::parseManifest()
217 if ( m_nFormat != embed::StorageFormats::PACKAGE )
218 return;
220 bool bManifestParsed = false;
221 ::std::optional<OUString> oFirstVersion;
222 static constexpr OUString sMeta (u"META-INF"_ustr);
223 if ( m_xRootFolder->hasByName( sMeta ) )
225 try {
226 static constexpr OUString sManifest (u"manifest.xml"_ustr);
227 Any aAny = m_xRootFolder->getByName( sMeta );
228 uno::Reference< XNameContainer > xMetaInfFolder;
229 aAny >>= xMetaInfFolder;
230 if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
232 uno::Reference < XActiveDataSink > xSink;
233 aAny = xMetaInfFolder->getByName( sManifest );
234 aAny >>= xSink;
235 if ( xSink.is() )
237 uno::Reference < XManifestReader > xReader = ManifestReader::create( m_xContext );
239 const uno::Sequence < uno::Sequence < PropertyValue > > aManifestSequence = xReader->readManifestSequence ( xSink->getInputStream() );
240 const Any *pKeyInfo = nullptr;
242 for ( const uno::Sequence<PropertyValue>& rSequence : aManifestSequence )
244 OUString sPath, sMediaType, sVersion;
245 const Any *pSalt = nullptr, *pVector = nullptr, *pCount = nullptr, *pSize = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptionAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
246 uno::Any const* pKDF = nullptr;
247 uno::Any const* pArgon2Args = nullptr;
248 for ( const PropertyValue& rValue : rSequence )
250 if ( rValue.Name == u"FullPath" )
251 rValue.Value >>= sPath;
252 else if ( rValue.Name == u"Version" )
254 rValue.Value >>= sVersion;
255 if (!oFirstVersion)
257 oFirstVersion.emplace(sVersion);
260 else if ( rValue.Name == u"MediaType" )
261 rValue.Value >>= sMediaType;
262 else if ( rValue.Name == u"Salt" )
263 pSalt = &( rValue.Value );
264 else if ( rValue.Name == u"InitialisationVector" )
265 pVector = &( rValue.Value );
266 else if ( rValue.Name == u"IterationCount" )
267 pCount = &( rValue.Value );
268 else if ( rValue.Name == u"Size" )
269 pSize = &( rValue.Value );
270 else if ( rValue.Name == u"Digest" )
271 pDigest = &( rValue.Value );
272 else if ( rValue.Name == u"DigestAlgorithm" )
273 pDigestAlg = &( rValue.Value );
274 else if ( rValue.Name == u"EncryptionAlgorithm" )
275 pEncryptionAlg = &( rValue.Value );
276 else if ( rValue.Name == u"StartKeyAlgorithm" )
277 pStartKeyAlg = &( rValue.Value );
278 else if ( rValue.Name == u"DerivedKeySize" )
279 pDerivedKeySize = &( rValue.Value );
280 else if ( rValue.Name == u"KeyInfo" )
281 pKeyInfo = &( rValue.Value );
282 else if (rValue.Name == u"KeyDerivationFunction") {
283 pKDF = &rValue.Value;
284 } else if (rValue.Name == u"Argon2Args") {
285 pArgon2Args = &rValue.Value;
289 if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
291 aAny = getByHierarchicalName( sPath );
292 uno::Reference < XInterface > xTmp;
293 aAny >>= xTmp;
294 if (auto pFolder = dynamic_cast<ZipPackageFolder*>(xTmp.get()))
296 pFolder->SetMediaType ( sMediaType );
297 pFolder->SetVersion ( sVersion );
299 else if (auto pStream = dynamic_cast<ZipPackageStream*>(xTmp.get()))
301 pStream->SetMediaType ( sMediaType );
302 pStream->SetFromManifest( true );
304 if (pKeyInfo
305 && pVector && pSize && pEncryptionAlg
306 && pKDF && pKDF->has<sal_Int32>() && pKDF->get<sal_Int32>() == xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
307 && ((pEncryptionAlg->has<sal_Int32>()
308 && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
309 || (pDigest && pDigestAlg)))
311 uno::Sequence < sal_Int8 > aSequence;
312 sal_Int64 nSize = 0;
313 ::std::optional<sal_Int32> oDigestAlg;
314 sal_Int32 nEncryptionAlg = 0;
316 pStream->SetToBeEncrypted ( true );
318 *pVector >>= aSequence;
319 pStream->setInitialisationVector ( aSequence );
321 *pSize >>= nSize;
322 pStream->setSize ( nSize );
324 if (pDigest && pDigestAlg)
326 *pDigest >>= aSequence;
327 pStream->setDigest(aSequence);
329 assert(pDigestAlg->has<sal_Int32>());
330 oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
333 *pEncryptionAlg >>= nEncryptionAlg;
335 *pKeyInfo >>= m_aGpgProps;
337 pStream->SetToBeCompressed ( true );
338 pStream->SetToBeEncrypted ( true );
339 pStream->SetIsEncrypted ( true );
340 pStream->setIterationCount(::std::optional<sal_Int32>());
341 pStream->setArgon2Args(::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>>());
343 // clamp to default SHA256 start key magic value,
344 // c.f. ZipPackageStream::GetEncryptionKey()
345 // trying to get key value from properties
346 const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
348 pStream->SetImportedAlgorithms({
349 .nImportedStartKeyAlgorithm = nStartKeyAlg,
350 .nImportedEncryptionAlgorithm = nEncryptionAlg,
351 .oImportedChecksumAlgorithm = oDigestAlg,
352 // note m_nCommonEncryptionID is not inited yet here
353 .nImportedDerivedKeySize = ::GetDefaultDerivedKeySize(nEncryptionAlg),
356 if (!m_bHasEncryptedEntries
357 && (pStream->getName() == "content.xml"
358 || pStream->getName() == "encrypted-package"))
360 m_bHasEncryptedEntries = true;
361 m_oChecksumDigestID = oDigestAlg;
362 m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
363 m_nCommonEncryptionID = nEncryptionAlg;
364 m_nStartKeyGenerationID = nStartKeyAlg;
367 else if (pSalt
368 && pVector && pSize && pEncryptionAlg
369 && pKDF && pKDF->has<sal_Int32>()
370 && ((pKDF->get<sal_Int32>() == xml::crypto::KDFID::PBKDF2 && pCount)
371 || (pKDF->get<sal_Int32>() == xml::crypto::KDFID::Argon2id && pArgon2Args))
372 && ((pEncryptionAlg->has<sal_Int32>()
373 && pEncryptionAlg->get<sal_Int32>() == xml::crypto::CipherID::AES_GCM_W3C)
374 || (pDigest && pDigestAlg)))
377 uno::Sequence < sal_Int8 > aSequence;
378 sal_Int64 nSize = 0;
379 ::std::optional<sal_Int32> oDigestAlg;
380 sal_Int32 nKDF = 0;
381 sal_Int32 nEncryptionAlg = 0;
382 sal_Int32 nCount = 0;
383 sal_Int32 nDerivedKeySize = 16, nStartKeyAlg = xml::crypto::DigestID::SHA1;
385 pStream->SetToBeEncrypted ( true );
387 *pSalt >>= aSequence;
388 pStream->setSalt ( aSequence );
390 *pVector >>= aSequence;
391 pStream->setInitialisationVector ( aSequence );
393 *pKDF >>= nKDF;
395 if (pCount)
397 *pCount >>= nCount;
398 pStream->setIterationCount(::std::optional<sal_Int32>(nCount));
401 if (pArgon2Args)
403 uno::Sequence<sal_Int32> args;
404 *pArgon2Args >>= args;
405 assert(args.getLength() == 3);
406 ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgs;
407 oArgs.emplace(args[0], args[1], args[2]);
408 pStream->setArgon2Args(oArgs);
411 *pSize >>= nSize;
412 pStream->setSize ( nSize );
414 if (pDigest && pDigestAlg)
416 *pDigest >>= aSequence;
417 pStream->setDigest(aSequence);
419 assert(pDigestAlg->has<sal_Int32>());
420 oDigestAlg.emplace(pDigestAlg->get<sal_Int32>());
423 *pEncryptionAlg >>= nEncryptionAlg;
425 if ( pDerivedKeySize )
426 *pDerivedKeySize >>= nDerivedKeySize;
428 if ( pStartKeyAlg )
429 *pStartKeyAlg >>= nStartKeyAlg;
431 pStream->SetImportedAlgorithms({
432 .nImportedStartKeyAlgorithm = nStartKeyAlg,
433 .nImportedEncryptionAlgorithm = nEncryptionAlg,
434 .oImportedChecksumAlgorithm = oDigestAlg,
435 .nImportedDerivedKeySize = nDerivedKeySize,
438 pStream->SetToBeCompressed ( true );
439 pStream->SetToBeEncrypted ( true );
440 pStream->SetIsEncrypted ( true );
441 if (!m_bHasEncryptedEntries
442 && (pStream->getName() == "content.xml"
443 || pStream->getName() == "encrypted-package"))
445 m_bHasEncryptedEntries = true;
446 m_nStartKeyGenerationID = nStartKeyAlg;
447 m_nKeyDerivationFunctionID = nKDF;
448 m_oChecksumDigestID = oDigestAlg;
449 m_nCommonEncryptionID = nEncryptionAlg;
452 else
453 m_bHasNonEncryptedEntries = true;
455 else
456 throw ZipIOException(THROW_WHERE "Wrong content");
460 bManifestParsed = true;
463 checkZipEntriesWithDD(); // check before removing entries!
465 // now hide the manifest.xml file from user
466 xMetaInfFolder->removeByName( sManifest );
469 catch( Exception& )
471 if ( !m_bForceRecovery )
472 throw;
476 if ( !bManifestParsed && !m_bForceRecovery )
477 throw ZipIOException(
478 THROW_WHERE "Could not parse manifest.xml" );
480 static constexpr OUString sMimetype (u"mimetype"_ustr);
481 if ( m_xRootFolder->hasByName( sMimetype ) )
483 // get mediatype from the "mimetype" stream
484 OUString aPackageMediatype;
485 uno::Reference < io::XActiveDataSink > xMimeSink;
486 m_xRootFolder->getByName( sMimetype ) >>= xMimeSink;
487 if ( xMimeSink.is() )
489 uno::Reference< io::XInputStream > xMimeInStream = xMimeSink->getInputStream();
490 if ( xMimeInStream.is() )
492 // Mediatypes longer than 1024 symbols should not appear here
493 uno::Sequence< sal_Int8 > aData( 1024 );
494 sal_Int32 nRead = xMimeInStream->readBytes( aData, 1024 );
495 if ( nRead > aData.getLength() )
496 nRead = aData.getLength();
498 if ( nRead )
499 aPackageMediatype = OUString( reinterpret_cast<char const *>(aData.getConstArray()), nRead, RTL_TEXTENCODING_ASCII_US );
503 if (!bManifestParsed || m_xRootFolder->GetMediaType().isEmpty())
505 // the manifest.xml could not be successfully parsed, this is an inconsistent package
506 if ( aPackageMediatype.startsWith("application/vnd.") )
508 // accept only types that look similar to own mediatypes
509 m_xRootFolder->SetMediaType( aPackageMediatype );
510 // also set version explicitly
511 if (oFirstVersion && m_xRootFolder->GetVersion().isEmpty())
513 m_xRootFolder->SetVersion(*oFirstVersion);
515 // if there is an encrypted inner package, there is no root
516 // document, because instead there is a package, and it is not
517 // an error
518 if (!m_xRootFolder->hasByName(u"encrypted-package"_ustr))
520 m_bMediaTypeFallbackUsed = true;
524 else if ( !m_bForceRecovery )
526 // the mimetype stream should contain the same information as manifest.xml
527 OUString const mediaTypeXML(m_xRootFolder->hasByName(u"encrypted-package"_ustr)
528 ? m_xRootFolder->doGetByName(u"encrypted-package"_ustr).xPackageEntry->GetMediaType()
529 : m_xRootFolder->GetMediaType());
530 if (mediaTypeXML != aPackageMediatype)
532 throw ZipIOException(
533 THROW_WHERE
534 "mimetype conflicts with manifest.xml, \""
535 + mediaTypeXML + "\" vs. \""
536 + aPackageMediatype + "\"" );
540 m_xRootFolder->removeByName( sMimetype );
543 m_bInconsistent = m_xRootFolder->LookForUnexpectedODF12Streams(
544 std::u16string_view(), m_xRootFolder->hasByName(u"encrypted-package"_ustr));
546 bool bODF12AndNewer = ( m_xRootFolder->GetVersion().compareTo( ODFVER_012_TEXT ) >= 0 );
547 if ( !m_bForceRecovery && bODF12AndNewer )
549 if ( m_bInconsistent )
551 // this is an ODF1.2 document that contains streams not referred in the manifest.xml;
552 // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent
553 // should be checked later
554 throw ZipIOException(
555 THROW_WHERE "there are streams not referred in manifest.xml" );
557 // all the streams should be encrypted with the same StartKey in ODF1.2
558 // TODO/LATER: in future the exception should be thrown
559 // throw ZipIOException( THROW_WHERE "More than one Start Key Generation algorithm is specified!" );
562 // in case it is a correct ODF1.2 document, the version must be set
563 // and the META-INF folder is reserved for package format
564 if ( bODF12AndNewer )
565 m_xRootFolder->removeByName( sMeta );
568 void ZipPackage::parseContentType()
570 if ( m_nFormat != embed::StorageFormats::OFOPXML )
571 return;
573 try {
574 static constexpr OUString aContentTypes(u"[Content_Types].xml"_ustr);
575 // the content type must exist in OFOPXML format!
576 if ( !m_xRootFolder->hasByName( aContentTypes ) )
577 throw io::IOException(THROW_WHERE "Wrong format!" );
579 uno::Reference < io::XActiveDataSink > xSink;
580 uno::Any aAny = m_xRootFolder->getByName( aContentTypes );
581 aAny >>= xSink;
582 if ( xSink.is() )
584 uno::Reference< io::XInputStream > xInStream = xSink->getInputStream();
585 if ( xInStream.is() )
587 // here aContentTypeInfo[0] - Defaults, and aContentTypeInfo[1] - Overrides
588 const uno::Sequence< uno::Sequence< beans::StringPair > > aContentTypeInfo =
589 ::comphelper::OFOPXMLHelper::ReadContentTypeSequence( xInStream, m_xContext );
591 if ( aContentTypeInfo.getLength() != 2 )
592 throw io::IOException(THROW_WHERE );
594 // set the implicit types first
595 for ( const auto& rPair : aContentTypeInfo[0] )
596 m_xRootFolder->setChildStreamsTypeByExtension( rPair );
598 // now set the explicit types
599 for ( const auto& rPair : aContentTypeInfo[1] )
601 OUString aPath;
602 if ( rPair.First.toChar() == '/' )
603 aPath = rPair.First.copy( 1 );
604 else
605 aPath = rPair.First;
607 if ( !aPath.isEmpty() && hasByHierarchicalName( aPath ) )
609 uno::Any aIterAny = getByHierarchicalName( aPath );
610 uno::Reference < XInterface > xIterTmp;
611 aIterAny >>= xIterTmp;
612 if (auto pStream = dynamic_cast<ZipPackageStream*>(xIterTmp.get()))
614 // this is a package stream, in OFOPXML format only streams can have mediatype
615 pStream->SetMediaType( rPair.Second );
622 m_xRootFolder->removeByName( aContentTypes );
624 catch( uno::Exception& )
626 if ( !m_bForceRecovery )
627 throw;
631 void ZipPackage::getZipFileContents()
633 ZipEnumeration aEnum = m_pZipFile->entries();
634 OUString sDirName;
636 while (aEnum.hasMoreElements())
638 ZipPackageFolder* pCurrent = m_xRootFolder.get();
639 const ZipEntry & rEntry = *aEnum.nextElement();
640 OUString rName = rEntry.sPath;
642 if ( m_bForceRecovery )
644 // the PKZIP Application note version 6.2 does not allows to use '\' as separator
645 // unfortunately it is used by some implementations, so we have to support it in recovery mode
646 rName = rName.replace( '\\', '/' );
649 sal_Int32 nStreamIndex = rName.lastIndexOf ( '/' );
650 if ( nStreamIndex != -1 )
652 sDirName = rName.copy ( 0, nStreamIndex );
653 FolderHash::iterator aIter = m_aRecent.find ( sDirName );
654 if ( aIter != m_aRecent.end() )
655 pCurrent = ( *aIter ).second;
658 if ( pCurrent == m_xRootFolder.get() )
660 sal_Int32 nIndex;
661 sal_Int32 nOldIndex = 0;
662 while ( ( nIndex = rName.indexOf( '/', nOldIndex ) ) != -1 )
664 OUString sTemp = rName.copy ( nOldIndex, nIndex - nOldIndex );
665 if ( nIndex == nOldIndex )
666 break;
667 if ( !pCurrent->hasByName( sTemp ) )
669 rtl::Reference<ZipPackageFolder> pPkgFolder = new ZipPackageFolder(m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
670 try {
671 pPkgFolder->setName( sTemp );
672 } catch (uno::RuntimeException const& e) {
673 throw css::packages::zip::ZipIOException(e.Message);
675 pPkgFolder->doSetParent( pCurrent );
676 pCurrent = pPkgFolder.get();
678 else
680 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
681 if (!rInfo.bFolder)
682 throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
683 pCurrent = rInfo.pFolder;
685 nOldIndex = nIndex+1;
687 if ( nStreamIndex != -1 && !sDirName.isEmpty() )
688 m_aRecent [ sDirName ] = pCurrent;
690 if ( rName.getLength() -1 != nStreamIndex )
692 nStreamIndex++;
693 OUString sTemp = rName.copy( nStreamIndex );
695 if (!pCurrent->hasByName(sTemp))
697 rtl::Reference<ZipPackageStream> pPkgStream = new ZipPackageStream(*this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
698 pPkgStream->SetPackageMember(true);
699 pPkgStream->setZipEntryOnLoading(rEntry);
700 pPkgStream->setName(sTemp);
701 pPkgStream->doSetParent(pCurrent);
706 if ( m_nFormat == embed::StorageFormats::PACKAGE )
707 parseManifest();
708 else if ( m_nFormat == embed::StorageFormats::OFOPXML )
710 parseContentType();
711 checkZipEntriesWithDD();
715 void SAL_CALL ZipPackage::initialize( const uno::Sequence< Any >& aArguments )
717 beans::NamedValue aNamedValue;
719 if ( !aArguments.hasElements() )
720 return;
722 bool bHaveZipFile = true;
724 for( const auto& rArgument : aArguments )
726 OUString aParamUrl;
727 if ( rArgument >>= aParamUrl )
729 m_eMode = e_IMode_URL;
732 sal_Int32 nParam = aParamUrl.indexOf( '?' );
733 if ( nParam >= 0 )
735 m_aURL = aParamUrl.copy( 0, nParam );
736 std::u16string_view aParam = aParamUrl.subView( nParam + 1 );
738 sal_Int32 nIndex = 0;
741 std::u16string_view aCommand = o3tl::getToken(aParam, 0, '&', nIndex );
742 if ( aCommand == u"repairpackage" )
744 m_bForceRecovery = true;
745 break;
747 else if ( aCommand == u"purezip" )
749 m_nFormat = embed::StorageFormats::ZIP;
750 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
751 break;
753 else if ( aCommand == u"ofopxml" )
755 m_nFormat = embed::StorageFormats::OFOPXML;
756 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
757 break;
760 while ( nIndex >= 0 );
762 else
763 m_aURL = aParamUrl;
765 Content aContent(
766 m_aURL, uno::Reference< XCommandEnvironment >(),
767 m_xContext );
768 Any aAny = aContent.getPropertyValue(u"Size"_ustr);
769 sal_uInt64 aSize = 0;
770 // kind of optimization: treat empty files as nonexistent files
771 // and write to such files directly. Note that "Size" property is optional.
772 bool bHasSizeProperty = aAny >>= aSize;
773 if( !bHasSizeProperty || aSize )
775 uno::Reference < XActiveDataSink > xSink = new ZipPackageSink;
776 if ( aContent.openStream ( xSink ) )
777 m_xContentStream = xSink->getInputStream();
779 else
780 bHaveZipFile = false;
782 catch ( css::uno::Exception& )
784 // Exception derived from uno::Exception thrown. This probably
785 // means the file doesn't exist...we'll create it at
786 // commitChanges time
787 bHaveZipFile = false;
790 else if ( rArgument >>= m_xStream )
792 // a writable stream can implement both XStream & XInputStream
793 m_eMode = e_IMode_XStream;
794 m_xContentStream = m_xStream->getInputStream();
796 else if ( rArgument >>= m_xContentStream )
798 m_eMode = e_IMode_XInputStream;
800 else if ( rArgument >>= aNamedValue )
802 if ( aNamedValue.Name == "RepairPackage" )
803 aNamedValue.Value >>= m_bForceRecovery;
804 else if ( aNamedValue.Name == "PackageFormat" )
806 // setting this argument to true means Package format
807 // setting it to false means plain Zip format
809 bool bPackFormat = true;
810 aNamedValue.Value >>= bPackFormat;
811 if ( !bPackFormat )
812 m_nFormat = embed::StorageFormats::ZIP;
814 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
816 else if ( aNamedValue.Name == "StorageFormat" )
818 OUString aFormatName;
819 sal_Int32 nFormatID = 0;
820 if ( aNamedValue.Value >>= aFormatName )
822 if ( aFormatName == PACKAGE_STORAGE_FORMAT_STRING )
823 m_nFormat = embed::StorageFormats::PACKAGE;
824 else if ( aFormatName == ZIP_STORAGE_FORMAT_STRING )
825 m_nFormat = embed::StorageFormats::ZIP;
826 else if ( aFormatName == OFOPXML_STORAGE_FORMAT_STRING )
827 m_nFormat = embed::StorageFormats::OFOPXML;
828 else
829 throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
831 else if ( aNamedValue.Value >>= nFormatID )
833 if (nFormatID != embed::StorageFormats::PACKAGE
834 && nFormatID != embed::StorageFormats::ZIP
835 && nFormatID != embed::StorageFormats::OFOPXML)
836 throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
838 m_nFormat = nFormatID;
840 else
841 throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
843 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
845 else if ( aNamedValue.Name == "AllowRemoveOnInsert" )
847 aNamedValue.Value >>= m_bAllowRemoveOnInsert;
848 m_xRootFolder->setRemoveOnInsertMode_Impl( m_bAllowRemoveOnInsert );
850 else if (aNamedValue.Name == "NoFileSync")
851 aNamedValue.Value >>= m_bDisableFileSync;
853 // for now the progress handler is not used, probably it will never be
854 // if ( aNamedValue.Name == "ProgressHandler" )
856 else
858 // The URL is not acceptable
859 throw css::uno::Exception (THROW_WHERE "Bad arguments.",
860 getXWeak() );
866 if ( m_xContentStream.is() )
868 // the stream must be seekable, if it is not it will be wrapped
869 m_xContentStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream, m_xContext );
870 m_xContentSeek.set( m_xContentStream, UNO_QUERY_THROW );
871 if ( !m_xContentSeek->getLength() )
872 bHaveZipFile = false;
874 else
875 bHaveZipFile = false;
877 catch ( css::uno::Exception& )
879 // Exception derived from uno::Exception thrown. This probably
880 // means the file doesn't exist...we'll create it at
881 // commitChanges time
882 bHaveZipFile = false;
884 if ( !bHaveZipFile )
885 return;
887 bool bBadZipFile = false;
888 OUString message;
891 m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, true,
892 m_bForceRecovery,
893 m_nFormat == embed::StorageFormats::ZIP
894 ? ZipFile::Checks::Default
895 : m_nFormat == embed::StorageFormats::OFOPXML
896 ? ZipFile::Checks::CheckInsensitive
897 : ZipFile::Checks::TryCheckInsensitive);
898 getZipFileContents();
900 catch ( IOException & e )
902 bBadZipFile = true;
903 message = "IOException: " + e.Message;
905 catch ( ZipException & e )
907 bBadZipFile = true;
908 message = "ZipException: " + e.Message;
910 catch ( Exception & )
912 m_pZipFile.reset();
913 throw;
916 if ( bBadZipFile )
918 // clean up the memory, and tell the UCB about the error
919 m_pZipFile.reset();
921 throw css::packages::zip::ZipIOException (
922 THROW_WHERE "Bad Zip File, " + message,
923 getXWeak() );
927 Any SAL_CALL ZipPackage::getByHierarchicalName( const OUString& aName )
929 OUString sDirName;
930 sal_Int32 nOldIndex, nStreamIndex;
932 if (aName == "/")
933 // root directory.
934 return Any ( uno::Reference( cppu::getXWeak(m_xRootFolder.get()) ) );
936 sal_Int32 nIndex = aName.getLength();
938 nStreamIndex = aName.lastIndexOf ( '/' );
939 bool bFolder = nStreamIndex == nIndex-1; // last character is '/'.
941 if ( nStreamIndex != -1 )
943 // The name contains '/'.
944 sDirName = aName.copy ( 0, nStreamIndex );
945 FolderHash::iterator aIter = m_aRecent.find ( sDirName );
946 if ( aIter != m_aRecent.end() )
948 // There is a cached entry for this name.
950 ZipPackageFolder* pFolder = aIter->second;
952 if ( bFolder )
954 // Determine the directory name.
955 sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
956 std::u16string_view sTemp = aName.subView ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
958 if (pFolder && sTemp == pFolder->getName())
959 return Any(uno::Reference(cppu::getXWeak(pFolder)));
961 else
963 // Determine the file name.
964 OUString sTemp = aName.copy ( nStreamIndex + 1 );
966 if (pFolder && pFolder->hasByName(sTemp))
967 return pFolder->getByName(sTemp);
970 m_aRecent.erase( aIter );
973 else if ( m_xRootFolder->hasByName ( aName ) )
974 // top-level element.
975 return m_xRootFolder->getByName ( aName );
977 // Not in the cache. Search normally.
979 nOldIndex = 0;
980 ZipPackageFolder * pCurrent = m_xRootFolder.get();
981 ZipPackageFolder * pPrevious = nullptr;
983 // Find the right directory for the given path.
985 while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
987 OUString sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
988 if ( nIndex == nOldIndex )
989 break;
990 if ( !pCurrent->hasByName( sTemp ) )
991 throw NoSuchElementException(THROW_WHERE );
993 pPrevious = pCurrent;
994 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
995 if (!rInfo.bFolder)
996 throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
997 pCurrent = rInfo.pFolder;
998 nOldIndex = nIndex+1;
1001 if ( bFolder )
1003 if ( nStreamIndex != -1 )
1004 m_aRecent[sDirName] = pPrevious; // cache it.
1005 return Any ( uno::Reference( cppu::getXWeak(pCurrent) ) );
1008 OUString sTemp = aName.copy( nOldIndex );
1010 if ( pCurrent->hasByName ( sTemp ) )
1012 if ( nStreamIndex != -1 )
1013 m_aRecent[sDirName] = pCurrent; // cache it.
1014 return pCurrent->getByName( sTemp );
1017 throw NoSuchElementException(THROW_WHERE);
1020 sal_Bool SAL_CALL ZipPackage::hasByHierarchicalName( const OUString& aName )
1022 if (aName == "/")
1023 // root directory
1024 return true;
1028 OUString sDirName;
1029 sal_Int32 nStreamIndex;
1030 nStreamIndex = aName.lastIndexOf ( '/' );
1031 bool bFolder = nStreamIndex == aName.getLength()-1;
1032 if ( nStreamIndex != -1 )
1034 sDirName = aName.copy ( 0, nStreamIndex );
1035 FolderHash::iterator aIter = m_aRecent.find ( sDirName );
1036 if ( aIter != m_aRecent.end() )
1038 if ( bFolder )
1040 sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
1041 std::u16string_view sTemp = aName.subView ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
1042 if ( sTemp == ( *aIter ).second->getName() )
1043 return true;
1044 else
1045 m_aRecent.erase ( aIter );
1047 else
1049 OUString sTemp = aName.copy ( nStreamIndex + 1 );
1050 if ( ( *aIter ).second->hasByName( sTemp ) )
1051 return true;
1052 else
1053 m_aRecent.erase( aIter );
1057 else
1059 if ( m_xRootFolder->hasByName ( aName ) )
1060 return true;
1062 ZipPackageFolder * pCurrent = m_xRootFolder.get();
1063 ZipPackageFolder * pPrevious = nullptr;
1064 sal_Int32 nOldIndex = 0;
1065 sal_Int32 nIndex;
1066 while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
1068 if ( nIndex == nOldIndex )
1069 break;
1071 OUString sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
1073 if ( pCurrent->hasByName( sTemp ) )
1075 pPrevious = pCurrent;
1076 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
1077 if (!rInfo.bFolder)
1078 throw css::packages::zip::ZipIOException(u"Bad Zip File, stream as folder"_ustr);
1079 pCurrent = rInfo.pFolder;
1081 else
1082 return false;
1083 nOldIndex = nIndex+1;
1085 if ( bFolder )
1087 m_aRecent[sDirName] = pPrevious;
1088 return true;
1090 else
1092 OUString sTemp = aName.copy( nOldIndex );
1094 if ( pCurrent->hasByName( sTemp ) )
1096 m_aRecent[sDirName] = pCurrent;
1097 return true;
1101 catch (const uno::RuntimeException &)
1103 throw;
1105 catch (const uno::Exception&)
1107 uno::Any e(::cppu::getCaughtException());
1108 throw lang::WrappedTargetRuntimeException(u"ZipPackage::hasByHierarchicalName"_ustr, nullptr, e);
1110 return false;
1113 uno::Reference< XInterface > SAL_CALL ZipPackage::createInstance()
1115 uno::Reference < XInterface > xRef = *( new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert ) );
1116 return xRef;
1119 uno::Reference< XInterface > SAL_CALL ZipPackage::createInstanceWithArguments( const uno::Sequence< Any >& aArguments )
1121 bool bArg = false;
1122 uno::Reference < XInterface > xRef;
1123 if ( aArguments.hasElements() )
1124 aArguments[0] >>= bArg;
1125 if ( bArg )
1126 xRef = *new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1127 else
1128 xRef = *new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1130 return xRef;
1133 void ZipPackage::WriteMimetypeMagicFile( ZipOutputStream& aZipOut )
1135 static constexpr OUString sMime (u"mimetype"_ustr);
1136 if ( m_xRootFolder->hasByName( sMime ) )
1137 m_xRootFolder->removeByName( sMime );
1139 auto pEntry = std::make_unique<ZipEntry>();
1140 sal_Int32 nBufferLength = m_xRootFolder->GetMediaType().getLength();
1141 OString sMediaType = OUStringToOString( m_xRootFolder->GetMediaType(), RTL_TEXTENCODING_ASCII_US );
1142 const uno::Sequence< sal_Int8 > aType( reinterpret_cast<sal_Int8 const *>(sMediaType.getStr()),
1143 nBufferLength );
1145 pEntry->sPath = sMime;
1146 pEntry->nMethod = STORED;
1147 pEntry->nSize = pEntry->nCompressedSize = nBufferLength;
1148 pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1150 CRC32 aCRC32;
1151 aCRC32.update( aType );
1152 pEntry->nCrc = aCRC32.getValue();
1156 ZipOutputStream::setEntry(*pEntry);
1157 aZipOut.writeLOC(std::move(pEntry));
1158 aZipOut.rawWrite(aType);
1159 aZipOut.rawCloseEntry();
1161 catch ( const css::io::IOException & )
1163 css::uno::Any anyEx = cppu::getCaughtException();
1164 throw WrappedTargetException(
1165 THROW_WHERE "Error adding mimetype to the ZipOutputStream!",
1166 getXWeak(),
1167 anyEx );
1171 void ZipPackage::WriteManifest( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
1173 // Write the manifest
1174 uno::Reference < XManifestWriter > xWriter = ManifestWriter::create( m_xContext );
1175 auto pEntry = std::make_unique<ZipEntry>();
1176 rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
1178 pEntry->sPath = "META-INF/manifest.xml";
1179 pEntry->nMethod = DEFLATED;
1180 pEntry->nCrc = -1;
1181 pEntry->nSize = pEntry->nCompressedSize = -1;
1182 pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1184 xWriter->writeManifestSequence ( pBuffer, comphelper::containerToSequence(aManList) );
1186 sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
1187 pBuffer->realloc( nBufferLength );
1189 // the manifest.xml is never encrypted - so pass an empty reference
1190 ZipOutputStream::setEntry(*pEntry);
1191 auto p = pEntry.get();
1192 aZipOut.writeLOC(std::move(pEntry));
1193 ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
1194 aZipEntry.write(pBuffer->getSequence());
1195 aZipEntry.closeEntry();
1196 aZipOut.rawCloseEntry();
1199 void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const std::vector< uno::Sequence < PropertyValue > >& aManList )
1201 auto pEntry = std::make_unique<ZipEntry>();
1202 rtl::Reference<ZipPackageBuffer> pBuffer = new ZipPackageBuffer;
1204 pEntry->sPath = "[Content_Types].xml";
1205 pEntry->nMethod = DEFLATED;
1206 pEntry->nCrc = -1;
1207 pEntry->nSize = pEntry->nCompressedSize = -1;
1208 pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1210 // Add default entries, the count must be updated manually when appending.
1211 // Add at least the standard default entries.
1212 uno::Sequence< beans::StringPair > aDefaultsSequence
1214 { u"xml"_ustr, u"application/xml"_ustr },
1215 { u"rels"_ustr, u"application/vnd.openxmlformats-package.relationships+xml"_ustr },
1216 { u"png"_ustr, u"image/png"_ustr },
1217 { u"jpeg"_ustr, u"image/jpeg"_ustr }
1220 uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size());
1221 auto aOverridesSequenceRange = asNonConstRange(aOverridesSequence);
1222 sal_Int32 nOverSeqLength = 0;
1223 for (const auto& rMan : aManList)
1225 OUString aType;
1226 OSL_ENSURE( rMan[PKG_MNFST_MEDIATYPE].Name == "MediaType" && rMan[PKG_MNFST_FULLPATH].Name == "FullPath",
1227 "The mediatype sequence format is wrong!" );
1228 rMan[PKG_MNFST_MEDIATYPE].Value >>= aType;
1229 if ( !aType.isEmpty() )
1231 OUString aPath;
1232 // only nonempty type makes sense here
1233 rMan[PKG_MNFST_FULLPATH].Value >>= aPath;
1234 //FIXME: For now we have no way of differentiating defaults from others.
1235 aOverridesSequenceRange[nOverSeqLength].First = "/" + aPath;
1236 aOverridesSequenceRange[nOverSeqLength].Second = aType;
1237 ++nOverSeqLength;
1240 aOverridesSequence.realloc(nOverSeqLength);
1242 ::comphelper::OFOPXMLHelper::WriteContentSequence(
1243 pBuffer, aDefaultsSequence, aOverridesSequence, m_xContext );
1245 sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
1246 pBuffer->realloc( nBufferLength );
1248 // there is no encryption in this format currently
1249 ZipOutputStream::setEntry(*pEntry);
1250 auto p = pEntry.get();
1251 aZipOut.writeLOC(std::move(pEntry));
1252 ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, p, nullptr, /*bEncrypt*/false);
1253 aZipEntry.write(pBuffer->getSequence());
1254 aZipEntry.closeEntry();
1255 aZipOut.rawCloseEntry();
1258 void ZipPackage::ConnectTo( const uno::Reference< io::XInputStream >& xInStream )
1260 assert(dynamic_cast<comphelper::ByteReader*>(xInStream.get()));
1261 m_xContentSeek.set( xInStream, uno::UNO_QUERY_THROW );
1262 m_xContentStream = xInStream;
1264 // seek back to the beginning of the temp file so we can read segments from it
1265 m_xContentSeek->seek( 0 );
1266 if ( m_pZipFile )
1267 m_pZipFile->setInputStream( m_xContentStream );
1268 else
1269 m_pZipFile.emplace(m_aMutexHolder, m_xContentStream, m_xContext, false,
1270 false,
1271 m_nFormat == embed::StorageFormats::ZIP
1272 ? ZipFile::Checks::Default
1273 : m_nFormat == embed::StorageFormats::OFOPXML
1274 ? ZipFile::Checks::CheckInsensitive
1275 : ZipFile::Checks::TryCheckInsensitive);
1278 uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
1280 // In case the target local file does not exist or empty
1281 // write directly to it otherwise create a temporary file to write to.
1282 // If a temporary file is created it is returned back by the method.
1283 // If the data written directly, xComponentStream will be switched here
1285 bool bUseTemp = true;
1286 uno::Reference < io::XInputStream > xResult;
1287 uno::Reference < io::XInputStream > xTempIn;
1289 uno::Reference < io::XOutputStream > xTempOut;
1290 uno::Reference< io::XActiveDataStreamer > xSink;
1292 if ( m_eMode == e_IMode_URL && !m_pZipFile && isLocalFile() )
1294 xSink = openOriginalForOutput();
1295 if( xSink.is() )
1297 uno::Reference< io::XStream > xStr = xSink->getStream();
1298 if( xStr.is() )
1300 xTempOut = xStr->getOutputStream();
1301 if( xTempOut.is() )
1302 bUseTemp = false;
1306 else if ( m_eMode == e_IMode_XStream && !m_pZipFile )
1308 // write directly to an empty stream
1309 xTempOut = m_xStream->getOutputStream();
1310 if( xTempOut.is() )
1311 bUseTemp = false;
1314 if( bUseTemp )
1316 // create temporary file
1317 rtl::Reference < utl::TempFileFastService > xTempFile( new utl::TempFileFastService );
1318 xTempOut.set( xTempFile );
1319 xTempIn.set( xTempFile );
1322 // Hand it to the ZipOutputStream:
1323 ZipOutputStream aZipOut( xTempOut );
1326 if ( m_nFormat == embed::StorageFormats::PACKAGE )
1328 // Remove the old manifest.xml file as the
1329 // manifest will be re-generated and the
1330 // META-INF directory implicitly created if does not exist
1331 static constexpr OUString sMeta (u"META-INF"_ustr);
1333 if ( m_xRootFolder->hasByName( sMeta ) )
1335 static constexpr OUString sManifest (u"manifest.xml"_ustr);
1337 uno::Reference< XNameContainer > xMetaInfFolder;
1338 Any aAny = m_xRootFolder->getByName( sMeta );
1339 aAny >>= xMetaInfFolder;
1340 if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
1341 xMetaInfFolder->removeByName( sManifest );
1344 // Write a magic file with mimetype
1345 WriteMimetypeMagicFile( aZipOut );
1347 else if ( m_nFormat == embed::StorageFormats::OFOPXML )
1349 // Remove the old [Content_Types].xml file as the
1350 // file will be re-generated
1352 static constexpr OUString aContentTypes(u"[Content_Types].xml"_ustr);
1354 if ( m_xRootFolder->hasByName( aContentTypes ) )
1355 m_xRootFolder->removeByName( aContentTypes );
1358 // Create a vector to store data for the manifest.xml file
1359 std::vector < uno::Sequence < PropertyValue > > aManList;
1361 static constexpr OUStringLiteral sMediaType(u"MediaType");
1362 static constexpr OUStringLiteral sVersion(u"Version");
1363 static constexpr OUStringLiteral sFullPath(u"FullPath");
1364 const bool bIsGpgEncrypt = m_aGpgProps.hasElements();
1366 // note: this is always created here (needed for GPG), possibly
1367 // filtered out later in ManifestExport
1368 if ( m_nFormat == embed::StorageFormats::PACKAGE )
1370 uno::Sequence < PropertyValue > aPropSeq(
1371 bIsGpgEncrypt ? PKG_SIZE_NOENCR_MNFST+1 : PKG_SIZE_NOENCR_MNFST );
1372 auto pPropSeq = aPropSeq.getArray();
1373 pPropSeq [PKG_MNFST_MEDIATYPE].Name = sMediaType;
1374 pPropSeq [PKG_MNFST_MEDIATYPE].Value <<= m_xRootFolder->GetMediaType();
1375 pPropSeq [PKG_MNFST_VERSION].Name = sVersion;
1376 pPropSeq [PKG_MNFST_VERSION].Value <<= m_xRootFolder->GetVersion();
1377 pPropSeq [PKG_MNFST_FULLPATH].Name = sFullPath;
1378 pPropSeq [PKG_MNFST_FULLPATH].Value <<= u"/"_ustr;
1380 if( bIsGpgEncrypt )
1382 pPropSeq[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
1383 pPropSeq[PKG_SIZE_NOENCR_MNFST].Value <<= m_aGpgProps;
1385 aManList.push_back( aPropSeq );
1389 ::std::optional<sal_Int32> oPBKDF2IterationCount;
1390 ::std::optional<::std::tuple<sal_Int32, sal_Int32, sal_Int32>> oArgon2Args;
1392 if (!bIsGpgEncrypt)
1394 if (m_nKeyDerivationFunctionID == xml::crypto::KDFID::PBKDF2)
1395 { // if there is only one KDF invocation, increase the safety margin
1396 oPBKDF2IterationCount.emplace(m_xRootFolder->hasByName(u"encrypted-package"_ustr) ? 600000 : 100000);
1398 else
1400 assert(m_nKeyDerivationFunctionID == xml::crypto::KDFID::Argon2id);
1401 oArgon2Args.emplace(3, (1<<16), 4);
1405 // call saveContents - it will recursively save sub-directories
1406 m_xRootFolder->saveContents(u""_ustr, aManList, aZipOut, GetEncryptionKey(),
1407 oPBKDF2IterationCount, oArgon2Args);
1410 if( m_nFormat == embed::StorageFormats::PACKAGE )
1412 WriteManifest( aZipOut, aManList );
1414 else if( m_nFormat == embed::StorageFormats::OFOPXML )
1416 WriteContentTypes( aZipOut, aManList );
1419 aZipOut.finish();
1421 if( bUseTemp )
1422 xResult = std::move(xTempIn);
1424 // Update our References to point to the new temp file
1425 if( !bUseTemp )
1427 // the case when the original contents were written directly
1428 xTempOut->flush();
1430 // in case the stream is based on a file it will implement the following interface
1431 // the call should be used to be sure that the contents are written to the file system
1432 uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( xTempOut, uno::UNO_QUERY );
1433 if (asyncOutputMonitor.is() && !m_bDisableFileSync)
1434 asyncOutputMonitor->waitForCompletion();
1436 // no need to postpone switching to the new stream since the target was written directly
1437 uno::Reference< io::XInputStream > xNewStream;
1438 if ( m_eMode == e_IMode_URL )
1439 xNewStream = xSink->getStream()->getInputStream();
1440 else if ( m_eMode == e_IMode_XStream && m_xStream.is() )
1441 xNewStream = m_xStream->getInputStream();
1443 if ( xNewStream.is() )
1444 ConnectTo( xNewStream );
1447 catch ( uno::Exception& )
1449 if( bUseTemp )
1451 // no information loss appears, thus no special handling is required
1452 uno::Any aCaught( ::cppu::getCaughtException() );
1454 // it is allowed to throw WrappedTargetException
1455 WrappedTargetException aException;
1456 if ( aCaught >>= aException )
1457 throw aException;
1459 throw WrappedTargetException(
1460 THROW_WHERE "Problem writing the original content!",
1461 getXWeak(),
1462 aCaught );
1464 else
1466 // the document is written directly, although it was empty it is important to notify that the writing has failed
1467 // TODO/LATER: let the package be able to recover in this situation
1468 OUString aErrTxt(THROW_WHERE "This package is unusable!");
1469 embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), OUString() );
1470 throw WrappedTargetException( aErrTxt,
1471 getXWeak(),
1472 Any ( aException ) );
1476 return xResult;
1479 uno::Reference< XActiveDataStreamer > ZipPackage::openOriginalForOutput()
1481 // open and truncate the original file
1482 Content aOriginalContent(
1483 m_aURL, uno::Reference< XCommandEnvironment >(),
1484 m_xContext );
1485 uno::Reference< XActiveDataStreamer > xSink = new ActiveDataStreamer;
1487 if ( m_eMode == e_IMode_URL )
1491 bool bTruncSuccess = false;
1495 Exception aDetect;
1496 Any aAny = aOriginalContent.setPropertyValue(u"Size"_ustr, Any( sal_Int64(0) ) );
1497 if( !( aAny >>= aDetect ) )
1498 bTruncSuccess = true;
1500 catch( Exception& )
1504 if( !bTruncSuccess )
1506 // the file is not accessible
1507 // just try to write an empty stream to it
1509 uno::Reference< XInputStream > xTempIn = new DummyInputStream; //uno::Reference< XInputStream >( xTempOut, UNO_QUERY );
1510 aOriginalContent.writeStream( xTempIn , true );
1513 OpenCommandArgument2 aArg;
1514 aArg.Mode = OpenMode::DOCUMENT;
1515 aArg.Priority = 0; // unused
1516 aArg.Sink = xSink;
1517 aArg.Properties = uno::Sequence< Property >( 0 ); // unused
1519 aOriginalContent.executeCommand(u"open"_ustr, Any( aArg ) );
1521 catch( Exception& )
1523 // seems to be nonlocal file
1524 // temporary file mechanics should be used
1528 return xSink;
1531 void SAL_CALL ZipPackage::commitChanges()
1533 // lock the component for the time of committing
1534 ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1536 if ( m_eMode == e_IMode_XInputStream )
1538 IOException aException;
1539 throw WrappedTargetException(THROW_WHERE "This package is read only!",
1540 getXWeak(), Any ( aException ) );
1542 // first the writeTempFile is called, if it returns a stream the stream should be written to the target
1543 // if no stream was returned, the file was written directly, nothing should be done
1544 uno::Reference< io::XInputStream > xTempInStream;
1547 xTempInStream = writeTempFile();
1549 catch (const ucb::ContentCreationException&)
1551 css::uno::Any anyEx = cppu::getCaughtException();
1552 throw WrappedTargetException(THROW_WHERE "Temporary file should be creatable!",
1553 getXWeak(), anyEx );
1555 if ( xTempInStream.is() )
1557 uno::Reference< io::XSeekable > xTempSeek( xTempInStream, uno::UNO_QUERY_THROW );
1561 xTempSeek->seek( 0 );
1563 catch( const uno::Exception& )
1565 css::uno::Any anyEx = cppu::getCaughtException();
1566 throw WrappedTargetException(THROW_WHERE "Temporary file should be seekable!",
1567 getXWeak(), anyEx );
1572 // connect to the temporary stream
1573 ConnectTo( xTempInStream );
1575 catch( const io::IOException& )
1577 css::uno::Any anyEx = cppu::getCaughtException();
1578 throw WrappedTargetException(THROW_WHERE "Temporary file should be connectable!",
1579 getXWeak(), anyEx );
1582 if ( m_eMode == e_IMode_XStream )
1584 // First truncate our output stream
1585 uno::Reference < XOutputStream > xOutputStream;
1587 // preparation for copy step
1590 xOutputStream = m_xStream->getOutputStream();
1592 // Make sure we avoid a situation where the current position is
1593 // not zero, but the underlying file is truncated in the
1594 // meantime.
1595 uno::Reference<io::XSeekable> xSeekable(xOutputStream, uno::UNO_QUERY);
1596 if (xSeekable.is())
1597 xSeekable->seek(0);
1599 uno::Reference < XTruncate > xTruncate ( xOutputStream, UNO_QUERY_THROW );
1601 // after successful truncation the original file contents are already lost
1602 xTruncate->truncate();
1604 catch( const uno::Exception& )
1606 css::uno::Any anyEx = cppu::getCaughtException();
1607 throw WrappedTargetException(THROW_WHERE "This package is read only!",
1608 getXWeak(), anyEx );
1613 // then copy the contents of the tempfile to our output stream
1614 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutputStream );
1615 xOutputStream->flush();
1616 uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor(
1617 xOutputStream, uno::UNO_QUERY );
1618 if ( asyncOutputMonitor.is() ) {
1619 asyncOutputMonitor->waitForCompletion();
1622 catch( uno::Exception& )
1624 // if anything goes wrong in this block the target file becomes corrupted
1625 // so an exception should be thrown as a notification about it
1626 // and the package must disconnect from the stream
1627 DisconnectFromTargetAndThrowException_Impl( xTempInStream );
1630 else if ( m_eMode == e_IMode_URL )
1632 uno::Reference< XOutputStream > aOrigFileStream;
1633 bool bCanBeCorrupted = false;
1635 if( isLocalFile() )
1637 // write directly in case of local file
1638 uno::Reference< css::ucb::XSimpleFileAccess3 > xSimpleAccess(
1639 SimpleFileAccess::create( m_xContext ) );
1640 OSL_ENSURE( xSimpleAccess.is(), "Can't instantiate SimpleFileAccess service!" );
1641 uno::Reference< io::XTruncate > xOrigTruncate;
1642 if ( xSimpleAccess.is() )
1646 aOrigFileStream = xSimpleAccess->openFileWrite( m_aURL );
1647 xOrigTruncate.set( aOrigFileStream, uno::UNO_QUERY_THROW );
1648 // after successful truncation the file is already corrupted
1649 xOrigTruncate->truncate();
1651 catch( uno::Exception& )
1655 if( xOrigTruncate.is() )
1659 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, aOrigFileStream );
1660 aOrigFileStream->closeOutput();
1662 catch( uno::Exception& )
1664 try {
1665 aOrigFileStream->closeOutput();
1666 } catch ( uno::Exception& ) {}
1668 aOrigFileStream.clear();
1669 // the original file can already be corrupted
1670 bCanBeCorrupted = true;
1675 if( !aOrigFileStream.is() )
1679 uno::Reference < XPropertySet > xPropSet ( xTempInStream, UNO_QUERY_THROW );
1681 OUString sTargetFolder = m_aURL.copy ( 0, m_aURL.lastIndexOf ( u'/' ) );
1682 Content aContent(
1683 sTargetFolder, uno::Reference< XCommandEnvironment >(),
1684 m_xContext );
1686 OUString sTempURL;
1687 Any aAny = xPropSet->getPropertyValue (u"Uri"_ustr);
1688 aAny >>= sTempURL;
1690 TransferInfo aInfo;
1691 aInfo.NameClash = NameClash::OVERWRITE;
1692 aInfo.MoveData = false;
1693 aInfo.SourceURL = sTempURL;
1694 aInfo.NewTitle = rtl::Uri::decode ( m_aURL.copy ( 1 + m_aURL.lastIndexOf ( u'/' ) ),
1695 rtl_UriDecodeWithCharset,
1696 RTL_TEXTENCODING_UTF8 );
1697 // if the file is still not corrupted, it can become after the next step
1698 aContent.executeCommand (u"transfer"_ustr, Any(aInfo) );
1700 catch ( const css::uno::Exception& )
1702 if ( bCanBeCorrupted )
1703 DisconnectFromTargetAndThrowException_Impl( xTempInStream );
1705 css::uno::Any anyEx = cppu::getCaughtException();
1706 throw WrappedTargetException(
1707 THROW_WHERE "This package may be read only!",
1708 getXWeak(),
1709 anyEx );
1715 // after successful storing it can be set to false
1716 m_bMediaTypeFallbackUsed = false;
1719 void ZipPackage::DisconnectFromTargetAndThrowException_Impl( const uno::Reference< io::XInputStream >& xTempStream )
1721 m_xStream.set( xTempStream, uno::UNO_QUERY );
1722 if ( m_xStream.is() )
1723 m_eMode = e_IMode_XStream;
1724 else
1725 m_eMode = e_IMode_XInputStream;
1727 OUString aTempURL;
1728 try {
1729 uno::Reference< beans::XPropertySet > xTempFile( xTempStream, uno::UNO_QUERY_THROW );
1730 uno::Any aUrl = xTempFile->getPropertyValue(u"Uri"_ustr);
1731 aUrl >>= aTempURL;
1732 xTempFile->setPropertyValue(u"RemoveFile"_ustr,
1733 uno::Any( false ) );
1735 catch ( uno::Exception& )
1737 OSL_FAIL( "These calls are pretty simple, they should not fail!" );
1740 OUString aErrTxt(THROW_WHERE "This package is read only!");
1741 embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), aTempURL );
1742 throw WrappedTargetException( aErrTxt,
1743 getXWeak(),
1744 Any ( aException ) );
1747 uno::Sequence< sal_Int8 > ZipPackage::GetEncryptionKey()
1749 uno::Sequence< sal_Int8 > aResult;
1751 if ( m_aStorageEncryptionKeys.hasElements() )
1753 OUString aNameToFind;
1754 if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA256 )
1755 aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
1756 else if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA1 )
1757 aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
1758 else
1759 throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
1761 for (const auto& rKey : m_aStorageEncryptionKeys)
1762 if ( rKey.Name == aNameToFind )
1763 rKey.Value >>= aResult;
1765 if (!aResult.hasElements() && m_aStorageEncryptionKeys.hasElements())
1766 { // tdf#159519 sanity check
1767 throw uno::RuntimeException(THROW_WHERE "Expected key is missing!");
1770 else
1771 aResult = m_aEncryptionKey;
1773 return aResult;
1776 sal_Bool SAL_CALL ZipPackage::hasPendingChanges()
1778 return false;
1780 Sequence< ElementChange > SAL_CALL ZipPackage::getPendingChanges()
1782 return uno::Sequence < ElementChange > ();
1786 OUString ZipPackage::getImplementationName()
1788 return u"com.sun.star.packages.comp.ZipPackage"_ustr;
1791 Sequence< OUString > ZipPackage::getSupportedServiceNames()
1793 return { u"com.sun.star.packages.Package"_ustr };
1796 sal_Bool SAL_CALL ZipPackage::supportsService( OUString const & rServiceName )
1798 return cppu::supportsService(this, rServiceName);
1801 uno::Reference< XPropertySetInfo > SAL_CALL ZipPackage::getPropertySetInfo()
1803 return uno::Reference < XPropertySetInfo > ();
1806 void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
1808 if ( m_nFormat != embed::StorageFormats::PACKAGE )
1809 throw UnknownPropertyException(aPropertyName);
1811 if (aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
1812 ||aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
1813 ||aPropertyName == IS_INCONSISTENT_PROPERTY
1814 ||aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY)
1815 throw PropertyVetoException(THROW_WHERE );
1816 else if ( aPropertyName == ENCRYPTION_KEY_PROPERTY )
1818 if ( !( aValue >>= m_aEncryptionKey ) )
1819 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
1821 m_aStorageEncryptionKeys.realloc( 0 );
1823 else if ( aPropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
1825 // this property is only necessary to support raw passwords in storage API;
1826 // because of this support the storage has to operate with more than one key dependent on storage generation algorithm;
1827 // when this support is removed, the storage will get only one key from outside
1828 if ( !( aValue >>= m_aStorageEncryptionKeys ) )
1829 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
1831 m_aEncryptionKey.realloc( 0 );
1833 else if ( aPropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
1835 uno::Sequence< beans::NamedValue > aAlgorithms;
1836 if ( m_pZipFile || !( aValue >>= aAlgorithms ) || !aAlgorithms.hasElements() )
1838 // the algorithms can not be changed if the file has a persistence based on the algorithms ( m_pZipFile )
1839 throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
1842 for (const auto& rAlgorithm : aAlgorithms)
1844 if ( rAlgorithm.Name == "StartKeyGenerationAlgorithm" )
1846 sal_Int32 nID = 0;
1847 if ( !( rAlgorithm.Value >>= nID )
1848 || ( nID != xml::crypto::DigestID::SHA256 && nID != xml::crypto::DigestID::SHA1 ) )
1850 throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
1853 m_nStartKeyGenerationID = nID;
1855 else if (rAlgorithm.Name == "KeyDerivationFunction")
1857 sal_Int32 nID = 0;
1858 if (!(rAlgorithm.Value >>= nID)
1859 || (nID != xml::crypto::KDFID::PBKDF2
1860 && nID != xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P
1861 && nID != xml::crypto::KDFID::Argon2id))
1863 throw IllegalArgumentException(THROW_WHERE "Unexpected key derivation function provided!", uno::Reference<uno::XInterface>(), 2);
1865 m_nKeyDerivationFunctionID = nID;
1867 else if ( rAlgorithm.Name == "EncryptionAlgorithm" )
1869 sal_Int32 nID = 0;
1870 if ( !( rAlgorithm.Value >>= nID )
1871 || (nID != xml::crypto::CipherID::AES_GCM_W3C
1872 && nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING
1873 && nID != xml::crypto::CipherID::BLOWFISH_CFB_8))
1875 throw IllegalArgumentException(THROW_WHERE "Unexpected encryption algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
1878 m_nCommonEncryptionID = nID;
1880 else if ( rAlgorithm.Name == "ChecksumAlgorithm" )
1882 sal_Int32 nID = 0;
1883 if (!rAlgorithm.Value.hasValue())
1885 m_oChecksumDigestID.reset();
1886 continue;
1888 if ( !( rAlgorithm.Value >>= nID )
1889 || ( nID != xml::crypto::DigestID::SHA1_1K && nID != xml::crypto::DigestID::SHA256_1K ) )
1891 throw IllegalArgumentException(THROW_WHERE "Unexpected checksum algorithm is provided!", uno::Reference<uno::XInterface>(), 2);
1894 m_oChecksumDigestID.emplace(nID);
1896 else
1898 OSL_ENSURE( false, "Unexpected encryption algorithm is provided!" );
1899 throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
1903 else if ( aPropertyName == ENCRYPTION_GPG_PROPERTIES )
1905 uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProps;
1906 if ( !( aValue >>= aGpgProps ) || !aGpgProps.hasElements() )
1908 throw IllegalArgumentException(THROW_WHERE "unexpected Gpg properties are provided.", uno::Reference< uno::XInterface >(), 2 );
1911 m_aGpgProps = std::move(aGpgProps);
1913 // override algorithm defaults (which are some legacy ODF
1914 // defaults) with reasonable values
1915 // note: these should be overridden by SfxObjectShell::SetupStorage()
1916 m_nStartKeyGenerationID = 0; // this is unused for PGP
1917 m_nKeyDerivationFunctionID = xml::crypto::KDFID::PGP_RSA_OAEP_MGF1P;
1918 m_nCommonEncryptionID = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
1919 m_oChecksumDigestID.emplace(xml::crypto::DigestID::SHA512_1K);
1921 else
1922 throw UnknownPropertyException(aPropertyName);
1925 Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
1927 // TODO/LATER: Activate the check when zip-ucp is ready
1928 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
1929 // throw UnknownPropertyException(THROW_WHERE );
1931 if ( PropertyName == ENCRYPTION_KEY_PROPERTY )
1933 return Any(m_aEncryptionKey);
1935 else if ( PropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
1937 ::comphelper::SequenceAsHashMap aAlgorithms;
1938 aAlgorithms[u"StartKeyGenerationAlgorithm"_ustr] <<= m_nStartKeyGenerationID;
1939 aAlgorithms[u"KeyDerivationFunction"_ustr] <<= m_nKeyDerivationFunctionID;
1940 aAlgorithms[u"EncryptionAlgorithm"_ustr] <<= m_nCommonEncryptionID;
1941 if (m_oChecksumDigestID)
1943 aAlgorithms[u"ChecksumAlgorithm"_ustr] <<= *m_oChecksumDigestID;
1945 else
1947 aAlgorithms[u"ChecksumAlgorithm"_ustr];
1949 return Any(aAlgorithms.getAsConstNamedValueList());
1951 if ( PropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
1953 return Any(m_aStorageEncryptionKeys);
1955 else if ( PropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY )
1957 return Any(m_bHasEncryptedEntries);
1959 else if ( PropertyName == ENCRYPTION_GPG_PROPERTIES )
1961 return Any(m_aGpgProps);
1963 else if ( PropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY )
1965 return Any(m_bHasNonEncryptedEntries);
1967 else if ( PropertyName == IS_INCONSISTENT_PROPERTY )
1969 return Any(m_bInconsistent);
1971 else if ( PropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY )
1973 return Any(m_bMediaTypeFallbackUsed);
1975 else if (PropertyName == "HasElements")
1977 return Any(m_pZipFile && m_pZipFile->entries().hasMoreElements());
1979 throw UnknownPropertyException(PropertyName);
1981 void SAL_CALL ZipPackage::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
1984 void SAL_CALL ZipPackage::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*aListener*/ )
1987 void SAL_CALL ZipPackage::addVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
1990 void SAL_CALL ZipPackage::removeVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
1994 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
1995 package_ZipPackage_get_implementation(
1996 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
1998 return cppu::acquire(new ZipPackage(context));
2001 extern "C" bool TestImportZip(SvStream& rStream)
2003 // explicitly tests the "RepairPackage" recovery mode
2004 rtl::Reference<ZipPackage> xPackage(new ZipPackage(comphelper::getProcessComponentContext()));
2005 css::uno::Reference<css::io::XInputStream> xStream(new utl::OInputStreamWrapper(rStream));
2006 css::uno::Sequence<Any> aArgs{ Any(xStream), Any(NamedValue(u"RepairPackage"_ustr, Any(true))) };
2007 xPackage->initialize(aArgs);
2008 return true;
2011 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */