bump product version to 6.4.0.3
[LibreOffice.git] / package / source / zippackage / ZipPackage.cxx
blob7b2e705e45e56d8e2fab680305f634534d8d5d3b
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/manifest/ManifestReader.hpp>
33 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
34 #include <com/sun/star/io/TempFile.hpp>
35 #include <com/sun/star/io/XStream.hpp>
36 #include <com/sun/star/io/XInputStream.hpp>
37 #include <com/sun/star/io/XOutputStream.hpp>
38 #include <com/sun/star/io/XTruncate.hpp>
39 #include <com/sun/star/io/XSeekable.hpp>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
42 #include <com/sun/star/container/XNameContainer.hpp>
43 #include <com/sun/star/ucb/IOErrorCode.hpp>
44 #include <comphelper/fileurl.hxx>
45 #include <ucbhelper/content.hxx>
46 #include <cppuhelper/factory.hxx>
47 #include <cppuhelper/exc_hlp.hxx>
48 #include <com/sun/star/ucb/ContentCreationException.hpp>
49 #include <com/sun/star/ucb/TransferInfo.hpp>
50 #include <com/sun/star/ucb/NameClash.hpp>
51 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
52 #include <com/sun/star/ucb/OpenMode.hpp>
53 #include <com/sun/star/ucb/XProgressHandler.hpp>
54 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
55 #include <com/sun/star/io/XActiveDataStreamer.hpp>
56 #include <com/sun/star/embed/XTransactedObject.hpp>
57 #include <com/sun/star/embed/UseBackupException.hpp>
58 #include <com/sun/star/embed/StorageFormats.hpp>
59 #include <com/sun/star/beans/NamedValue.hpp>
60 #include <com/sun/star/xml/crypto/DigestID.hpp>
61 #include <com/sun/star/xml/crypto/CipherID.hpp>
62 #include <cppuhelper/implbase.hxx>
63 #include "ContentInfo.hxx"
64 #include <cppuhelper/typeprovider.hxx>
65 #include <rtl/uri.hxx>
66 #include <rtl/random.h>
67 #include <osl/diagnose.h>
68 #include <sal/log.hxx>
69 #include <com/sun/star/io/XAsyncOutputMonitor.hpp>
71 #include <cstring>
72 #include <memory>
73 #include <vector>
75 #include <comphelper/processfactory.hxx>
76 #include <comphelper/seekableinput.hxx>
77 #include <comphelper/storagehelper.hxx>
78 #include <comphelper/ofopxmlhelper.hxx>
79 #include <comphelper/documentconstants.hxx>
80 #include <comphelper/sequenceashashmap.hxx>
81 #include <cppuhelper/supportsservice.hxx>
82 #include <comphelper/sequence.hxx>
83 #include <comphelper/servicehelper.hxx>
85 using namespace std;
86 using namespace osl;
87 using namespace cppu;
88 using namespace ucbhelper;
89 using namespace com::sun::star;
90 using namespace com::sun::star::io;
91 using namespace com::sun::star::uno;
92 using namespace com::sun::star::ucb;
93 using namespace com::sun::star::util;
94 using namespace com::sun::star::lang;
95 using namespace com::sun::star::task;
96 using namespace com::sun::star::beans;
97 using namespace com::sun::star::packages;
98 using namespace com::sun::star::container;
99 using namespace com::sun::star::packages::zip;
100 using namespace com::sun::star::packages::manifest;
101 using namespace com::sun::star::packages::zip::ZipConstants;
103 #if OSL_DEBUG_LEVEL > 0
104 #define THROW_WHERE SAL_WHERE
105 #else
106 #define THROW_WHERE ""
107 #endif
109 class ActiveDataStreamer : public ::cppu::WeakImplHelper< XActiveDataStreamer >
111 uno::Reference< XStream > mStream;
112 public:
114 virtual uno::Reference< XStream > SAL_CALL getStream() override
115 { return mStream; }
117 virtual void SAL_CALL setStream( const uno::Reference< XStream >& stream ) override
118 { mStream = stream; }
121 class DummyInputStream : public ::cppu::WeakImplHelper< XInputStream >
123 virtual sal_Int32 SAL_CALL readBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
124 { return 0; }
126 virtual sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
127 { return 0; }
129 virtual void SAL_CALL skipBytes( sal_Int32 ) override
132 virtual sal_Int32 SAL_CALL available() override
133 { return 0; }
135 virtual void SAL_CALL closeInput() override
139 ZipPackage::ZipPackage ( const uno::Reference < XComponentContext > &xContext )
140 : m_aMutexHolder( new comphelper::RefCountedMutex )
141 , m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1 )
142 , m_nChecksumDigestID( xml::crypto::DigestID::SHA1_1K )
143 , m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8 )
144 , m_bHasEncryptedEntries ( false )
145 , m_bHasNonEncryptedEntries ( false )
146 , m_bInconsistent ( false )
147 , m_bForceRecovery ( false )
148 , m_bMediaTypeFallbackUsed ( false )
149 , m_nFormat( embed::StorageFormats::PACKAGE ) // package is the default format
150 , m_bAllowRemoveOnInsert( true )
151 , m_eMode ( e_IMode_None )
152 , m_xContext( xContext )
154 m_xRootFolder = new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
157 ZipPackage::~ZipPackage()
161 bool ZipPackage::isLocalFile() const
163 return comphelper::isFileUrl(m_aURL);
166 void ZipPackage::parseManifest()
168 if ( m_nFormat == embed::StorageFormats::PACKAGE )
170 bool bManifestParsed = false;
171 const OUString sMeta ("META-INF");
172 if ( m_xRootFolder->hasByName( sMeta ) )
174 const OUString sManifest ("manifest.xml");
176 try {
177 uno::Reference< XUnoTunnel > xTunnel;
178 Any aAny = m_xRootFolder->getByName( sMeta );
179 aAny >>= xTunnel;
180 uno::Reference< XNameContainer > xMetaInfFolder( xTunnel, UNO_QUERY );
181 if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
183 aAny = xMetaInfFolder->getByName( sManifest );
184 aAny >>= xTunnel;
185 uno::Reference < XActiveDataSink > xSink ( xTunnel, UNO_QUERY );
186 if ( xSink.is() )
188 uno::Reference < XManifestReader > xReader = ManifestReader::create( m_xContext );
190 const OUString sPropFullPath ("FullPath");
191 const OUString sPropVersion ("Version");
192 const OUString sPropMediaType ("MediaType");
193 const OUString sPropInitialisationVector ("InitialisationVector");
194 const OUString sPropSalt ("Salt");
195 const OUString sPropIterationCount ("IterationCount");
196 const OUString sPropSize ("Size");
197 const OUString sPropDigest ("Digest");
198 const OUString sPropDerivedKeySize ("DerivedKeySize");
199 const OUString sPropDigestAlgorithm ("DigestAlgorithm");
200 const OUString sPropEncryptionAlgorithm ("EncryptionAlgorithm");
201 const OUString sPropStartKeyAlgorithm ("StartKeyAlgorithm");
202 const OUString sKeyInfo ("KeyInfo");
204 const uno::Sequence < uno::Sequence < PropertyValue > > aManifestSequence = xReader->readManifestSequence ( xSink->getInputStream() );
205 ZipPackageStream *pStream = nullptr;
206 ZipPackageFolder *pFolder = nullptr;
207 const Any *pKeyInfo = nullptr;
209 for ( const uno::Sequence<PropertyValue>& rSequence : aManifestSequence )
211 OUString sPath, sMediaType, sVersion;
212 const Any *pSalt = nullptr, *pVector = nullptr, *pCount = nullptr, *pSize = nullptr, *pDigest = nullptr, *pDigestAlg = nullptr, *pEncryptionAlg = nullptr, *pStartKeyAlg = nullptr, *pDerivedKeySize = nullptr;
213 for ( const PropertyValue& rValue : rSequence )
215 if ( rValue.Name == sPropFullPath )
216 rValue.Value >>= sPath;
217 else if ( rValue.Name == sPropVersion )
218 rValue.Value >>= sVersion;
219 else if ( rValue.Name == sPropMediaType )
220 rValue.Value >>= sMediaType;
221 else if ( rValue.Name == sPropSalt )
222 pSalt = &( rValue.Value );
223 else if ( rValue.Name == sPropInitialisationVector )
224 pVector = &( rValue.Value );
225 else if ( rValue.Name == sPropIterationCount )
226 pCount = &( rValue.Value );
227 else if ( rValue.Name == sPropSize )
228 pSize = &( rValue.Value );
229 else if ( rValue.Name == sPropDigest )
230 pDigest = &( rValue.Value );
231 else if ( rValue.Name == sPropDigestAlgorithm )
232 pDigestAlg = &( rValue.Value );
233 else if ( rValue.Name == sPropEncryptionAlgorithm )
234 pEncryptionAlg = &( rValue.Value );
235 else if ( rValue.Name == sPropStartKeyAlgorithm )
236 pStartKeyAlg = &( rValue.Value );
237 else if ( rValue.Name == sPropDerivedKeySize )
238 pDerivedKeySize = &( rValue.Value );
239 else if ( rValue.Name == sKeyInfo )
240 pKeyInfo = &( rValue.Value );
243 if ( !sPath.isEmpty() && hasByHierarchicalName ( sPath ) )
245 aAny = getByHierarchicalName( sPath );
246 uno::Reference < XUnoTunnel > xUnoTunnel;
247 aAny >>= xUnoTunnel;
248 sal_Int64 nTest=0;
249 if ( (nTest = xUnoTunnel->getSomething( ZipPackageFolder::getUnoTunnelId() )) != 0 )
251 pFolder = reinterpret_cast < ZipPackageFolder* > ( nTest );
252 pFolder->SetMediaType ( sMediaType );
253 pFolder->SetVersion ( sVersion );
255 else
257 pStream = reinterpret_cast < ZipPackageStream* > ( xUnoTunnel->getSomething( ZipPackageStream::getUnoTunnelId() ));
258 pStream->SetMediaType ( sMediaType );
259 pStream->SetFromManifest( true );
261 if ( pKeyInfo && pVector && pSize && pDigest && pDigestAlg && pEncryptionAlg )
263 uno::Sequence < sal_Int8 > aSequence;
264 sal_Int64 nSize = 0;
265 sal_Int32 nDigestAlg = 0, nEncryptionAlg = 0;
267 pStream->SetToBeEncrypted ( true );
269 *pVector >>= aSequence;
270 pStream->setInitialisationVector ( aSequence );
272 *pSize >>= nSize;
273 pStream->setSize ( nSize );
275 *pDigest >>= aSequence;
276 pStream->setDigest ( aSequence );
278 *pDigestAlg >>= nDigestAlg;
279 pStream->SetImportedChecksumAlgorithm( nDigestAlg );
281 *pEncryptionAlg >>= nEncryptionAlg;
282 pStream->SetImportedEncryptionAlgorithm( nEncryptionAlg );
284 *pKeyInfo >>= m_aGpgProps;
286 pStream->SetToBeCompressed ( true );
287 pStream->SetToBeEncrypted ( true );
288 pStream->SetIsEncrypted ( true );
289 pStream->setIterationCount(0);
291 // clamp to default SHA256 start key magic value,
292 // c.f. ZipPackageStream::GetEncryptionKey()
293 // trying to get key value from properties
294 const sal_Int32 nStartKeyAlg = xml::crypto::DigestID::SHA256;
295 pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
297 if ( !m_bHasEncryptedEntries && pStream->getName() == "content.xml" )
299 m_bHasEncryptedEntries = true;
300 m_nChecksumDigestID = nDigestAlg;
301 m_nCommonEncryptionID = nEncryptionAlg;
302 m_nStartKeyGenerationID = nStartKeyAlg;
305 else if ( pSalt && pVector && pCount && pSize && pDigest && pDigestAlg && pEncryptionAlg )
307 uno::Sequence < sal_Int8 > aSequence;
308 sal_Int64 nSize = 0;
309 sal_Int32 nCount = 0, nDigestAlg = 0, nEncryptionAlg = 0;
310 sal_Int32 nDerivedKeySize = 16, nStartKeyAlg = xml::crypto::DigestID::SHA1;
312 pStream->SetToBeEncrypted ( true );
314 *pSalt >>= aSequence;
315 pStream->setSalt ( aSequence );
317 *pVector >>= aSequence;
318 pStream->setInitialisationVector ( aSequence );
320 *pCount >>= nCount;
321 pStream->setIterationCount ( nCount );
323 *pSize >>= nSize;
324 pStream->setSize ( nSize );
326 *pDigest >>= aSequence;
327 pStream->setDigest ( aSequence );
329 *pDigestAlg >>= nDigestAlg;
330 pStream->SetImportedChecksumAlgorithm( nDigestAlg );
332 *pEncryptionAlg >>= nEncryptionAlg;
333 pStream->SetImportedEncryptionAlgorithm( nEncryptionAlg );
335 if ( pDerivedKeySize )
336 *pDerivedKeySize >>= nDerivedKeySize;
337 pStream->SetImportedDerivedKeySize( nDerivedKeySize );
339 if ( pStartKeyAlg )
340 *pStartKeyAlg >>= nStartKeyAlg;
341 pStream->SetImportedStartKeyAlgorithm( nStartKeyAlg );
343 pStream->SetToBeCompressed ( true );
344 pStream->SetToBeEncrypted ( true );
345 pStream->SetIsEncrypted ( true );
346 if ( !m_bHasEncryptedEntries && pStream->getName() == "content.xml" )
348 m_bHasEncryptedEntries = true;
349 m_nStartKeyGenerationID = nStartKeyAlg;
350 m_nChecksumDigestID = nDigestAlg;
351 m_nCommonEncryptionID = nEncryptionAlg;
354 else
355 m_bHasNonEncryptedEntries = true;
360 bManifestParsed = true;
363 // now hide the manifest.xml file from user
364 xMetaInfFolder->removeByName( sManifest );
367 catch( Exception& )
369 if ( !m_bForceRecovery )
370 throw;
374 if ( !bManifestParsed && !m_bForceRecovery )
375 throw ZipIOException(
376 THROW_WHERE "Could not parse manifest.xml" );
378 const OUString sMimetype ("mimetype");
379 if ( m_xRootFolder->hasByName( sMimetype ) )
381 // get mediatype from the "mimetype" stream
382 OUString aPackageMediatype;
383 uno::Reference< lang::XUnoTunnel > xMimeTypeTunnel;
384 m_xRootFolder->getByName( sMimetype ) >>= xMimeTypeTunnel;
385 uno::Reference < io::XActiveDataSink > xMimeSink( xMimeTypeTunnel, UNO_QUERY );
386 if ( xMimeSink.is() )
388 uno::Reference< io::XInputStream > xMimeInStream = xMimeSink->getInputStream();
389 if ( xMimeInStream.is() )
391 // Mediatypes longer than 1024 symbols should not appear here
392 uno::Sequence< sal_Int8 > aData( 1024 );
393 sal_Int32 nRead = xMimeInStream->readBytes( aData, 1024 );
394 if ( nRead > aData.getLength() )
395 nRead = aData.getLength();
397 if ( nRead )
398 aPackageMediatype = OUString( reinterpret_cast<char const *>(aData.getConstArray()), nRead, RTL_TEXTENCODING_ASCII_US );
402 if ( !bManifestParsed )
404 // the manifest.xml could not be successfully parsed, this is an inconsistent package
405 if ( aPackageMediatype.startsWith("application/vnd.") )
407 // accept only types that look similar to own mediatypes
408 m_xRootFolder->SetMediaType( aPackageMediatype );
409 m_bMediaTypeFallbackUsed = true;
412 else if ( !m_bForceRecovery )
414 // the mimetype stream should contain the information from manifest.xml
415 if ( m_xRootFolder->GetMediaType() != aPackageMediatype )
416 throw ZipIOException(
417 THROW_WHERE
418 "mimetype conflicts with manifest.xml, \""
419 + m_xRootFolder->GetMediaType() + "\" vs. \""
420 + aPackageMediatype + "\"" );
423 m_xRootFolder->removeByName( sMimetype );
426 m_bInconsistent = m_xRootFolder->LookForUnexpectedODF12Streams( OUString() );
428 bool bODF12AndNewer = ( m_xRootFolder->GetVersion().compareTo( ODFVER_012_TEXT ) >= 0 );
429 if ( !m_bForceRecovery && bODF12AndNewer )
431 if ( m_bInconsistent )
433 // this is an ODF1.2 document that contains streams not referred in the manifest.xml;
434 // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent
435 // should be checked later
436 throw ZipIOException(
437 THROW_WHERE "there are streams not referred in manifest.xml" );
439 // all the streams should be encrypted with the same StartKey in ODF1.2
440 // TODO/LATER: in future the exception should be thrown
441 // throw ZipIOException( THROW_WHERE "More than one Start Key Generation algorithm is specified!" );
444 // in case it is a correct ODF1.2 document, the version must be set
445 // and the META-INF folder is reserved for package format
446 if ( bODF12AndNewer )
447 m_xRootFolder->removeByName( sMeta );
451 void ZipPackage::parseContentType()
453 if ( m_nFormat == embed::StorageFormats::OFOPXML )
455 const OUString aContentTypes("[Content_Types].xml");
456 try {
457 // the content type must exist in OFOPXML format!
458 if ( !m_xRootFolder->hasByName( aContentTypes ) )
459 throw io::IOException(THROW_WHERE "Wrong format!" );
461 uno::Reference< lang::XUnoTunnel > xTunnel;
462 uno::Any aAny = m_xRootFolder->getByName( aContentTypes );
463 aAny >>= xTunnel;
464 uno::Reference < io::XActiveDataSink > xSink( xTunnel, UNO_QUERY );
465 if ( xSink.is() )
467 uno::Reference< io::XInputStream > xInStream = xSink->getInputStream();
468 if ( xInStream.is() )
470 // here aContentTypeInfo[0] - Defaults, and aContentTypeInfo[1] - Overrides
471 const uno::Sequence< uno::Sequence< beans::StringPair > > aContentTypeInfo =
472 ::comphelper::OFOPXMLHelper::ReadContentTypeSequence( xInStream, m_xContext );
474 if ( aContentTypeInfo.getLength() != 2 )
475 throw io::IOException(THROW_WHERE );
477 // set the implicit types first
478 for ( const auto& rPair : aContentTypeInfo[0] )
479 m_xRootFolder->setChildStreamsTypeByExtension( rPair );
481 // now set the explicit types
482 for ( const auto& rPair : aContentTypeInfo[1] )
484 OUString aPath;
485 if ( rPair.First.toChar() == '/' )
486 aPath = rPair.First.copy( 1 );
487 else
488 aPath = rPair.First;
490 if ( !aPath.isEmpty() && hasByHierarchicalName( aPath ) )
492 uno::Any aIterAny = getByHierarchicalName( aPath );
493 uno::Reference < lang::XUnoTunnel > xIterTunnel;
494 aIterAny >>= xIterTunnel;
495 sal_Int64 nTest = xIterTunnel->getSomething( ZipPackageStream::getUnoTunnelId() );
496 if ( nTest != 0 )
498 // this is a package stream, in OFOPXML format only streams can have mediatype
499 ZipPackageStream *pStream = reinterpret_cast < ZipPackageStream* > ( nTest );
500 pStream->SetMediaType( rPair.Second );
507 m_xRootFolder->removeByName( aContentTypes );
509 catch( uno::Exception& )
511 if ( !m_bForceRecovery )
512 throw;
517 void ZipPackage::getZipFileContents()
519 std::unique_ptr<ZipEnumeration> xEnum = m_pZipFile->entries();
520 OUString sTemp, sDirName;
521 sal_Int32 nOldIndex, nStreamIndex;
522 FolderHash::iterator aIter;
524 while (xEnum->hasMoreElements())
526 nOldIndex = 0;
527 ZipPackageFolder* pCurrent = m_xRootFolder.get();
528 const ZipEntry & rEntry = *xEnum->nextElement();
529 OUString rName = rEntry.sPath;
531 if ( m_bForceRecovery )
533 // the PKZIP Application note version 6.2 does not allows to use '\' as separator
534 // unfortunately it is used by some implementations, so we have to support it in recovery mode
535 rName = rName.replace( '\\', '/' );
538 nStreamIndex = rName.lastIndexOf ( '/' );
539 if ( nStreamIndex != -1 )
541 sDirName = rName.copy ( 0, nStreamIndex );
542 aIter = m_aRecent.find ( sDirName );
543 if ( aIter != m_aRecent.end() )
544 pCurrent = ( *aIter ).second;
547 if ( pCurrent == m_xRootFolder.get() )
549 sal_Int32 nIndex;
550 while ( ( nIndex = rName.indexOf( '/', nOldIndex ) ) != -1 )
552 sTemp = rName.copy ( nOldIndex, nIndex - nOldIndex );
553 if ( nIndex == nOldIndex )
554 break;
555 if ( !pCurrent->hasByName( sTemp ) )
557 ZipPackageFolder* pPkgFolder = new ZipPackageFolder(m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
558 pPkgFolder->setName( sTemp );
559 pPkgFolder->doSetParent( pCurrent );
560 pCurrent = pPkgFolder;
562 else
564 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
565 if (!rInfo.bFolder)
566 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
567 pCurrent = rInfo.pFolder;
569 nOldIndex = nIndex+1;
571 if ( nStreamIndex != -1 && !sDirName.isEmpty() )
572 m_aRecent [ sDirName ] = pCurrent;
574 if ( rName.getLength() -1 != nStreamIndex )
576 nStreamIndex++;
577 sTemp = rName.copy( nStreamIndex );
579 if (!pCurrent->hasByName(sTemp))
581 ZipPackageStream *pPkgStream = new ZipPackageStream(*this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert);
582 pPkgStream->SetPackageMember(true);
583 pPkgStream->setZipEntryOnLoading(rEntry);
584 pPkgStream->setName(sTemp);
585 pPkgStream->doSetParent(pCurrent);
590 if ( m_nFormat == embed::StorageFormats::PACKAGE )
591 parseManifest();
592 else if ( m_nFormat == embed::StorageFormats::OFOPXML )
593 parseContentType();
596 void SAL_CALL ZipPackage::initialize( const uno::Sequence< Any >& aArguments )
598 beans::NamedValue aNamedValue;
600 if ( aArguments.hasElements() )
602 bool bHaveZipFile = true;
604 for( const auto& rArgument : aArguments )
606 OUString aParamUrl;
607 if ( rArgument >>= aParamUrl )
609 m_eMode = e_IMode_URL;
612 sal_Int32 nParam = aParamUrl.indexOf( '?' );
613 if ( nParam >= 0 )
615 m_aURL = aParamUrl.copy( 0, nParam );
616 OUString aParam = aParamUrl.copy( nParam + 1 );
618 sal_Int32 nIndex = 0;
621 OUString aCommand = aParam.getToken( 0, '&', nIndex );
622 if ( aCommand == "repairpackage" )
624 m_bForceRecovery = true;
625 break;
627 else if ( aCommand == "purezip" )
629 m_nFormat = embed::StorageFormats::ZIP;
630 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
631 break;
633 else if ( aCommand == "ofopxml" )
635 m_nFormat = embed::StorageFormats::OFOPXML;
636 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
637 break;
640 while ( nIndex >= 0 );
642 else
643 m_aURL = aParamUrl;
645 Content aContent(
646 m_aURL, uno::Reference< XCommandEnvironment >(),
647 m_xContext );
648 Any aAny = aContent.getPropertyValue("Size");
649 sal_uInt64 aSize = 0;
650 // kind of optimization: treat empty files as nonexistent files
651 // and write to such files directly. Note that "Size" property is optional.
652 bool bHasSizeProperty = aAny >>= aSize;
653 if( !bHasSizeProperty || aSize )
655 uno::Reference < XActiveDataSink > xSink = new ZipPackageSink;
656 if ( aContent.openStream ( xSink ) )
657 m_xContentStream = xSink->getInputStream();
659 else
660 bHaveZipFile = false;
662 catch ( css::uno::Exception& )
664 // Exception derived from uno::Exception thrown. This probably
665 // means the file doesn't exist...we'll create it at
666 // commitChanges time
667 bHaveZipFile = false;
670 else if ( rArgument >>= m_xStream )
672 // a writable stream can implement both XStream & XInputStream
673 m_eMode = e_IMode_XStream;
674 m_xContentStream = m_xStream->getInputStream();
676 else if ( rArgument >>= m_xContentStream )
678 m_eMode = e_IMode_XInputStream;
680 else if ( rArgument >>= aNamedValue )
682 if ( aNamedValue.Name == "RepairPackage" )
683 aNamedValue.Value >>= m_bForceRecovery;
684 else if ( aNamedValue.Name == "PackageFormat" )
686 // setting this argument to true means Package format
687 // setting it to false means plain Zip format
689 bool bPackFormat = true;
690 aNamedValue.Value >>= bPackFormat;
691 if ( !bPackFormat )
692 m_nFormat = embed::StorageFormats::ZIP;
694 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
696 else if ( aNamedValue.Name == "StorageFormat" )
698 OUString aFormatName;
699 sal_Int32 nFormatID = 0;
700 if ( aNamedValue.Value >>= aFormatName )
702 if ( aFormatName == PACKAGE_STORAGE_FORMAT_STRING )
703 m_nFormat = embed::StorageFormats::PACKAGE;
704 else if ( aFormatName == ZIP_STORAGE_FORMAT_STRING )
705 m_nFormat = embed::StorageFormats::ZIP;
706 else if ( aFormatName == OFOPXML_STORAGE_FORMAT_STRING )
707 m_nFormat = embed::StorageFormats::OFOPXML;
708 else
709 throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
711 else if ( aNamedValue.Value >>= nFormatID )
713 if (nFormatID != embed::StorageFormats::PACKAGE
714 && nFormatID != embed::StorageFormats::ZIP
715 && nFormatID != embed::StorageFormats::OFOPXML)
716 throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
718 m_nFormat = nFormatID;
720 else
721 throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );
723 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
725 else if ( aNamedValue.Name == "AllowRemoveOnInsert" )
727 aNamedValue.Value >>= m_bAllowRemoveOnInsert;
728 m_xRootFolder->setRemoveOnInsertMode_Impl( m_bAllowRemoveOnInsert );
730 else if (aNamedValue.Name == "NoFileSync")
731 aNamedValue.Value >>= m_bDisableFileSync;
733 // for now the progress handler is not used, probably it will never be
734 // if ( aNamedValue.Name == "ProgressHandler" )
736 else
738 // The URL is not acceptable
739 throw css::uno::Exception (THROW_WHERE "Bad arguments.",
740 static_cast < ::cppu::OWeakObject * > ( this ) );
746 if ( m_xContentStream.is() )
748 // the stream must be seekable, if it is not it will be wrapped
749 m_xContentStream = ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream, m_xContext );
750 m_xContentSeek.set( m_xContentStream, UNO_QUERY_THROW );
751 if ( !m_xContentSeek->getLength() )
752 bHaveZipFile = false;
754 else
755 bHaveZipFile = false;
757 catch ( css::uno::Exception& )
759 // Exception derived from uno::Exception thrown. This probably
760 // means the file doesn't exist...we'll create it at
761 // commitChanges time
762 bHaveZipFile = false;
764 if ( bHaveZipFile )
766 bool bBadZipFile = false;
767 OUString message;
770 m_pZipFile = std::make_unique<ZipFile>(m_aMutexHolder, m_xContentStream, m_xContext, true, m_bForceRecovery);
771 getZipFileContents();
773 catch ( IOException & e )
775 bBadZipFile = true;
776 message = "IOException: " + e.Message;
778 catch ( ZipException & e )
780 bBadZipFile = true;
781 message = "ZipException: " + e.Message;
783 catch ( Exception & )
785 m_pZipFile.reset();
786 throw;
789 if ( bBadZipFile )
791 // clean up the memory, and tell the UCB about the error
792 m_pZipFile.reset();
794 throw css::packages::zip::ZipIOException (
795 THROW_WHERE "Bad Zip File, " + message,
796 static_cast < ::cppu::OWeakObject * > ( this ) );
802 Any SAL_CALL ZipPackage::getByHierarchicalName( const OUString& aName )
804 OUString sTemp, sDirName;
805 sal_Int32 nOldIndex, nStreamIndex;
806 FolderHash::iterator aIter;
808 sal_Int32 nIndex = aName.getLength();
810 if (aName == "/")
811 // root directory.
812 return makeAny ( uno::Reference < XUnoTunnel > ( m_xRootFolder.get() ) );
814 nStreamIndex = aName.lastIndexOf ( '/' );
815 bool bFolder = nStreamIndex == nIndex-1; // last character is '/'.
817 if ( nStreamIndex != -1 )
819 // The name contains '/'.
820 sDirName = aName.copy ( 0, nStreamIndex );
821 aIter = m_aRecent.find ( sDirName );
822 if ( aIter != m_aRecent.end() )
824 // There is a cached entry for this name.
826 ZipPackageFolder* pFolder = aIter->second;
828 if ( bFolder )
830 // Determine the directory name.
831 sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
832 sTemp = aName.copy ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
834 if (pFolder && sTemp == pFolder->getName())
835 return makeAny(uno::Reference<XUnoTunnel>(pFolder));
837 else
839 // Determine the file name.
840 sTemp = aName.copy ( nStreamIndex + 1 );
842 if (pFolder && pFolder->hasByName(sTemp))
843 return pFolder->getByName(sTemp);
846 m_aRecent.erase( aIter );
849 else if ( m_xRootFolder->hasByName ( aName ) )
850 // top-level element.
851 return m_xRootFolder->getByName ( aName );
853 // Not in the cache. Search normally.
855 nOldIndex = 0;
856 ZipPackageFolder * pCurrent = m_xRootFolder.get();
857 ZipPackageFolder * pPrevious = nullptr;
859 // Find the right directory for the given path.
861 while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
863 sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
864 if ( nIndex == nOldIndex )
865 break;
866 if ( !pCurrent->hasByName( sTemp ) )
867 throw NoSuchElementException(THROW_WHERE );
869 pPrevious = pCurrent;
870 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
871 if (!rInfo.bFolder)
872 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
873 pCurrent = rInfo.pFolder;
874 nOldIndex = nIndex+1;
877 if ( bFolder )
879 if ( nStreamIndex != -1 )
880 m_aRecent[sDirName] = pPrevious; // cache it.
881 return makeAny ( uno::Reference < XUnoTunnel > ( pCurrent ) );
884 sTemp = aName.copy( nOldIndex );
886 if ( pCurrent->hasByName ( sTemp ) )
888 if ( nStreamIndex != -1 )
889 m_aRecent[sDirName] = pCurrent; // cache it.
890 return pCurrent->getByName( sTemp );
893 throw NoSuchElementException(THROW_WHERE);
896 sal_Bool SAL_CALL ZipPackage::hasByHierarchicalName( const OUString& aName )
898 OUString sTemp, sDirName;
899 sal_Int32 nOldIndex, nStreamIndex;
900 FolderHash::iterator aIter;
902 sal_Int32 nIndex = aName.getLength();
904 if (aName == "/")
905 // root directory
906 return true;
910 nStreamIndex = aName.lastIndexOf ( '/' );
911 bool bFolder = nStreamIndex == nIndex-1;
912 if ( nStreamIndex != -1 )
914 sDirName = aName.copy ( 0, nStreamIndex );
915 aIter = m_aRecent.find ( sDirName );
916 if ( aIter != m_aRecent.end() )
918 if ( bFolder )
920 sal_Int32 nDirIndex = aName.lastIndexOf ( '/', nStreamIndex );
921 sTemp = aName.copy ( nDirIndex == -1 ? 0 : nDirIndex+1, nStreamIndex-nDirIndex-1 );
922 if ( sTemp == ( *aIter ).second->getName() )
923 return true;
924 else
925 m_aRecent.erase ( aIter );
927 else
929 sTemp = aName.copy ( nStreamIndex + 1 );
930 if ( ( *aIter ).second->hasByName( sTemp ) )
931 return true;
932 else
933 m_aRecent.erase( aIter );
937 else
939 if ( m_xRootFolder->hasByName ( aName ) )
940 return true;
942 ZipPackageFolder * pCurrent = m_xRootFolder.get();
943 ZipPackageFolder * pPrevious = nullptr;
944 nOldIndex = 0;
945 while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
947 sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
948 if ( nIndex == nOldIndex )
949 break;
950 if ( pCurrent->hasByName( sTemp ) )
952 pPrevious = pCurrent;
953 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
954 if (!rInfo.bFolder)
955 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
956 pCurrent = rInfo.pFolder;
958 else
959 return false;
960 nOldIndex = nIndex+1;
962 if ( bFolder )
964 m_aRecent[sDirName] = pPrevious;
965 return true;
967 else
969 sTemp = aName.copy( nOldIndex );
971 if ( pCurrent->hasByName( sTemp ) )
973 m_aRecent[sDirName] = pCurrent;
974 return true;
978 catch (const uno::RuntimeException &)
980 throw;
982 catch (const uno::Exception&)
984 uno::Any e(::cppu::getCaughtException());
985 throw lang::WrappedTargetRuntimeException("ZipPackage::hasByHierarchicalName", nullptr, e);
987 return false;
990 uno::Reference< XInterface > SAL_CALL ZipPackage::createInstance()
992 uno::Reference < XInterface > xRef = *( new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert ) );
993 return xRef;
996 uno::Reference< XInterface > SAL_CALL ZipPackage::createInstanceWithArguments( const uno::Sequence< Any >& aArguments )
998 bool bArg = false;
999 uno::Reference < XInterface > xRef;
1000 if ( aArguments.hasElements() )
1001 aArguments[0] >>= bArg;
1002 if ( bArg )
1003 xRef = *new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1004 else
1005 xRef = *new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1007 return xRef;
1010 void ZipPackage::WriteMimetypeMagicFile( ZipOutputStream& aZipOut )
1012 const OUString sMime ("mimetype");
1013 if ( m_xRootFolder->hasByName( sMime ) )
1014 m_xRootFolder->removeByName( sMime );
1016 ZipEntry * pEntry = new ZipEntry;
1017 sal_Int32 nBufferLength = m_xRootFolder->GetMediaType().getLength();
1018 OString sMediaType = OUStringToOString( m_xRootFolder->GetMediaType(), RTL_TEXTENCODING_ASCII_US );
1019 const uno::Sequence< sal_Int8 > aType( reinterpret_cast<sal_Int8 const *>(sMediaType.getStr()),
1020 nBufferLength );
1022 pEntry->sPath = sMime;
1023 pEntry->nMethod = STORED;
1024 pEntry->nSize = pEntry->nCompressedSize = nBufferLength;
1025 pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1027 CRC32 aCRC32;
1028 aCRC32.update( aType );
1029 pEntry->nCrc = aCRC32.getValue();
1033 ZipOutputStream::setEntry(pEntry);
1034 aZipOut.writeLOC(pEntry);
1035 aZipOut.rawWrite(aType);
1036 aZipOut.rawCloseEntry();
1038 catch ( const css::io::IOException & )
1040 css::uno::Any anyEx = cppu::getCaughtException();
1041 throw WrappedTargetException(
1042 THROW_WHERE "Error adding mimetype to the ZipOutputStream!",
1043 static_cast < OWeakObject * > ( this ),
1044 anyEx );
1048 void ZipPackage::WriteManifest( ZipOutputStream& aZipOut, const vector< uno::Sequence < PropertyValue > >& aManList )
1050 // Write the manifest
1051 uno::Reference < XManifestWriter > xWriter = ManifestWriter::create( m_xContext );
1052 ZipEntry * pEntry = new ZipEntry;
1053 ZipPackageBuffer *pBuffer = new ZipPackageBuffer;
1054 uno::Reference < XOutputStream > xManOutStream( *pBuffer, UNO_QUERY );
1056 pEntry->sPath = "META-INF/manifest.xml";
1057 pEntry->nMethod = DEFLATED;
1058 pEntry->nCrc = -1;
1059 pEntry->nSize = pEntry->nCompressedSize = -1;
1060 pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1062 xWriter->writeManifestSequence ( xManOutStream, comphelper::containerToSequence(aManList) );
1064 sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
1065 pBuffer->realloc( nBufferLength );
1067 // the manifest.xml is never encrypted - so pass an empty reference
1068 ZipOutputStream::setEntry(pEntry);
1069 aZipOut.writeLOC(pEntry);
1070 ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, *pEntry, nullptr, /*bEncrypt*/false);
1071 aZipEntry.write(pBuffer->getSequence());
1072 aZipEntry.closeEntry();
1073 aZipOut.rawCloseEntry();
1076 void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const vector< uno::Sequence < PropertyValue > >& aManList )
1078 ZipEntry* pEntry = new ZipEntry;
1079 ZipPackageBuffer *pBuffer = new ZipPackageBuffer;
1080 uno::Reference< io::XOutputStream > xConTypeOutStream( *pBuffer, UNO_QUERY );
1082 pEntry->sPath = "[Content_Types].xml";
1083 pEntry->nMethod = DEFLATED;
1084 pEntry->nCrc = -1;
1085 pEntry->nSize = pEntry->nCompressedSize = -1;
1086 pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1088 // Add default entries, the count must be updated manually when appending.
1089 uno::Sequence< beans::StringPair > aDefaultsSequence(4);
1090 // Add at least the standard default entries.
1091 aDefaultsSequence[0].First = "xml";
1092 aDefaultsSequence[0].Second= "application/xml";
1093 aDefaultsSequence[1].First = "rels";
1094 aDefaultsSequence[1].Second= "application/vnd.openxmlformats-package.relationships+xml";
1095 aDefaultsSequence[2].First = "png";
1096 aDefaultsSequence[2].Second= "image/png";
1097 aDefaultsSequence[3].First = "jpeg";
1098 aDefaultsSequence[3].Second= "image/jpeg";
1100 uno::Sequence< beans::StringPair > aOverridesSequence(aManList.size());
1101 sal_Int32 nOverSeqLength = 0;
1102 for (const auto& rMan : aManList)
1104 OUString aPath;
1105 OUString aType;
1106 OSL_ENSURE( rMan[PKG_MNFST_MEDIATYPE].Name == "MediaType" && rMan[PKG_MNFST_FULLPATH].Name == "FullPath",
1107 "The mediatype sequence format is wrong!" );
1108 rMan[PKG_MNFST_MEDIATYPE].Value >>= aType;
1109 if ( !aType.isEmpty() )
1111 // only nonempty type makes sense here
1112 rMan[PKG_MNFST_FULLPATH].Value >>= aPath;
1113 //FIXME: For now we have no way of differentiating defaults from others.
1114 aOverridesSequence[nOverSeqLength].First = "/" + aPath;
1115 aOverridesSequence[nOverSeqLength].Second = aType;
1116 ++nOverSeqLength;
1119 aOverridesSequence.realloc(nOverSeqLength);
1121 ::comphelper::OFOPXMLHelper::WriteContentSequence(
1122 xConTypeOutStream, aDefaultsSequence, aOverridesSequence, m_xContext );
1124 sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() );
1125 pBuffer->realloc( nBufferLength );
1127 // there is no encryption in this format currently
1128 ZipOutputStream::setEntry(pEntry);
1129 aZipOut.writeLOC(pEntry);
1130 ZipOutputEntry aZipEntry(aZipOut.getStream(), m_xContext, *pEntry, nullptr, /*bEncrypt*/false);
1131 aZipEntry.write(pBuffer->getSequence());
1132 aZipEntry.closeEntry();
1133 aZipOut.rawCloseEntry();
1136 void ZipPackage::ConnectTo( const uno::Reference< io::XInputStream >& xInStream )
1138 m_xContentSeek.set( xInStream, uno::UNO_QUERY_THROW );
1139 m_xContentStream = xInStream;
1141 // seek back to the beginning of the temp file so we can read segments from it
1142 m_xContentSeek->seek( 0 );
1143 if ( m_pZipFile )
1144 m_pZipFile->setInputStream( m_xContentStream );
1145 else
1146 m_pZipFile = std::make_unique<ZipFile>(m_aMutexHolder, m_xContentStream, m_xContext, false);
1149 namespace
1151 class RandomPool
1153 private:
1154 rtlRandomPool m_aRandomPool;
1155 public:
1156 RandomPool()
1158 m_aRandomPool = rtl_random_createPool ();
1160 rtlRandomPool get()
1162 return m_aRandomPool;
1164 ~RandomPool()
1166 // Clean up random pool memory
1167 rtl_random_destroyPool(m_aRandomPool);
1172 uno::Reference< io::XInputStream > ZipPackage::writeTempFile()
1174 // In case the target local file does not exist or empty
1175 // write directly to it otherwise create a temporary file to write to.
1176 // If a temporary file is created it is returned back by the method.
1177 // If the data written directly, xComponentStream will be switched here
1179 bool bUseTemp = true;
1180 uno::Reference < io::XInputStream > xResult;
1181 uno::Reference < io::XInputStream > xTempIn;
1183 uno::Reference < io::XOutputStream > xTempOut;
1184 uno::Reference< io::XActiveDataStreamer > xSink;
1186 if ( m_eMode == e_IMode_URL && !m_pZipFile && isLocalFile() )
1188 xSink = openOriginalForOutput();
1189 if( xSink.is() )
1191 uno::Reference< io::XStream > xStr = xSink->getStream();
1192 if( xStr.is() )
1194 xTempOut = xStr->getOutputStream();
1195 if( xTempOut.is() )
1196 bUseTemp = false;
1200 else if ( m_eMode == e_IMode_XStream && !m_pZipFile )
1202 // write directly to an empty stream
1203 xTempOut = m_xStream->getOutputStream();
1204 if( xTempOut.is() )
1205 bUseTemp = false;
1208 if( bUseTemp )
1210 // create temporary file
1211 uno::Reference < io::XTempFile > xTempFile( io::TempFile::create(m_xContext) );
1212 xTempOut.set( xTempFile->getOutputStream(), UNO_SET_THROW );
1213 xTempIn.set( xTempFile->getInputStream(), UNO_SET_THROW );
1216 // Hand it to the ZipOutputStream:
1217 ZipOutputStream aZipOut( xTempOut );
1220 if ( m_nFormat == embed::StorageFormats::PACKAGE )
1222 // Remove the old manifest.xml file as the
1223 // manifest will be re-generated and the
1224 // META-INF directory implicitly created if does not exist
1225 static const OUString sMeta ("META-INF");
1227 if ( m_xRootFolder->hasByName( sMeta ) )
1229 const OUString sManifest ("manifest.xml");
1231 uno::Reference< XUnoTunnel > xTunnel;
1232 Any aAny = m_xRootFolder->getByName( sMeta );
1233 aAny >>= xTunnel;
1234 uno::Reference< XNameContainer > xMetaInfFolder( xTunnel, UNO_QUERY );
1235 if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
1236 xMetaInfFolder->removeByName( sManifest );
1239 // Write a magic file with mimetype
1240 WriteMimetypeMagicFile( aZipOut );
1242 else if ( m_nFormat == embed::StorageFormats::OFOPXML )
1244 // Remove the old [Content_Types].xml file as the
1245 // file will be re-generated
1247 static const OUString aContentTypes("[Content_Types].xml");
1249 if ( m_xRootFolder->hasByName( aContentTypes ) )
1250 m_xRootFolder->removeByName( aContentTypes );
1253 // Create a vector to store data for the manifest.xml file
1254 vector < uno::Sequence < PropertyValue > > aManList;
1256 static const OUString sMediaType("MediaType");
1257 static const OUString sVersion("Version");
1258 static const OUString sFullPath("FullPath");
1259 const bool bIsGpgEncrypt = m_aGpgProps.hasElements();
1261 if ( m_nFormat == embed::StorageFormats::PACKAGE )
1263 uno::Sequence < PropertyValue > aPropSeq(
1264 bIsGpgEncrypt ? PKG_SIZE_NOENCR_MNFST+1 : PKG_SIZE_NOENCR_MNFST );
1265 aPropSeq [PKG_MNFST_MEDIATYPE].Name = sMediaType;
1266 aPropSeq [PKG_MNFST_MEDIATYPE].Value <<= m_xRootFolder->GetMediaType();
1267 aPropSeq [PKG_MNFST_VERSION].Name = sVersion;
1268 aPropSeq [PKG_MNFST_VERSION].Value <<= m_xRootFolder->GetVersion();
1269 aPropSeq [PKG_MNFST_FULLPATH].Name = sFullPath;
1270 aPropSeq [PKG_MNFST_FULLPATH].Value <<= OUString("/");
1272 if( bIsGpgEncrypt )
1274 aPropSeq[PKG_SIZE_NOENCR_MNFST].Name = "KeyInfo";
1275 aPropSeq[PKG_SIZE_NOENCR_MNFST].Value <<= m_aGpgProps;
1277 aManList.push_back( aPropSeq );
1281 // This will be used to generate random salt and initialisation vectors
1282 // for encrypted streams
1283 RandomPool aRandomPool;
1285 sal_Int32 const nPBKDF2IterationCount = 100000;
1287 // call saveContents ( it will recursively save sub-directories
1288 m_xRootFolder->saveContents("", aManList, aZipOut, GetEncryptionKey(), bIsGpgEncrypt ? 0 : nPBKDF2IterationCount, aRandomPool.get());
1291 if( m_nFormat == embed::StorageFormats::PACKAGE )
1293 WriteManifest( aZipOut, aManList );
1295 else if( m_nFormat == embed::StorageFormats::OFOPXML )
1297 WriteContentTypes( aZipOut, aManList );
1300 aZipOut.finish();
1302 if( bUseTemp )
1303 xResult = xTempIn;
1305 // Update our References to point to the new temp file
1306 if( !bUseTemp )
1308 // the case when the original contents were written directly
1309 xTempOut->flush();
1311 // in case the stream is based on a file it will implement the following interface
1312 // the call should be used to be sure that the contents are written to the file system
1313 uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( xTempOut, uno::UNO_QUERY );
1314 if (asyncOutputMonitor.is() && !m_bDisableFileSync)
1315 asyncOutputMonitor->waitForCompletion();
1317 // no need to postpone switching to the new stream since the target was written directly
1318 uno::Reference< io::XInputStream > xNewStream;
1319 if ( m_eMode == e_IMode_URL )
1320 xNewStream = xSink->getStream()->getInputStream();
1321 else if ( m_eMode == e_IMode_XStream && m_xStream.is() )
1322 xNewStream = m_xStream->getInputStream();
1324 if ( xNewStream.is() )
1325 ConnectTo( xNewStream );
1328 catch ( uno::Exception& )
1330 if( bUseTemp )
1332 // no information loss appears, thus no special handling is required
1333 uno::Any aCaught( ::cppu::getCaughtException() );
1335 // it is allowed to throw WrappedTargetException
1336 WrappedTargetException aException;
1337 if ( aCaught >>= aException )
1338 throw aException;
1340 throw WrappedTargetException(
1341 THROW_WHERE "Problem writing the original content!",
1342 static_cast < OWeakObject * > ( this ),
1343 aCaught );
1345 else
1347 // the document is written directly, although it was empty it is important to notify that the writing has failed
1348 // TODO/LATER: let the package be able to recover in this situation
1349 OUString aErrTxt(THROW_WHERE "This package is unusable!");
1350 embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), OUString() );
1351 throw WrappedTargetException( aErrTxt,
1352 static_cast < OWeakObject * > ( this ),
1353 makeAny ( aException ) );
1357 return xResult;
1360 uno::Reference< XActiveDataStreamer > ZipPackage::openOriginalForOutput()
1362 // open and truncate the original file
1363 Content aOriginalContent(
1364 m_aURL, uno::Reference< XCommandEnvironment >(),
1365 m_xContext );
1366 uno::Reference< XActiveDataStreamer > xSink = new ActiveDataStreamer;
1368 if ( m_eMode == e_IMode_URL )
1372 bool bTruncSuccess = false;
1376 Exception aDetect;
1377 Any aAny = aOriginalContent.setPropertyValue("Size", makeAny( sal_Int64(0) ) );
1378 if( !( aAny >>= aDetect ) )
1379 bTruncSuccess = true;
1381 catch( Exception& )
1385 if( !bTruncSuccess )
1387 // the file is not accessible
1388 // just try to write an empty stream to it
1390 uno::Reference< XInputStream > xTempIn = new DummyInputStream; //uno::Reference< XInputStream >( xTempOut, UNO_QUERY );
1391 aOriginalContent.writeStream( xTempIn , true );
1394 OpenCommandArgument2 aArg;
1395 aArg.Mode = OpenMode::DOCUMENT;
1396 aArg.Priority = 0; // unused
1397 aArg.Sink = xSink;
1398 aArg.Properties = uno::Sequence< Property >( 0 ); // unused
1400 aOriginalContent.executeCommand("open", makeAny( aArg ) );
1402 catch( Exception& )
1404 // seems to be nonlocal file
1405 // temporary file mechanics should be used
1409 return xSink;
1412 void SAL_CALL ZipPackage::commitChanges()
1414 // lock the component for the time of committing
1415 ::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
1417 if ( m_eMode == e_IMode_XInputStream )
1419 IOException aException;
1420 throw WrappedTargetException(THROW_WHERE "This package is read only!",
1421 static_cast < OWeakObject * > ( this ), makeAny ( aException ) );
1423 // first the writeTempFile is called, if it returns a stream the stream should be written to the target
1424 // if no stream was returned, the file was written directly, nothing should be done
1425 uno::Reference< io::XInputStream > xTempInStream;
1428 xTempInStream = writeTempFile();
1430 catch (const ucb::ContentCreationException&)
1432 css::uno::Any anyEx = cppu::getCaughtException();
1433 throw WrappedTargetException(THROW_WHERE "Temporary file should be creatable!",
1434 static_cast < OWeakObject * > ( this ), anyEx );
1436 if ( xTempInStream.is() )
1438 uno::Reference< io::XSeekable > xTempSeek( xTempInStream, uno::UNO_QUERY_THROW );
1442 xTempSeek->seek( 0 );
1444 catch( const uno::Exception& )
1446 css::uno::Any anyEx = cppu::getCaughtException();
1447 throw WrappedTargetException(THROW_WHERE "Temporary file should be seekable!",
1448 static_cast < OWeakObject * > ( this ), anyEx );
1453 // connect to the temporary stream
1454 ConnectTo( xTempInStream );
1456 catch( const io::IOException& )
1458 css::uno::Any anyEx = cppu::getCaughtException();
1459 throw WrappedTargetException(THROW_WHERE "Temporary file should be connectable!",
1460 static_cast < OWeakObject * > ( this ), anyEx );
1463 if ( m_eMode == e_IMode_XStream )
1465 // First truncate our output stream
1466 uno::Reference < XOutputStream > xOutputStream;
1468 // preparation for copy step
1471 xOutputStream = m_xStream->getOutputStream();
1473 // Make sure we avoid a situation where the current position is
1474 // not zero, but the underlying file is truncated in the
1475 // meantime.
1476 uno::Reference<io::XSeekable> xSeekable(xOutputStream, uno::UNO_QUERY);
1477 if (xSeekable.is())
1478 xSeekable->seek(0);
1480 uno::Reference < XTruncate > xTruncate ( xOutputStream, UNO_QUERY_THROW );
1482 // after successful truncation the original file contents are already lost
1483 xTruncate->truncate();
1485 catch( const uno::Exception& )
1487 css::uno::Any anyEx = cppu::getCaughtException();
1488 throw WrappedTargetException(THROW_WHERE "This package is read only!",
1489 static_cast < OWeakObject * > ( this ), anyEx );
1494 // then copy the contents of the tempfile to our output stream
1495 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, xOutputStream );
1496 xOutputStream->flush();
1497 uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor(
1498 xOutputStream, uno::UNO_QUERY );
1499 if ( asyncOutputMonitor.is() ) {
1500 asyncOutputMonitor->waitForCompletion();
1503 catch( uno::Exception& )
1505 // if anything goes wrong in this block the target file becomes corrupted
1506 // so an exception should be thrown as a notification about it
1507 // and the package must disconnect from the stream
1508 DisconnectFromTargetAndThrowException_Impl( xTempInStream );
1511 else if ( m_eMode == e_IMode_URL )
1513 uno::Reference< XOutputStream > aOrigFileStream;
1514 bool bCanBeCorrupted = false;
1516 if( isLocalFile() )
1518 // write directly in case of local file
1519 uno::Reference< css::ucb::XSimpleFileAccess3 > xSimpleAccess(
1520 SimpleFileAccess::create( m_xContext ) );
1521 OSL_ENSURE( xSimpleAccess.is(), "Can't instantiate SimpleFileAccess service!" );
1522 uno::Reference< io::XTruncate > xOrigTruncate;
1523 if ( xSimpleAccess.is() )
1527 aOrigFileStream = xSimpleAccess->openFileWrite( m_aURL );
1528 xOrigTruncate.set( aOrigFileStream, uno::UNO_QUERY_THROW );
1529 // after successful truncation the file is already corrupted
1530 xOrigTruncate->truncate();
1532 catch( uno::Exception& )
1536 if( xOrigTruncate.is() )
1540 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream, aOrigFileStream );
1541 aOrigFileStream->closeOutput();
1543 catch( uno::Exception& )
1545 try {
1546 aOrigFileStream->closeOutput();
1547 } catch ( uno::Exception& ) {}
1549 aOrigFileStream.clear();
1550 // the original file can already be corrupted
1551 bCanBeCorrupted = true;
1556 if( !aOrigFileStream.is() )
1560 uno::Reference < XPropertySet > xPropSet ( xTempInStream, UNO_QUERY_THROW );
1562 OUString sTargetFolder = m_aURL.copy ( 0, m_aURL.lastIndexOf ( u'/' ) );
1563 Content aContent(
1564 sTargetFolder, uno::Reference< XCommandEnvironment >(),
1565 m_xContext );
1567 OUString sTempURL;
1568 Any aAny = xPropSet->getPropertyValue ("Uri");
1569 aAny >>= sTempURL;
1571 TransferInfo aInfo;
1572 aInfo.NameClash = NameClash::OVERWRITE;
1573 aInfo.MoveData = false;
1574 aInfo.SourceURL = sTempURL;
1575 aInfo.NewTitle = rtl::Uri::decode ( m_aURL.copy ( 1 + m_aURL.lastIndexOf ( u'/' ) ),
1576 rtl_UriDecodeWithCharset,
1577 RTL_TEXTENCODING_UTF8 );
1578 // if the file is still not corrupted, it can become after the next step
1579 aContent.executeCommand ("transfer", Any(aInfo) );
1581 catch ( const css::uno::Exception& )
1583 if ( bCanBeCorrupted )
1584 DisconnectFromTargetAndThrowException_Impl( xTempInStream );
1586 css::uno::Any anyEx = cppu::getCaughtException();
1587 throw WrappedTargetException(
1588 THROW_WHERE "This package may be read only!",
1589 static_cast < OWeakObject * > ( this ),
1590 anyEx );
1596 // after successful storing it can be set to false
1597 m_bMediaTypeFallbackUsed = false;
1600 void ZipPackage::DisconnectFromTargetAndThrowException_Impl( const uno::Reference< io::XInputStream >& xTempStream )
1602 m_xStream.set( xTempStream, uno::UNO_QUERY );
1603 if ( m_xStream.is() )
1604 m_eMode = e_IMode_XStream;
1605 else
1606 m_eMode = e_IMode_XInputStream;
1608 OUString aTempURL;
1609 try {
1610 uno::Reference< beans::XPropertySet > xTempFile( xTempStream, uno::UNO_QUERY_THROW );
1611 uno::Any aUrl = xTempFile->getPropertyValue("Uri");
1612 aUrl >>= aTempURL;
1613 xTempFile->setPropertyValue("RemoveFile",
1614 uno::makeAny( false ) );
1616 catch ( uno::Exception& )
1618 OSL_FAIL( "These calls are pretty simple, they should not fail!" );
1621 OUString aErrTxt(THROW_WHERE "This package is read only!");
1622 embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), aTempURL );
1623 throw WrappedTargetException( aErrTxt,
1624 static_cast < OWeakObject * > ( this ),
1625 makeAny ( aException ) );
1628 uno::Sequence< sal_Int8 > ZipPackage::GetEncryptionKey()
1630 uno::Sequence< sal_Int8 > aResult;
1632 if ( m_aStorageEncryptionKeys.hasElements() )
1634 OUString aNameToFind;
1635 if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA256 )
1636 aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
1637 else if ( m_nStartKeyGenerationID == xml::crypto::DigestID::SHA1 )
1638 aNameToFind = PACKAGE_ENCRYPTIONDATA_SHA1CORRECT;
1639 else
1640 throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
1642 for ( const auto& rKey : std::as_const(m_aStorageEncryptionKeys) )
1643 if ( rKey.Name == aNameToFind )
1644 rKey.Value >>= aResult;
1646 // empty keys are not allowed here
1647 // so it is not important whether there is no key, or the key is empty, it is an error
1648 if ( !aResult.hasElements() )
1649 throw uno::RuntimeException(THROW_WHERE "No expected key is provided!" );
1651 else
1652 aResult = m_aEncryptionKey;
1654 return aResult;
1657 sal_Bool SAL_CALL ZipPackage::hasPendingChanges()
1659 return false;
1661 Sequence< ElementChange > SAL_CALL ZipPackage::getPendingChanges()
1663 return uno::Sequence < ElementChange > ();
1667 * Function to create a new component instance; is needed by factory helper implementation.
1668 * @param xMgr service manager to if the components needs other component instances
1670 static uno::Reference < XInterface > ZipPackage_createInstance(
1671 const uno::Reference< XMultiServiceFactory > & xMgr )
1673 return uno::Reference< XInterface >( *new ZipPackage( comphelper::getComponentContext(xMgr) ) );
1676 OUString ZipPackage::static_getImplementationName()
1678 return "com.sun.star.packages.comp.ZipPackage";
1681 Sequence< OUString > ZipPackage::static_getSupportedServiceNames()
1683 uno::Sequence<OUString> aNames { "com.sun.star.packages.Package" };
1684 return aNames;
1687 OUString ZipPackage::getImplementationName()
1689 return static_getImplementationName();
1692 Sequence< OUString > ZipPackage::getSupportedServiceNames()
1694 return static_getSupportedServiceNames();
1697 sal_Bool SAL_CALL ZipPackage::supportsService( OUString const & rServiceName )
1699 return cppu::supportsService(this, rServiceName);
1702 uno::Reference < XSingleServiceFactory > ZipPackage::createServiceFactory( uno::Reference < XMultiServiceFactory > const & rServiceFactory )
1704 return cppu::createSingleFactory ( rServiceFactory,
1705 static_getImplementationName(),
1706 ZipPackage_createInstance,
1707 static_getSupportedServiceNames() );
1710 Sequence< sal_Int8 > ZipPackage::getUnoTunnelId()
1712 static ::cppu::OImplementationId implId;
1714 return implId.getImplementationId();
1717 sal_Int64 SAL_CALL ZipPackage::getSomething( const uno::Sequence< sal_Int8 >& aIdentifier )
1719 if ( isUnoTunnelId<ZipPackage>(aIdentifier) )
1720 return reinterpret_cast < sal_Int64 > ( this );
1721 return 0;
1724 uno::Reference< XPropertySetInfo > SAL_CALL ZipPackage::getPropertySetInfo()
1726 return uno::Reference < XPropertySetInfo > ();
1729 void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
1731 if ( m_nFormat != embed::StorageFormats::PACKAGE )
1732 throw UnknownPropertyException(aPropertyName);
1734 if (aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY
1735 ||aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY
1736 ||aPropertyName == IS_INCONSISTENT_PROPERTY
1737 ||aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY)
1738 throw PropertyVetoException(THROW_WHERE );
1739 else if ( aPropertyName == ENCRYPTION_KEY_PROPERTY )
1741 if ( !( aValue >>= m_aEncryptionKey ) )
1742 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
1744 m_aStorageEncryptionKeys.realloc( 0 );
1746 else if ( aPropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
1748 // this property is only necessary to support raw passwords in storage API;
1749 // because of this support the storage has to operate with more than one key dependent on storage generation algorithm;
1750 // when this support is removed, the storage will get only one key from outside
1751 uno::Sequence< beans::NamedValue > aKeys;
1752 if ( !( aValue >>= aKeys ) )
1753 throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 2 );
1755 if ( aKeys.hasElements() )
1757 bool bHasSHA256 = false;
1758 bool bHasSHA1 = false;
1759 for ( const auto& rKey : std::as_const(aKeys) )
1761 if ( rKey.Name == PACKAGE_ENCRYPTIONDATA_SHA256UTF8 )
1762 bHasSHA256 = true;
1763 if ( rKey.Name == PACKAGE_ENCRYPTIONDATA_SHA1UTF8 )
1764 bHasSHA1 = true;
1767 if ( !bHasSHA256 && !bHasSHA1 )
1768 throw IllegalArgumentException(THROW_WHERE "Expected keys are not provided!", uno::Reference< uno::XInterface >(), 2 );
1771 m_aStorageEncryptionKeys = aKeys;
1772 m_aEncryptionKey.realloc( 0 );
1774 else if ( aPropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
1776 uno::Sequence< beans::NamedValue > aAlgorithms;
1777 if ( m_pZipFile || !( aValue >>= aAlgorithms ) || !aAlgorithms.hasElements() )
1779 // the algorithms can not be changed if the file has a persistence based on the algorithms ( m_pZipFile )
1780 throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
1783 for ( const auto& rAlgorithm : std::as_const(aAlgorithms) )
1785 if ( rAlgorithm.Name == "StartKeyGenerationAlgorithm" )
1787 sal_Int32 nID = 0;
1788 if ( !( rAlgorithm.Value >>= nID )
1789 || ( nID != xml::crypto::DigestID::SHA256 && nID != xml::crypto::DigestID::SHA1 ) )
1790 throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference< uno::XInterface >(), 2 );
1792 m_nStartKeyGenerationID = nID;
1794 else if ( rAlgorithm.Name == "EncryptionAlgorithm" )
1796 sal_Int32 nID = 0;
1797 if ( !( rAlgorithm.Value >>= nID )
1798 || ( nID != xml::crypto::CipherID::AES_CBC_W3C_PADDING && nID != xml::crypto::CipherID::BLOWFISH_CFB_8 ) )
1799 throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference< uno::XInterface >(), 2 );
1801 m_nCommonEncryptionID = nID;
1803 else if ( rAlgorithm.Name == "ChecksumAlgorithm" )
1805 sal_Int32 nID = 0;
1806 if ( !( rAlgorithm.Value >>= nID )
1807 || ( nID != xml::crypto::DigestID::SHA1_1K && nID != xml::crypto::DigestID::SHA256_1K ) )
1808 throw IllegalArgumentException(THROW_WHERE "Unexpected start key generation algorithm is provided!", uno::Reference< uno::XInterface >(), 2 );
1810 m_nChecksumDigestID = nID;
1812 else
1814 OSL_ENSURE( false, "Unexpected encryption algorithm is provided!" );
1815 throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
1819 else if ( aPropertyName == ENCRYPTION_GPG_PROPERTIES )
1821 uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProps;
1822 if ( !( aValue >>= aGpgProps ) || !aGpgProps.hasElements() )
1824 throw IllegalArgumentException(THROW_WHERE "unexpected Gpg properties are provided.", uno::Reference< uno::XInterface >(), 2 );
1827 m_aGpgProps = aGpgProps;
1829 // override algorithm defaults (which are some legacy ODF
1830 // defaults) with reasonable values
1831 m_nStartKeyGenerationID = 0; // this is unused for PGP
1832 m_nCommonEncryptionID = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
1833 m_nChecksumDigestID = xml::crypto::DigestID::SHA512_1K;
1835 else
1836 throw UnknownPropertyException(aPropertyName);
1839 Any SAL_CALL ZipPackage::getPropertyValue( const OUString& PropertyName )
1841 // TODO/LATER: Activate the check when zip-ucp is ready
1842 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
1843 // throw UnknownPropertyException(THROW_WHERE );
1845 if ( PropertyName == ENCRYPTION_KEY_PROPERTY )
1847 return Any(m_aEncryptionKey);
1849 else if ( PropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
1851 ::comphelper::SequenceAsHashMap aAlgorithms;
1852 aAlgorithms["StartKeyGenerationAlgorithm"] <<= m_nStartKeyGenerationID;
1853 aAlgorithms["EncryptionAlgorithm"] <<= m_nCommonEncryptionID;
1854 aAlgorithms["ChecksumAlgorithm"] <<= m_nChecksumDigestID;
1855 return Any(aAlgorithms.getAsConstNamedValueList());
1857 if ( PropertyName == STORAGE_ENCRYPTION_KEYS_PROPERTY )
1859 return Any(m_aStorageEncryptionKeys);
1861 else if ( PropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY )
1863 return Any(m_bHasEncryptedEntries);
1865 else if ( PropertyName == ENCRYPTION_GPG_PROPERTIES )
1867 return Any(m_aGpgProps);
1869 else if ( PropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY )
1871 return Any(m_bHasNonEncryptedEntries);
1873 else if ( PropertyName == IS_INCONSISTENT_PROPERTY )
1875 return Any(m_bInconsistent);
1877 else if ( PropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY )
1879 return Any(m_bMediaTypeFallbackUsed);
1881 throw UnknownPropertyException(PropertyName);
1883 void SAL_CALL ZipPackage::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
1886 void SAL_CALL ZipPackage::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*aListener*/ )
1889 void SAL_CALL ZipPackage::addVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
1892 void SAL_CALL ZipPackage::removeVetoableChangeListener( const OUString& /*PropertyName*/, const uno::Reference< XVetoableChangeListener >& /*aListener*/ )
1896 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */