1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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
106 #define THROW_WHERE ""
109 class ActiveDataStreamer
: public ::cppu::WeakImplHelper
< XActiveDataStreamer
>
111 uno::Reference
< XStream
> mStream
;
114 virtual uno::Reference
< XStream
> SAL_CALL
getStream() override
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
126 virtual sal_Int32 SAL_CALL
readSomeBytes( uno::Sequence
< sal_Int8
>&, sal_Int32
) override
129 virtual void SAL_CALL
skipBytes( sal_Int32
) override
132 virtual sal_Int32 SAL_CALL
available() override
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");
177 uno::Reference
< XUnoTunnel
> xTunnel
;
178 Any aAny
= m_xRootFolder
->getByName( sMeta
);
180 uno::Reference
< XNameContainer
> xMetaInfFolder( xTunnel
, UNO_QUERY
);
181 if ( xMetaInfFolder
.is() && xMetaInfFolder
->hasByName( sManifest
) )
183 aAny
= xMetaInfFolder
->getByName( sManifest
);
185 uno::Reference
< XActiveDataSink
> xSink ( xTunnel
, UNO_QUERY
);
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
;
249 if ( (nTest
= xUnoTunnel
->getSomething( ZipPackageFolder::getUnoTunnelId() )) != 0 )
251 pFolder
= reinterpret_cast < ZipPackageFolder
* > ( nTest
);
252 pFolder
->SetMediaType ( sMediaType
);
253 pFolder
->SetVersion ( sVersion
);
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
;
265 sal_Int32 nDigestAlg
= 0, nEncryptionAlg
= 0;
267 pStream
->SetToBeEncrypted ( true );
269 *pVector
>>= aSequence
;
270 pStream
->setInitialisationVector ( aSequence
);
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
;
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
);
321 pStream
->setIterationCount ( nCount
);
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
);
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
;
355 m_bHasNonEncryptedEntries
= true;
360 bManifestParsed
= true;
363 // now hide the manifest.xml file from user
364 xMetaInfFolder
->removeByName( sManifest
);
369 if ( !m_bForceRecovery
)
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();
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(
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");
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
);
464 uno::Reference
< io::XActiveDataSink
> xSink( xTunnel
, UNO_QUERY
);
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] )
485 if ( rPair
.First
.toChar() == '/' )
486 aPath
= rPair
.First
.copy( 1 );
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() );
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
)
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())
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() )
550 while ( ( nIndex
= rName
.indexOf( '/', nOldIndex
) ) != -1 )
552 sTemp
= rName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
553 if ( nIndex
== nOldIndex
)
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
;
564 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
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
)
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
)
592 else if ( m_nFormat
== embed::StorageFormats::OFOPXML
)
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
)
607 if ( rArgument
>>= aParamUrl
)
609 m_eMode
= e_IMode_URL
;
612 sal_Int32 nParam
= aParamUrl
.indexOf( '?' );
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;
627 else if ( aCommand
== "purezip" )
629 m_nFormat
= embed::StorageFormats::ZIP
;
630 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
633 else if ( aCommand
== "ofopxml" )
635 m_nFormat
= embed::StorageFormats::OFOPXML
;
636 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
640 while ( nIndex
>= 0 );
646 m_aURL
, uno::Reference
< XCommandEnvironment
>(),
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();
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
;
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
;
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
;
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" )
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;
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;
766 bool bBadZipFile
= false;
770 m_pZipFile
= std::make_unique
<ZipFile
>(m_aMutexHolder
, m_xContentStream
, m_xContext
, true, m_bForceRecovery
);
771 getZipFileContents();
773 catch ( IOException
& e
)
776 message
= "IOException: " + e
.Message
;
778 catch ( ZipException
& e
)
781 message
= "ZipException: " + e
.Message
;
783 catch ( Exception
& )
791 // clean up the memory, and tell the UCB about the error
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();
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
;
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
));
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.
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
)
866 if ( !pCurrent
->hasByName( sTemp
) )
867 throw NoSuchElementException(THROW_WHERE
);
869 pPrevious
= pCurrent
;
870 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
872 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
873 pCurrent
= rInfo
.pFolder
;
874 nOldIndex
= nIndex
+1;
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();
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() )
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() )
925 m_aRecent
.erase ( aIter
);
929 sTemp
= aName
.copy ( nStreamIndex
+ 1 );
930 if ( ( *aIter
).second
->hasByName( sTemp
) )
933 m_aRecent
.erase( aIter
);
939 if ( m_xRootFolder
->hasByName ( aName
) )
942 ZipPackageFolder
* pCurrent
= m_xRootFolder
.get();
943 ZipPackageFolder
* pPrevious
= nullptr;
945 while ( ( nIndex
= aName
.indexOf( '/', nOldIndex
)) != -1 )
947 sTemp
= aName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
948 if ( nIndex
== nOldIndex
)
950 if ( pCurrent
->hasByName( sTemp
) )
952 pPrevious
= pCurrent
;
953 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
955 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
956 pCurrent
= rInfo
.pFolder
;
960 nOldIndex
= nIndex
+1;
964 m_aRecent
[sDirName
] = pPrevious
;
969 sTemp
= aName
.copy( nOldIndex
);
971 if ( pCurrent
->hasByName( sTemp
) )
973 m_aRecent
[sDirName
] = pCurrent
;
978 catch (const uno::RuntimeException
&)
982 catch (const uno::Exception
&)
984 uno::Any
e(::cppu::getCaughtException());
985 throw lang::WrappedTargetRuntimeException("ZipPackage::hasByHierarchicalName", nullptr, e
);
990 uno::Reference
< XInterface
> SAL_CALL
ZipPackage::createInstance()
992 uno::Reference
< XInterface
> xRef
= *( new ZipPackageStream( *this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
) );
996 uno::Reference
< XInterface
> SAL_CALL
ZipPackage::createInstanceWithArguments( const uno::Sequence
< Any
>& aArguments
)
999 uno::Reference
< XInterface
> xRef
;
1000 if ( aArguments
.hasElements() )
1001 aArguments
[0] >>= bArg
;
1003 xRef
= *new ZipPackageFolder( m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
1005 xRef
= *new ZipPackageStream( *this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
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()),
1022 pEntry
->sPath
= sMime
;
1023 pEntry
->nMethod
= STORED
;
1024 pEntry
->nSize
= pEntry
->nCompressedSize
= nBufferLength
;
1025 pEntry
->nTime
= ZipOutputStream::getCurrentDosTime();
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 ),
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
;
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
;
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
)
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
;
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 );
1144 m_pZipFile
->setInputStream( m_xContentStream
);
1146 m_pZipFile
= std::make_unique
<ZipFile
>(m_aMutexHolder
, m_xContentStream
, m_xContext
, false);
1154 rtlRandomPool m_aRandomPool
;
1158 m_aRandomPool
= rtl_random_createPool ();
1162 return m_aRandomPool
;
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();
1191 uno::Reference
< io::XStream
> xStr
= xSink
->getStream();
1194 xTempOut
= xStr
->getOutputStream();
1200 else if ( m_eMode
== e_IMode_XStream
&& !m_pZipFile
)
1202 // write directly to an empty stream
1203 xTempOut
= m_xStream
->getOutputStream();
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
);
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("/");
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
);
1305 // Update our References to point to the new temp file
1308 // the case when the original contents were written directly
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
& )
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
)
1340 throw WrappedTargetException(
1341 THROW_WHERE
"Problem writing the original content!",
1342 static_cast < OWeakObject
* > ( this ),
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
) );
1360 uno::Reference
< XActiveDataStreamer
> ZipPackage::openOriginalForOutput()
1362 // open and truncate the original file
1363 Content
aOriginalContent(
1364 m_aURL
, uno::Reference
< XCommandEnvironment
>(),
1366 uno::Reference
< XActiveDataStreamer
> xSink
= new ActiveDataStreamer
;
1368 if ( m_eMode
== e_IMode_URL
)
1372 bool bTruncSuccess
= false;
1377 Any aAny
= aOriginalContent
.setPropertyValue("Size", makeAny( sal_Int64(0) ) );
1378 if( !( aAny
>>= aDetect
) )
1379 bTruncSuccess
= true;
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
1398 aArg
.Properties
= uno::Sequence
< Property
>( 0 ); // unused
1400 aOriginalContent
.executeCommand("open", makeAny( aArg
) );
1404 // seems to be nonlocal file
1405 // temporary file mechanics should be used
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
1476 uno::Reference
<io::XSeekable
> xSeekable(xOutputStream
, uno::UNO_QUERY
);
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;
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
& )
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
'/' ) );
1564 sTargetFolder
, uno::Reference
< XCommandEnvironment
>(),
1568 Any aAny
= xPropSet
->getPropertyValue ("Uri");
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 ),
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
;
1606 m_eMode
= e_IMode_XInputStream
;
1610 uno::Reference
< beans::XPropertySet
> xTempFile( xTempStream
, uno::UNO_QUERY_THROW
);
1611 uno::Any aUrl
= xTempFile
->getPropertyValue("Uri");
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
;
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!" );
1652 aResult
= m_aEncryptionKey
;
1657 sal_Bool SAL_CALL
ZipPackage::hasPendingChanges()
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" };
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 );
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
)
1763 if ( rKey
.Name
== PACKAGE_ENCRYPTIONDATA_SHA1UTF8
)
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" )
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" )
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" )
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
;
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
;
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: */