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/TransferInfo.hpp>
49 #include <com/sun/star/ucb/NameClash.hpp>
50 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
51 #include <com/sun/star/ucb/OpenMode.hpp>
52 #include <com/sun/star/ucb/XProgressHandler.hpp>
53 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
54 #include <com/sun/star/io/XActiveDataStreamer.hpp>
55 #include <com/sun/star/embed/XTransactedObject.hpp>
56 #include <com/sun/star/embed/UseBackupException.hpp>
57 #include <com/sun/star/embed/StorageFormats.hpp>
58 #include <com/sun/star/beans/NamedValue.hpp>
59 #include <com/sun/star/xml/crypto/DigestID.hpp>
60 #include <com/sun/star/xml/crypto/CipherID.hpp>
61 #include <cppuhelper/implbase.hxx>
62 #include "ContentInfo.hxx"
63 #include <cppuhelper/typeprovider.hxx>
64 #include <rtl/uri.hxx>
65 #include <rtl/random.h>
66 #include <osl/diagnose.h>
67 #include <com/sun/star/io/XAsyncOutputMonitor.hpp>
73 #include <comphelper/processfactory.hxx>
74 #include <comphelper/seekableinput.hxx>
75 #include <comphelper/storagehelper.hxx>
76 #include <comphelper/ofopxmlhelper.hxx>
77 #include <comphelper/documentconstants.hxx>
78 #include <comphelper/sequenceashashmap.hxx>
79 #include <cppuhelper/supportsservice.hxx>
80 #include <comphelper/sequence.hxx>
81 #include <o3tl/make_unique.hxx>
86 using namespace ucbhelper
;
87 using namespace com::sun::star
;
88 using namespace com::sun::star::io
;
89 using namespace com::sun::star::uno
;
90 using namespace com::sun::star::ucb
;
91 using namespace com::sun::star::util
;
92 using namespace com::sun::star::lang
;
93 using namespace com::sun::star::task
;
94 using namespace com::sun::star::beans
;
95 using namespace com::sun::star::packages
;
96 using namespace com::sun::star::container
;
97 using namespace com::sun::star::packages::zip
;
98 using namespace com::sun::star::packages::manifest
;
99 using namespace com::sun::star::packages::zip::ZipConstants
;
101 #if OSL_DEBUG_LEVEL > 0
102 #define THROW_WHERE SAL_WHERE
104 #define THROW_WHERE ""
107 class ActiveDataStreamer
: public ::cppu::WeakImplHelper
< XActiveDataStreamer
>
109 uno::Reference
< XStream
> mStream
;
112 virtual uno::Reference
< XStream
> SAL_CALL
getStream() override
115 virtual void SAL_CALL
setStream( const uno::Reference
< XStream
>& stream
) override
116 { mStream
= stream
; }
119 class DummyInputStream
: public ::cppu::WeakImplHelper
< XInputStream
>
121 virtual sal_Int32 SAL_CALL
readBytes( uno::Sequence
< sal_Int8
>&, sal_Int32
) override
124 virtual sal_Int32 SAL_CALL
readSomeBytes( uno::Sequence
< sal_Int8
>&, sal_Int32
) override
127 virtual void SAL_CALL
skipBytes( sal_Int32
) override
130 virtual sal_Int32 SAL_CALL
available() override
133 virtual void SAL_CALL
closeInput() override
137 ZipPackage::ZipPackage ( const uno::Reference
< XComponentContext
> &xContext
)
138 : m_aMutexHolder( new comphelper::RefCountedMutex
)
139 , m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1
)
140 , m_nChecksumDigestID( xml::crypto::DigestID::SHA1_1K
)
141 , m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8
)
142 , m_bHasEncryptedEntries ( false )
143 , m_bHasNonEncryptedEntries ( false )
144 , m_bInconsistent ( false )
145 , m_bForceRecovery ( false )
146 , m_bMediaTypeFallbackUsed ( false )
147 , m_nFormat( embed::StorageFormats::PACKAGE
) // package is the default format
148 , m_bAllowRemoveOnInsert( true )
149 , m_eMode ( e_IMode_None
)
150 , m_xContext( xContext
)
152 m_xRootFolder
= new ZipPackageFolder( m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
155 ZipPackage::~ZipPackage()
159 bool ZipPackage::isLocalFile() const
161 return comphelper::isFileUrl(m_aURL
);
164 void ZipPackage::parseManifest()
166 if ( m_nFormat
== embed::StorageFormats::PACKAGE
)
168 bool bManifestParsed
= false;
169 const OUString
sMeta ("META-INF");
170 if ( m_xRootFolder
->hasByName( sMeta
) )
172 const OUString
sManifest ("manifest.xml");
175 uno::Reference
< XUnoTunnel
> xTunnel
;
176 Any aAny
= m_xRootFolder
->getByName( sMeta
);
178 uno::Reference
< XNameContainer
> xMetaInfFolder( xTunnel
, UNO_QUERY
);
179 if ( xMetaInfFolder
.is() && xMetaInfFolder
->hasByName( sManifest
) )
181 aAny
= xMetaInfFolder
->getByName( sManifest
);
183 uno::Reference
< XActiveDataSink
> xSink ( xTunnel
, UNO_QUERY
);
186 uno::Reference
< XManifestReader
> xReader
= ManifestReader::create( m_xContext
);
188 const OUString
sPropFullPath ("FullPath");
189 const OUString
sPropVersion ("Version");
190 const OUString
sPropMediaType ("MediaType");
191 const OUString
sPropInitialisationVector ("InitialisationVector");
192 const OUString
sPropSalt ("Salt");
193 const OUString
sPropIterationCount ("IterationCount");
194 const OUString
sPropSize ("Size");
195 const OUString
sPropDigest ("Digest");
196 const OUString
sPropDerivedKeySize ("DerivedKeySize");
197 const OUString
sPropDigestAlgorithm ("DigestAlgorithm");
198 const OUString
sPropEncryptionAlgorithm ("EncryptionAlgorithm");
199 const OUString
sPropStartKeyAlgorithm ("StartKeyAlgorithm");
200 const OUString
sKeyInfo ("KeyInfo");
202 uno::Sequence
< uno::Sequence
< PropertyValue
> > aManifestSequence
= xReader
->readManifestSequence ( xSink
->getInputStream() );
203 sal_Int32 nLength
= aManifestSequence
.getLength();
204 const uno::Sequence
< PropertyValue
> *pSequence
= aManifestSequence
.getConstArray();
205 ZipPackageStream
*pStream
= nullptr;
206 ZipPackageFolder
*pFolder
= nullptr;
207 const Any
*pKeyInfo
= nullptr;
209 for ( sal_Int32 i
= 0; i
< nLength
; i
++, pSequence
++ )
211 OUString sPath
, sMediaType
, sVersion
;
212 const PropertyValue
*pValue
= pSequence
->getConstArray();
213 const Any
*pSalt
= nullptr, *pVector
= nullptr, *pCount
= nullptr, *pSize
= nullptr, *pDigest
= nullptr, *pDigestAlg
= nullptr, *pEncryptionAlg
= nullptr, *pStartKeyAlg
= nullptr, *pDerivedKeySize
= nullptr;
214 for ( sal_Int32 j
= 0, nNum
= pSequence
->getLength(); j
< nNum
; j
++ )
216 if ( pValue
[j
].Name
== sPropFullPath
)
217 pValue
[j
].Value
>>= sPath
;
218 else if ( pValue
[j
].Name
== sPropVersion
)
219 pValue
[j
].Value
>>= sVersion
;
220 else if ( pValue
[j
].Name
== sPropMediaType
)
221 pValue
[j
].Value
>>= sMediaType
;
222 else if ( pValue
[j
].Name
== sPropSalt
)
223 pSalt
= &( pValue
[j
].Value
);
224 else if ( pValue
[j
].Name
== sPropInitialisationVector
)
225 pVector
= &( pValue
[j
].Value
);
226 else if ( pValue
[j
].Name
== sPropIterationCount
)
227 pCount
= &( pValue
[j
].Value
);
228 else if ( pValue
[j
].Name
== sPropSize
)
229 pSize
= &( pValue
[j
].Value
);
230 else if ( pValue
[j
].Name
== sPropDigest
)
231 pDigest
= &( pValue
[j
].Value
);
232 else if ( pValue
[j
].Name
== sPropDigestAlgorithm
)
233 pDigestAlg
= &( pValue
[j
].Value
);
234 else if ( pValue
[j
].Name
== sPropEncryptionAlgorithm
)
235 pEncryptionAlg
= &( pValue
[j
].Value
);
236 else if ( pValue
[j
].Name
== sPropStartKeyAlgorithm
)
237 pStartKeyAlg
= &( pValue
[j
].Value
);
238 else if ( pValue
[j
].Name
== sPropDerivedKeySize
)
239 pDerivedKeySize
= &( pValue
[j
].Value
);
240 else if ( pValue
[j
].Name
== sKeyInfo
)
241 pKeyInfo
= &( pValue
[j
].Value
);
244 if ( !sPath
.isEmpty() && hasByHierarchicalName ( sPath
) )
246 aAny
= getByHierarchicalName( sPath
);
247 uno::Reference
< XUnoTunnel
> xUnoTunnel
;
250 if ( (nTest
= xUnoTunnel
->getSomething( ZipPackageFolder::static_getImplementationId() )) != 0 )
252 pFolder
= reinterpret_cast < ZipPackageFolder
* > ( nTest
);
253 pFolder
->SetMediaType ( sMediaType
);
254 pFolder
->SetVersion ( sVersion
);
258 pStream
= reinterpret_cast < ZipPackageStream
* > ( xUnoTunnel
->getSomething( ZipPackageStream::static_getImplementationId() ));
259 pStream
->SetMediaType ( sMediaType
);
260 pStream
->SetFromManifest( true );
262 if ( pKeyInfo
&& pVector
&& pSize
&& pDigest
&& pDigestAlg
&& pEncryptionAlg
)
264 uno::Sequence
< sal_Int8
> aSequence
;
266 sal_Int32 nDigestAlg
= 0, nEncryptionAlg
= 0;
268 pStream
->SetToBeEncrypted ( true );
270 *pVector
>>= aSequence
;
271 pStream
->setInitialisationVector ( aSequence
);
274 pStream
->setSize ( nSize
);
276 *pDigest
>>= aSequence
;
277 pStream
->setDigest ( aSequence
);
279 *pDigestAlg
>>= nDigestAlg
;
280 pStream
->SetImportedChecksumAlgorithm( nDigestAlg
);
282 *pEncryptionAlg
>>= nEncryptionAlg
;
283 pStream
->SetImportedEncryptionAlgorithm( nEncryptionAlg
);
285 *pKeyInfo
>>= m_aGpgProps
;
287 pStream
->SetToBeCompressed ( true );
288 pStream
->SetToBeEncrypted ( true );
289 pStream
->SetIsEncrypted ( true );
290 pStream
->setIterationCount(0);
292 // clamp to default SHA256 start key magic value,
293 // c.f. ZipPackageStream::GetEncryptionKey()
294 // trying to get key value from properties
295 const sal_Int32 nStartKeyAlg
= xml::crypto::DigestID::SHA256
;
296 pStream
->SetImportedStartKeyAlgorithm( nStartKeyAlg
);
298 if ( !m_bHasEncryptedEntries
&& pStream
->getName() == "content.xml" )
300 m_bHasEncryptedEntries
= true;
301 m_nChecksumDigestID
= nDigestAlg
;
302 m_nCommonEncryptionID
= nEncryptionAlg
;
303 m_nStartKeyGenerationID
= nStartKeyAlg
;
306 else if ( pSalt
&& pVector
&& pCount
&& pSize
&& pDigest
&& pDigestAlg
&& pEncryptionAlg
)
308 uno::Sequence
< sal_Int8
> aSequence
;
310 sal_Int32 nCount
= 0, nDigestAlg
= 0, nEncryptionAlg
= 0;
311 sal_Int32 nDerivedKeySize
= 16, nStartKeyAlg
= xml::crypto::DigestID::SHA1
;
313 pStream
->SetToBeEncrypted ( true );
315 *pSalt
>>= aSequence
;
316 pStream
->setSalt ( aSequence
);
318 *pVector
>>= aSequence
;
319 pStream
->setInitialisationVector ( aSequence
);
322 pStream
->setIterationCount ( nCount
);
325 pStream
->setSize ( nSize
);
327 *pDigest
>>= aSequence
;
328 pStream
->setDigest ( aSequence
);
330 *pDigestAlg
>>= nDigestAlg
;
331 pStream
->SetImportedChecksumAlgorithm( nDigestAlg
);
333 *pEncryptionAlg
>>= nEncryptionAlg
;
334 pStream
->SetImportedEncryptionAlgorithm( nEncryptionAlg
);
336 if ( pDerivedKeySize
)
337 *pDerivedKeySize
>>= nDerivedKeySize
;
338 pStream
->SetImportedDerivedKeySize( nDerivedKeySize
);
341 *pStartKeyAlg
>>= nStartKeyAlg
;
342 pStream
->SetImportedStartKeyAlgorithm( nStartKeyAlg
);
344 pStream
->SetToBeCompressed ( true );
345 pStream
->SetToBeEncrypted ( true );
346 pStream
->SetIsEncrypted ( true );
347 if ( !m_bHasEncryptedEntries
&& pStream
->getName() == "content.xml" )
349 m_bHasEncryptedEntries
= true;
350 m_nStartKeyGenerationID
= nStartKeyAlg
;
351 m_nChecksumDigestID
= nDigestAlg
;
352 m_nCommonEncryptionID
= nEncryptionAlg
;
356 m_bHasNonEncryptedEntries
= true;
361 bManifestParsed
= true;
364 // now hide the manifest.xml file from user
365 xMetaInfFolder
->removeByName( sManifest
);
370 if ( !m_bForceRecovery
)
375 if ( !bManifestParsed
&& !m_bForceRecovery
)
376 throw ZipIOException(
377 THROW_WHERE
"Could not parse manifest.xml" );
379 const OUString
sMimetype ("mimetype");
380 if ( m_xRootFolder
->hasByName( sMimetype
) )
382 // get mediatype from the "mimetype" stream
383 OUString aPackageMediatype
;
384 uno::Reference
< lang::XUnoTunnel
> xMimeTypeTunnel
;
385 m_xRootFolder
->getByName( sMimetype
) >>= xMimeTypeTunnel
;
386 uno::Reference
< io::XActiveDataSink
> xMimeSink( xMimeTypeTunnel
, UNO_QUERY
);
387 if ( xMimeSink
.is() )
389 uno::Reference
< io::XInputStream
> xMimeInStream
= xMimeSink
->getInputStream();
390 if ( xMimeInStream
.is() )
392 // Mediatypes longer than 1024 symbols should not appear here
393 uno::Sequence
< sal_Int8
> aData( 1024 );
394 sal_Int32 nRead
= xMimeInStream
->readBytes( aData
, 1024 );
395 if ( nRead
> aData
.getLength() )
396 nRead
= aData
.getLength();
399 aPackageMediatype
= OUString( reinterpret_cast<char const *>(aData
.getConstArray()), nRead
, RTL_TEXTENCODING_ASCII_US
);
403 if ( !bManifestParsed
)
405 // the manifest.xml could not be successfully parsed, this is an inconsistent package
406 if ( aPackageMediatype
.startsWith("application/vnd.") )
408 // accept only types that look similar to own mediatypes
409 m_xRootFolder
->SetMediaType( aPackageMediatype
);
410 m_bMediaTypeFallbackUsed
= true;
413 else if ( !m_bForceRecovery
)
415 // the mimetype stream should contain the information from manifest.xml
416 if ( m_xRootFolder
->GetMediaType() != aPackageMediatype
)
417 throw ZipIOException(
419 "mimetype conflicts with manifest.xml, \""
420 + m_xRootFolder
->GetMediaType() + "\" vs. \""
421 + aPackageMediatype
+ "\"" );
424 m_xRootFolder
->removeByName( sMimetype
);
427 m_bInconsistent
= m_xRootFolder
->LookForUnexpectedODF12Streams( OUString() );
429 bool bODF12AndNewer
= ( m_xRootFolder
->GetVersion().compareTo( ODFVER_012_TEXT
) >= 0 );
430 if ( !m_bForceRecovery
&& bODF12AndNewer
)
432 if ( m_bInconsistent
)
434 // this is an ODF1.2 document that contains streams not referred in the manifest.xml;
435 // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent
436 // should be checked later
437 throw ZipIOException(
438 THROW_WHERE
"there are streams not referred in manifest.xml" );
440 // all the streams should be encrypted with the same StartKey in ODF1.2
441 // TODO/LATER: in future the exception should be thrown
442 // throw ZipIOException( THROW_WHERE "More than one Start Key Generation algorithm is specified!" );
445 // in case it is a correct ODF1.2 document, the version must be set
446 // and the META-INF folder is reserved for package format
447 if ( bODF12AndNewer
)
448 m_xRootFolder
->removeByName( sMeta
);
452 void ZipPackage::parseContentType()
454 if ( m_nFormat
== embed::StorageFormats::OFOPXML
)
456 const OUString
aContentTypes("[Content_Types].xml");
458 // the content type must exist in OFOPXML format!
459 if ( !m_xRootFolder
->hasByName( aContentTypes
) )
460 throw io::IOException(THROW_WHERE
"Wrong format!" );
462 uno::Reference
< lang::XUnoTunnel
> xTunnel
;
463 uno::Any aAny
= m_xRootFolder
->getByName( aContentTypes
);
465 uno::Reference
< io::XActiveDataSink
> xSink( xTunnel
, UNO_QUERY
);
468 uno::Reference
< io::XInputStream
> xInStream
= xSink
->getInputStream();
469 if ( xInStream
.is() )
472 // here aContentTypeInfo[0] - Defaults, and aContentTypeInfo[1] - Overrides
473 uno::Sequence
< uno::Sequence
< beans::StringPair
> > aContentTypeInfo
=
474 ::comphelper::OFOPXMLHelper::ReadContentTypeSequence( xInStream
, m_xContext
);
476 if ( aContentTypeInfo
.getLength() != 2 )
477 throw io::IOException(THROW_WHERE
);
479 // set the implicit types first
480 for ( nInd
= 0; nInd
< aContentTypeInfo
[0].getLength(); nInd
++ )
481 m_xRootFolder
->setChildStreamsTypeByExtension( aContentTypeInfo
[0][nInd
] );
483 // now set the explicit types
484 for ( nInd
= 0; nInd
< aContentTypeInfo
[1].getLength(); nInd
++ )
487 if ( aContentTypeInfo
[1][nInd
].First
.toChar() == '/' )
488 aPath
= aContentTypeInfo
[1][nInd
].First
.copy( 1 );
490 aPath
= aContentTypeInfo
[1][nInd
].First
;
492 if ( !aPath
.isEmpty() && hasByHierarchicalName( aPath
) )
494 uno::Any aIterAny
= getByHierarchicalName( aPath
);
495 uno::Reference
< lang::XUnoTunnel
> xIterTunnel
;
496 aIterAny
>>= xIterTunnel
;
497 sal_Int64 nTest
= xIterTunnel
->getSomething( ZipPackageStream::static_getImplementationId() );
500 // this is a package stream, in OFOPXML format only streams can have mediatype
501 ZipPackageStream
*pStream
= reinterpret_cast < ZipPackageStream
* > ( nTest
);
502 pStream
->SetMediaType( aContentTypeInfo
[1][nInd
].Second
);
509 m_xRootFolder
->removeByName( aContentTypes
);
511 catch( uno::Exception
& )
513 if ( !m_bForceRecovery
)
519 void ZipPackage::getZipFileContents()
521 std::unique_ptr
<ZipEnumeration
> xEnum
= m_pZipFile
->entries();
522 OUString sTemp
, sDirName
;
523 sal_Int32 nOldIndex
, nStreamIndex
;
524 FolderHash::iterator aIter
;
526 while (xEnum
->hasMoreElements())
529 ZipPackageFolder
* pCurrent
= m_xRootFolder
.get();
530 const ZipEntry
& rEntry
= *xEnum
->nextElement();
531 OUString rName
= rEntry
.sPath
;
533 if ( m_bForceRecovery
)
535 // the PKZIP Application note version 6.2 does not allows to use '\' as separator
536 // unfortunately it is used by some implementations, so we have to support it in recovery mode
537 rName
= rName
.replace( '\\', '/' );
540 nStreamIndex
= rName
.lastIndexOf ( '/' );
541 if ( nStreamIndex
!= -1 )
543 sDirName
= rName
.copy ( 0, nStreamIndex
);
544 aIter
= m_aRecent
.find ( sDirName
);
545 if ( aIter
!= m_aRecent
.end() )
546 pCurrent
= ( *aIter
).second
;
549 if ( pCurrent
== m_xRootFolder
.get() )
552 while ( ( nIndex
= rName
.indexOf( '/', nOldIndex
) ) != -1 )
554 sTemp
= rName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
555 if ( nIndex
== nOldIndex
)
557 if ( !pCurrent
->hasByName( sTemp
) )
559 ZipPackageFolder
* pPkgFolder
= new ZipPackageFolder(m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
560 pPkgFolder
->setName( sTemp
);
561 pPkgFolder
->doSetParent( pCurrent
);
562 pCurrent
= pPkgFolder
;
566 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
568 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
569 pCurrent
= rInfo
.pFolder
;
571 nOldIndex
= nIndex
+1;
573 if ( nStreamIndex
!= -1 && !sDirName
.isEmpty() )
574 m_aRecent
[ sDirName
] = pCurrent
;
576 if ( rName
.getLength() -1 != nStreamIndex
)
579 sTemp
= rName
.copy( nStreamIndex
);
581 if (!pCurrent
->hasByName(sTemp
))
583 ZipPackageStream
*pPkgStream
= new ZipPackageStream(*this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
584 pPkgStream
->SetPackageMember(true);
585 pPkgStream
->setZipEntryOnLoading(rEntry
);
586 pPkgStream
->setName(sTemp
);
587 pPkgStream
->doSetParent(pCurrent
);
592 if ( m_nFormat
== embed::StorageFormats::PACKAGE
)
594 else if ( m_nFormat
== embed::StorageFormats::OFOPXML
)
598 void SAL_CALL
ZipPackage::initialize( const uno::Sequence
< Any
>& aArguments
)
600 beans::NamedValue aNamedValue
;
602 if ( aArguments
.getLength() )
604 bool bHaveZipFile
= true;
606 for( int ind
= 0; ind
< aArguments
.getLength(); ind
++ )
609 if ( aArguments
[ind
] >>= aParamUrl
)
611 m_eMode
= e_IMode_URL
;
614 sal_Int32 nParam
= aParamUrl
.indexOf( '?' );
617 m_aURL
= aParamUrl
.copy( 0, nParam
);
618 OUString aParam
= aParamUrl
.copy( nParam
+ 1 );
620 sal_Int32 nIndex
= 0;
623 OUString aCommand
= aParam
.getToken( 0, '&', nIndex
);
624 if ( aCommand
== "repairpackage" )
626 m_bForceRecovery
= true;
629 else if ( aCommand
== "purezip" )
631 m_nFormat
= embed::StorageFormats::ZIP
;
632 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
635 else if ( aCommand
== "ofopxml" )
637 m_nFormat
= embed::StorageFormats::OFOPXML
;
638 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
642 while ( nIndex
>= 0 );
648 m_aURL
, uno::Reference
< XCommandEnvironment
>(),
650 Any aAny
= aContent
.getPropertyValue("Size");
651 sal_uInt64 aSize
= 0;
652 // kind of optimization: treat empty files as nonexistent files
653 // and write to such files directly. Note that "Size" property is optional.
654 bool bHasSizeProperty
= aAny
>>= aSize
;
655 if( !bHasSizeProperty
|| aSize
)
657 uno::Reference
< XActiveDataSink
> xSink
= new ZipPackageSink
;
658 if ( aContent
.openStream ( xSink
) )
659 m_xContentStream
= xSink
->getInputStream();
662 bHaveZipFile
= false;
664 catch ( css::uno::Exception
& )
666 // Exception derived from uno::Exception thrown. This probably
667 // means the file doesn't exist...we'll create it at
668 // commitChanges time
669 bHaveZipFile
= false;
672 else if ( aArguments
[ind
] >>= m_xStream
)
674 // a writable stream can implement both XStream & XInputStream
675 m_eMode
= e_IMode_XStream
;
676 m_xContentStream
= m_xStream
->getInputStream();
678 else if ( aArguments
[ind
] >>= m_xContentStream
)
680 m_eMode
= e_IMode_XInputStream
;
682 else if ( aArguments
[ind
] >>= aNamedValue
)
684 if ( aNamedValue
.Name
== "RepairPackage" )
685 aNamedValue
.Value
>>= m_bForceRecovery
;
686 else if ( aNamedValue
.Name
== "PackageFormat" )
688 // setting this argument to true means Package format
689 // setting it to false means plain Zip format
691 bool bPackFormat
= true;
692 aNamedValue
.Value
>>= bPackFormat
;
694 m_nFormat
= embed::StorageFormats::ZIP
;
696 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
698 else if ( aNamedValue
.Name
== "StorageFormat" )
700 OUString aFormatName
;
701 sal_Int32 nFormatID
= 0;
702 if ( aNamedValue
.Value
>>= aFormatName
)
704 if ( aFormatName
== PACKAGE_STORAGE_FORMAT_STRING
)
705 m_nFormat
= embed::StorageFormats::PACKAGE
;
706 else if ( aFormatName
== ZIP_STORAGE_FORMAT_STRING
)
707 m_nFormat
= embed::StorageFormats::ZIP
;
708 else if ( aFormatName
== OFOPXML_STORAGE_FORMAT_STRING
)
709 m_nFormat
= embed::StorageFormats::OFOPXML
;
711 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
713 else if ( aNamedValue
.Value
>>= nFormatID
)
715 if ( nFormatID
!= embed::StorageFormats::PACKAGE
716 && nFormatID
!= embed::StorageFormats::ZIP
717 && nFormatID
!= embed::StorageFormats::OFOPXML
)
718 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
720 m_nFormat
= nFormatID
;
723 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
725 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
727 else if ( aNamedValue
.Name
== "AllowRemoveOnInsert" )
729 aNamedValue
.Value
>>= m_bAllowRemoveOnInsert
;
730 m_xRootFolder
->setRemoveOnInsertMode_Impl( m_bAllowRemoveOnInsert
);
732 else if (aNamedValue
.Name
== "NoFileSync")
733 aNamedValue
.Value
>>= m_bDisableFileSync
;
735 // for now the progress handler is not used, probably it will never be
736 // if ( aNamedValue.Name == "ProgressHandler" )
740 // The URL is not acceptable
741 throw css::uno::Exception (THROW_WHERE
"Bad arguments.",
742 static_cast < ::cppu::OWeakObject
* > ( this ) );
748 if ( m_xContentStream
.is() )
750 // the stream must be seekable, if it is not it will be wrapped
751 m_xContentStream
= ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream
, m_xContext
);
752 m_xContentSeek
.set( m_xContentStream
, UNO_QUERY_THROW
);
753 if ( !m_xContentSeek
->getLength() )
754 bHaveZipFile
= false;
757 bHaveZipFile
= false;
759 catch ( css::uno::Exception
& )
761 // Exception derived from uno::Exception thrown. This probably
762 // means the file doesn't exist...we'll create it at
763 // commitChanges time
764 bHaveZipFile
= false;
768 bool bBadZipFile
= false;
772 m_pZipFile
= o3tl::make_unique
<ZipFile
>(m_aMutexHolder
, m_xContentStream
, m_xContext
, true, m_bForceRecovery
);
773 getZipFileContents();
775 catch ( IOException
& e
)
778 message
= "IOException: " + e
.Message
;
780 catch ( ZipException
& e
)
783 message
= "ZipException: " + e
.Message
;
785 catch ( Exception
& )
793 // clean up the memory, and tell the UCB about the error
796 throw css::packages::zip::ZipIOException (
797 THROW_WHERE
"Bad Zip File, " + message
,
798 static_cast < ::cppu::OWeakObject
* > ( this ) );
804 Any SAL_CALL
ZipPackage::getByHierarchicalName( const OUString
& aName
)
806 OUString sTemp
, sDirName
;
807 sal_Int32 nOldIndex
, nStreamIndex
;
808 FolderHash::iterator aIter
;
810 sal_Int32 nIndex
= aName
.getLength();
814 return makeAny ( uno::Reference
< XUnoTunnel
> ( m_xRootFolder
.get() ) );
816 nStreamIndex
= aName
.lastIndexOf ( '/' );
817 bool bFolder
= nStreamIndex
== nIndex
-1; // last character is '/'.
819 if ( nStreamIndex
!= -1 )
821 // The name contains '/'.
822 sDirName
= aName
.copy ( 0, nStreamIndex
);
823 aIter
= m_aRecent
.find ( sDirName
);
824 if ( aIter
!= m_aRecent
.end() )
826 // There is a cached entry for this name.
828 ZipPackageFolder
* pFolder
= aIter
->second
;
832 // Determine the directory name.
833 sal_Int32 nDirIndex
= aName
.lastIndexOf ( '/', nStreamIndex
);
834 sTemp
= aName
.copy ( nDirIndex
== -1 ? 0 : nDirIndex
+1, nStreamIndex
-nDirIndex
-1 );
836 if (pFolder
&& sTemp
== pFolder
->getName())
837 return makeAny(uno::Reference
<XUnoTunnel
>(pFolder
));
841 // Determine the file name.
842 sTemp
= aName
.copy ( nStreamIndex
+ 1 );
844 if (pFolder
&& pFolder
->hasByName(sTemp
))
845 return pFolder
->getByName(sTemp
);
848 m_aRecent
.erase( aIter
);
851 else if ( m_xRootFolder
->hasByName ( aName
) )
852 // top-level element.
853 return m_xRootFolder
->getByName ( aName
);
855 // Not in the cache. Search normally.
858 ZipPackageFolder
* pCurrent
= m_xRootFolder
.get();
859 ZipPackageFolder
* pPrevious
= nullptr;
861 // Find the right directory for the given path.
863 while ( ( nIndex
= aName
.indexOf( '/', nOldIndex
)) != -1 )
865 sTemp
= aName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
866 if ( nIndex
== nOldIndex
)
868 if ( !pCurrent
->hasByName( sTemp
) )
869 throw NoSuchElementException(THROW_WHERE
);
871 pPrevious
= pCurrent
;
872 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
874 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
875 pCurrent
= rInfo
.pFolder
;
876 nOldIndex
= nIndex
+1;
881 if ( nStreamIndex
!= -1 )
882 m_aRecent
[sDirName
] = pPrevious
; // cache it.
883 return makeAny ( uno::Reference
< XUnoTunnel
> ( pCurrent
) );
886 sTemp
= aName
.copy( nOldIndex
);
888 if ( pCurrent
->hasByName ( sTemp
) )
890 if ( nStreamIndex
!= -1 )
891 m_aRecent
[sDirName
] = pCurrent
; // cache it.
892 return pCurrent
->getByName( sTemp
);
895 throw NoSuchElementException(THROW_WHERE
);
898 sal_Bool SAL_CALL
ZipPackage::hasByHierarchicalName( const OUString
& aName
)
900 OUString sTemp
, sDirName
;
901 sal_Int32 nOldIndex
, nStreamIndex
;
902 FolderHash::iterator aIter
;
904 sal_Int32 nIndex
= aName
.getLength();
912 nStreamIndex
= aName
.lastIndexOf ( '/' );
913 bool bFolder
= nStreamIndex
== nIndex
-1;
914 if ( nStreamIndex
!= -1 )
916 sDirName
= aName
.copy ( 0, nStreamIndex
);
917 aIter
= m_aRecent
.find ( sDirName
);
918 if ( aIter
!= m_aRecent
.end() )
922 sal_Int32 nDirIndex
= aName
.lastIndexOf ( '/', nStreamIndex
);
923 sTemp
= aName
.copy ( nDirIndex
== -1 ? 0 : nDirIndex
+1, nStreamIndex
-nDirIndex
-1 );
924 if ( sTemp
== ( *aIter
).second
->getName() )
927 m_aRecent
.erase ( aIter
);
931 sTemp
= aName
.copy ( nStreamIndex
+ 1 );
932 if ( ( *aIter
).second
->hasByName( sTemp
) )
935 m_aRecent
.erase( aIter
);
941 if ( m_xRootFolder
->hasByName ( aName
) )
944 ZipPackageFolder
* pCurrent
= m_xRootFolder
.get();
945 ZipPackageFolder
* pPrevious
= nullptr;
947 while ( ( nIndex
= aName
.indexOf( '/', nOldIndex
)) != -1 )
949 sTemp
= aName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
950 if ( nIndex
== nOldIndex
)
952 if ( pCurrent
->hasByName( sTemp
) )
954 pPrevious
= pCurrent
;
955 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
957 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
958 pCurrent
= rInfo
.pFolder
;
962 nOldIndex
= nIndex
+1;
966 m_aRecent
[sDirName
] = pPrevious
;
971 sTemp
= aName
.copy( nOldIndex
);
973 if ( pCurrent
->hasByName( sTemp
) )
975 m_aRecent
[sDirName
] = pCurrent
;
980 catch (const uno::RuntimeException
&)
984 catch (const uno::Exception
&)
986 uno::Any
e(::cppu::getCaughtException());
987 throw lang::WrappedTargetRuntimeException("ZipPackage::hasByHierarchicalName", nullptr, e
);
992 uno::Reference
< XInterface
> SAL_CALL
ZipPackage::createInstance()
994 uno::Reference
< XInterface
> xRef
= *( new ZipPackageStream( *this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
) );
998 uno::Reference
< XInterface
> SAL_CALL
ZipPackage::createInstanceWithArguments( const uno::Sequence
< Any
>& aArguments
)
1001 uno::Reference
< XInterface
> xRef
;
1002 if ( aArguments
.getLength() )
1003 aArguments
[0] >>= bArg
;
1005 xRef
= *new ZipPackageFolder( m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
1007 xRef
= *new ZipPackageStream( *this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
1012 void ZipPackage::WriteMimetypeMagicFile( ZipOutputStream
& aZipOut
)
1014 const OUString
sMime ("mimetype");
1015 if ( m_xRootFolder
->hasByName( sMime
) )
1016 m_xRootFolder
->removeByName( sMime
);
1018 ZipEntry
* pEntry
= new ZipEntry
;
1019 sal_Int32 nBufferLength
= m_xRootFolder
->GetMediaType().getLength();
1020 OString sMediaType
= OUStringToOString( m_xRootFolder
->GetMediaType(), RTL_TEXTENCODING_ASCII_US
);
1021 const uno::Sequence
< sal_Int8
> aType( reinterpret_cast<sal_Int8
const *>(sMediaType
.getStr()),
1024 pEntry
->sPath
= sMime
;
1025 pEntry
->nMethod
= STORED
;
1026 pEntry
->nSize
= pEntry
->nCompressedSize
= nBufferLength
;
1027 pEntry
->nTime
= ZipOutputStream::getCurrentDosTime();
1030 aCRC32
.update( aType
);
1031 pEntry
->nCrc
= aCRC32
.getValue();
1035 ZipOutputStream::setEntry(pEntry
);
1036 aZipOut
.writeLOC(pEntry
);
1037 aZipOut
.rawWrite(aType
);
1038 aZipOut
.rawCloseEntry();
1040 catch ( const css::io::IOException
& r
)
1042 throw WrappedTargetException(
1043 THROW_WHERE
"Error adding mimetype to the ZipOutputStream!",
1044 static_cast < OWeakObject
* > ( this ),
1049 void ZipPackage::WriteManifest( ZipOutputStream
& aZipOut
, const vector
< uno::Sequence
< PropertyValue
> >& aManList
)
1051 // Write the manifest
1052 uno::Reference
< XManifestWriter
> xWriter
= ManifestWriter::create( m_xContext
);
1053 ZipEntry
* pEntry
= new ZipEntry
;
1054 ZipPackageBuffer
*pBuffer
= new ZipPackageBuffer
;
1055 uno::Reference
< XOutputStream
> xManOutStream( *pBuffer
, UNO_QUERY
);
1057 pEntry
->sPath
= "META-INF/manifest.xml";
1058 pEntry
->nMethod
= DEFLATED
;
1060 pEntry
->nSize
= pEntry
->nCompressedSize
= -1;
1061 pEntry
->nTime
= ZipOutputStream::getCurrentDosTime();
1063 xWriter
->writeManifestSequence ( xManOutStream
, comphelper::containerToSequence(aManList
) );
1065 sal_Int32 nBufferLength
= static_cast < sal_Int32
> ( pBuffer
->getPosition() );
1066 pBuffer
->realloc( nBufferLength
);
1068 // the manifest.xml is never encrypted - so pass an empty reference
1069 ZipOutputStream::setEntry(pEntry
);
1070 aZipOut
.writeLOC(pEntry
);
1071 ZipOutputEntry
aZipEntry(aZipOut
.getStream(), m_xContext
, *pEntry
, nullptr, /*bEncrypt*/false);
1072 aZipEntry
.write(pBuffer
->getSequence());
1073 aZipEntry
.closeEntry();
1074 aZipOut
.rawCloseEntry();
1077 void ZipPackage::WriteContentTypes( ZipOutputStream
& aZipOut
, const vector
< uno::Sequence
< PropertyValue
> >& aManList
)
1079 ZipEntry
* pEntry
= new ZipEntry
;
1080 ZipPackageBuffer
*pBuffer
= new ZipPackageBuffer
;
1081 uno::Reference
< io::XOutputStream
> xConTypeOutStream( *pBuffer
, UNO_QUERY
);
1083 pEntry
->sPath
= "[Content_Types].xml";
1084 pEntry
->nMethod
= DEFLATED
;
1086 pEntry
->nSize
= pEntry
->nCompressedSize
= -1;
1087 pEntry
->nTime
= ZipOutputStream::getCurrentDosTime();
1089 // Convert vector into a uno::Sequence
1090 // TODO/LATER: use Default entries in future
1091 uno::Sequence
< beans::StringPair
> aDefaultsSequence(4);
1092 // Add at least the standard default entries.
1093 aDefaultsSequence
[0].First
= "xml";
1094 aDefaultsSequence
[0].Second
= "application/xml";
1095 aDefaultsSequence
[1].First
= "rels";
1096 aDefaultsSequence
[1].Second
= "application/vnd.openxmlformats-package.relationships+xml";
1097 aDefaultsSequence
[2].First
= "png";
1098 aDefaultsSequence
[2].Second
= "image/png";
1099 aDefaultsSequence
[3].First
= "jpeg";
1100 aDefaultsSequence
[3].Second
= "image/jpeg";
1102 uno::Sequence
< beans::StringPair
> aOverridesSequence(aManList
.size());
1103 sal_Int32 nOverSeqLength
= 0;
1104 for ( vector
< uno::Sequence
< beans::PropertyValue
> >::const_iterator aIter
= aManList
.begin(),
1105 aEnd
= aManList
.end();
1111 OSL_ENSURE( ( *aIter
)[PKG_MNFST_MEDIATYPE
].Name
== "MediaType" && ( *aIter
)[PKG_MNFST_FULLPATH
].Name
== "FullPath",
1112 "The mediatype sequence format is wrong!" );
1113 ( *aIter
)[PKG_MNFST_MEDIATYPE
].Value
>>= aType
;
1114 if ( !aType
.isEmpty() )
1116 // only nonempty type makes sense here
1117 ( *aIter
)[PKG_MNFST_FULLPATH
].Value
>>= aPath
;
1118 //FIXME: For now we have no way of differentiating defaults from others.
1119 aOverridesSequence
[nOverSeqLength
].First
= "/" + aPath
;
1120 aOverridesSequence
[nOverSeqLength
].Second
= aType
;
1124 aOverridesSequence
.realloc(nOverSeqLength
);
1126 ::comphelper::OFOPXMLHelper::WriteContentSequence(
1127 xConTypeOutStream
, aDefaultsSequence
, aOverridesSequence
, m_xContext
);
1129 sal_Int32 nBufferLength
= static_cast < sal_Int32
> ( pBuffer
->getPosition() );
1130 pBuffer
->realloc( nBufferLength
);
1132 // there is no encryption in this format currently
1133 ZipOutputStream::setEntry(pEntry
);
1134 aZipOut
.writeLOC(pEntry
);
1135 ZipOutputEntry
aZipEntry(aZipOut
.getStream(), m_xContext
, *pEntry
, nullptr, /*bEncrypt*/false);
1136 aZipEntry
.write(pBuffer
->getSequence());
1137 aZipEntry
.closeEntry();
1138 aZipOut
.rawCloseEntry();
1141 void ZipPackage::ConnectTo( const uno::Reference
< io::XInputStream
>& xInStream
)
1143 m_xContentSeek
.set( xInStream
, uno::UNO_QUERY_THROW
);
1144 m_xContentStream
= xInStream
;
1146 // seek back to the beginning of the temp file so we can read segments from it
1147 m_xContentSeek
->seek( 0 );
1149 m_pZipFile
->setInputStream( m_xContentStream
);
1151 m_pZipFile
= o3tl::make_unique
<ZipFile
>(m_aMutexHolder
, m_xContentStream
, m_xContext
, false);
1159 rtlRandomPool m_aRandomPool
;
1163 m_aRandomPool
= rtl_random_createPool ();
1167 return m_aRandomPool
;
1171 // Clean up random pool memory
1172 rtl_random_destroyPool(m_aRandomPool
);
1177 uno::Reference
< io::XInputStream
> ZipPackage::writeTempFile()
1179 // In case the target local file does not exist or empty
1180 // write directly to it otherwise create a temporary file to write to.
1181 // If a temporary file is created it is returned back by the method.
1182 // If the data written directly, xComponentStream will be switched here
1184 bool bUseTemp
= true;
1185 uno::Reference
< io::XInputStream
> xResult
;
1186 uno::Reference
< io::XInputStream
> xTempIn
;
1188 uno::Reference
< io::XOutputStream
> xTempOut
;
1189 uno::Reference
< io::XActiveDataStreamer
> xSink
;
1191 if ( m_eMode
== e_IMode_URL
&& !m_pZipFile
&& isLocalFile() )
1193 xSink
= openOriginalForOutput();
1196 uno::Reference
< io::XStream
> xStr
= xSink
->getStream();
1199 xTempOut
= xStr
->getOutputStream();
1205 else if ( m_eMode
== e_IMode_XStream
&& !m_pZipFile
)
1207 // write directly to an empty stream
1208 xTempOut
= m_xStream
->getOutputStream();
1215 // create temporary file
1216 uno::Reference
< io::XTempFile
> xTempFile( io::TempFile::create(m_xContext
) );
1217 xTempOut
.set( xTempFile
->getOutputStream(), UNO_SET_THROW
);
1218 xTempIn
.set( xTempFile
->getInputStream(), UNO_SET_THROW
);
1221 // Hand it to the ZipOutputStream:
1222 ZipOutputStream
aZipOut( xTempOut
);
1225 if ( m_nFormat
== embed::StorageFormats::PACKAGE
)
1227 // Remove the old manifest.xml file as the
1228 // manifest will be re-generated and the
1229 // META-INF directory implicitly created if does not exist
1230 static const OUString
sMeta ("META-INF");
1232 if ( m_xRootFolder
->hasByName( sMeta
) )
1234 const OUString
sManifest ("manifest.xml");
1236 uno::Reference
< XUnoTunnel
> xTunnel
;
1237 Any aAny
= m_xRootFolder
->getByName( sMeta
);
1239 uno::Reference
< XNameContainer
> xMetaInfFolder( xTunnel
, UNO_QUERY
);
1240 if ( xMetaInfFolder
.is() && xMetaInfFolder
->hasByName( sManifest
) )
1241 xMetaInfFolder
->removeByName( sManifest
);
1244 // Write a magic file with mimetype
1245 WriteMimetypeMagicFile( aZipOut
);
1247 else if ( m_nFormat
== embed::StorageFormats::OFOPXML
)
1249 // Remove the old [Content_Types].xml file as the
1250 // file will be re-generated
1252 static const OUString
aContentTypes("[Content_Types].xml");
1254 if ( m_xRootFolder
->hasByName( aContentTypes
) )
1255 m_xRootFolder
->removeByName( aContentTypes
);
1258 // Create a vector to store data for the manifest.xml file
1259 vector
< uno::Sequence
< PropertyValue
> > aManList
;
1261 static const OUString
sMediaType("MediaType");
1262 static const OUString
sVersion("Version");
1263 static const OUString
sFullPath("FullPath");
1264 const bool bIsGpgEncrypt
= m_aGpgProps
.hasElements();
1266 if ( m_nFormat
== embed::StorageFormats::PACKAGE
)
1268 uno::Sequence
< PropertyValue
> aPropSeq(
1269 bIsGpgEncrypt
? PKG_SIZE_NOENCR_MNFST
+1 : PKG_SIZE_NOENCR_MNFST
);
1270 aPropSeq
[PKG_MNFST_MEDIATYPE
].Name
= sMediaType
;
1271 aPropSeq
[PKG_MNFST_MEDIATYPE
].Value
<<= m_xRootFolder
->GetMediaType();
1272 aPropSeq
[PKG_MNFST_VERSION
].Name
= sVersion
;
1273 aPropSeq
[PKG_MNFST_VERSION
].Value
<<= m_xRootFolder
->GetVersion();
1274 aPropSeq
[PKG_MNFST_FULLPATH
].Name
= sFullPath
;
1275 aPropSeq
[PKG_MNFST_FULLPATH
].Value
<<= OUString("/");
1279 aPropSeq
[PKG_SIZE_NOENCR_MNFST
].Name
= "KeyInfo";
1280 aPropSeq
[PKG_SIZE_NOENCR_MNFST
].Value
<<= m_aGpgProps
;
1282 aManList
.push_back( aPropSeq
);
1286 // This will be used to generate random salt and initialisation vectors
1287 // for encrypted streams
1288 RandomPool aRandomPool
;
1290 sal_Int32
const nPBKDF2IterationCount
= 100000;
1292 // call saveContents ( it will recursively save sub-directories
1293 m_xRootFolder
->saveContents("", aManList
, aZipOut
, GetEncryptionKey(), bIsGpgEncrypt
? 0 : nPBKDF2IterationCount
, aRandomPool
.get());
1296 if( m_nFormat
== embed::StorageFormats::PACKAGE
)
1298 WriteManifest( aZipOut
, aManList
);
1300 else if( m_nFormat
== embed::StorageFormats::OFOPXML
)
1302 WriteContentTypes( aZipOut
, aManList
);
1310 // Update our References to point to the new temp file
1313 // the case when the original contents were written directly
1316 // in case the stream is based on a file it will implement the following interface
1317 // the call should be used to be sure that the contents are written to the file system
1318 uno::Reference
< io::XAsyncOutputMonitor
> asyncOutputMonitor( xTempOut
, uno::UNO_QUERY
);
1319 if (asyncOutputMonitor
.is() && !m_bDisableFileSync
)
1320 asyncOutputMonitor
->waitForCompletion();
1322 // no need to postpone switching to the new stream since the target was written directly
1323 uno::Reference
< io::XInputStream
> xNewStream
;
1324 if ( m_eMode
== e_IMode_URL
)
1325 xNewStream
= xSink
->getStream()->getInputStream();
1326 else if ( m_eMode
== e_IMode_XStream
&& m_xStream
.is() )
1327 xNewStream
= m_xStream
->getInputStream();
1329 if ( xNewStream
.is() )
1330 ConnectTo( xNewStream
);
1333 catch ( uno::Exception
& )
1337 // no information loss appears, thus no special handling is required
1338 uno::Any
aCaught( ::cppu::getCaughtException() );
1340 // it is allowed to throw WrappedTargetException
1341 WrappedTargetException aException
;
1342 if ( aCaught
>>= aException
)
1345 throw WrappedTargetException(
1346 THROW_WHERE
"Problem writing the original content!",
1347 static_cast < OWeakObject
* > ( this ),
1352 // the document is written directly, although it was empty it is important to notify that the writing has failed
1353 // TODO/LATER: let the package be able to recover in this situation
1354 OUString
aErrTxt(THROW_WHERE
"This package is unusable!");
1355 embed::UseBackupException
aException( aErrTxt
, uno::Reference
< uno::XInterface
>(), OUString() );
1356 throw WrappedTargetException( aErrTxt
,
1357 static_cast < OWeakObject
* > ( this ),
1358 makeAny ( aException
) );
1365 uno::Reference
< XActiveDataStreamer
> ZipPackage::openOriginalForOutput()
1367 // open and truncate the original file
1368 Content
aOriginalContent(
1369 m_aURL
, uno::Reference
< XCommandEnvironment
>(),
1371 uno::Reference
< XActiveDataStreamer
> xSink
= new ActiveDataStreamer
;
1373 if ( m_eMode
== e_IMode_URL
)
1377 bool bTruncSuccess
= false;
1382 Any aAny
= aOriginalContent
.setPropertyValue("Size", makeAny( sal_Int64(0) ) );
1383 if( !( aAny
>>= aDetect
) )
1384 bTruncSuccess
= true;
1390 if( !bTruncSuccess
)
1392 // the file is not accessible
1393 // just try to write an empty stream to it
1395 uno::Reference
< XInputStream
> xTempIn
= new DummyInputStream
; //uno::Reference< XInputStream >( xTempOut, UNO_QUERY );
1396 aOriginalContent
.writeStream( xTempIn
, true );
1399 OpenCommandArgument2 aArg
;
1400 aArg
.Mode
= OpenMode::DOCUMENT
;
1401 aArg
.Priority
= 0; // unused
1403 aArg
.Properties
= uno::Sequence
< Property
>( 0 ); // unused
1405 aOriginalContent
.executeCommand("open", makeAny( aArg
) );
1409 // seems to be nonlocal file
1410 // temporary file mechanics should be used
1417 void SAL_CALL
ZipPackage::commitChanges()
1419 // lock the component for the time of committing
1420 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
1422 if ( m_eMode
== e_IMode_XInputStream
)
1424 IOException aException
;
1425 throw WrappedTargetException(THROW_WHERE
"This package is read only!",
1426 static_cast < OWeakObject
* > ( this ), makeAny ( aException
) );
1428 // first the writeTempFile is called, if it returns a stream the stream should be written to the target
1429 // if no stream was returned, the file was written directly, nothing should be done
1430 uno::Reference
< io::XInputStream
> xTempInStream
;
1433 xTempInStream
= writeTempFile();
1435 catch (const ucb::ContentCreationException
& r
)
1437 throw WrappedTargetException(THROW_WHERE
"Temporary file should be creatable!",
1438 static_cast < OWeakObject
* > ( this ), makeAny ( r
) );
1440 if ( xTempInStream
.is() )
1442 uno::Reference
< io::XSeekable
> xTempSeek( xTempInStream
, uno::UNO_QUERY_THROW
);
1446 xTempSeek
->seek( 0 );
1448 catch( const uno::Exception
& r
)
1450 throw WrappedTargetException(THROW_WHERE
"Temporary file should be seekable!",
1451 static_cast < OWeakObject
* > ( this ), makeAny ( r
) );
1456 // connect to the temporary stream
1457 ConnectTo( xTempInStream
);
1459 catch( const io::IOException
& r
)
1461 throw WrappedTargetException(THROW_WHERE
"Temporary file should be connectable!",
1462 static_cast < OWeakObject
* > ( this ), makeAny ( r
) );
1465 if ( m_eMode
== e_IMode_XStream
)
1467 // First truncate our output stream
1468 uno::Reference
< XOutputStream
> xOutputStream
;
1470 // preparation for copy step
1473 xOutputStream
= m_xStream
->getOutputStream();
1475 // Make sure we avoid a situation where the current position is
1476 // not zero, but the underlying file is truncated in the
1478 uno::Reference
<io::XSeekable
> xSeekable(xOutputStream
, uno::UNO_QUERY
);
1482 uno::Reference
< XTruncate
> xTruncate ( xOutputStream
, UNO_QUERY_THROW
);
1484 // after successful truncation the original file contents are already lost
1485 xTruncate
->truncate();
1487 catch( const uno::Exception
& r
)
1489 throw WrappedTargetException(THROW_WHERE
"This package is read only!",
1490 static_cast < OWeakObject
* > ( this ), makeAny ( r
) );
1495 // then copy the contents of the tempfile to our output stream
1496 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream
, xOutputStream
);
1497 xOutputStream
->flush();
1498 uno::Reference
< io::XAsyncOutputMonitor
> asyncOutputMonitor(
1499 xOutputStream
, uno::UNO_QUERY
);
1500 if ( asyncOutputMonitor
.is() ) {
1501 asyncOutputMonitor
->waitForCompletion();
1504 catch( uno::Exception
& )
1506 // if anything goes wrong in this block the target file becomes corrupted
1507 // so an exception should be thrown as a notification about it
1508 // and the package must disconnect from the stream
1509 DisconnectFromTargetAndThrowException_Impl( xTempInStream
);
1512 else if ( m_eMode
== e_IMode_URL
)
1514 uno::Reference
< XOutputStream
> aOrigFileStream
;
1515 bool bCanBeCorrupted
= false;
1519 // write directly in case of local file
1520 uno::Reference
< css::ucb::XSimpleFileAccess3
> xSimpleAccess(
1521 SimpleFileAccess::create( m_xContext
) );
1522 OSL_ENSURE( xSimpleAccess
.is(), "Can't instantiate SimpleFileAccess service!" );
1523 uno::Reference
< io::XTruncate
> xOrigTruncate
;
1524 if ( xSimpleAccess
.is() )
1528 aOrigFileStream
= xSimpleAccess
->openFileWrite( m_aURL
);
1529 xOrigTruncate
.set( aOrigFileStream
, uno::UNO_QUERY_THROW
);
1530 // after successful truncation the file is already corrupted
1531 xOrigTruncate
->truncate();
1533 catch( uno::Exception
& )
1537 if( xOrigTruncate
.is() )
1541 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream
, aOrigFileStream
);
1542 aOrigFileStream
->closeOutput();
1544 catch( uno::Exception
& )
1547 aOrigFileStream
->closeOutput();
1548 } catch ( uno::Exception
& ) {}
1550 aOrigFileStream
.clear();
1551 // the original file can already be corrupted
1552 bCanBeCorrupted
= true;
1557 if( !aOrigFileStream
.is() )
1561 uno::Reference
< XPropertySet
> xPropSet ( xTempInStream
, UNO_QUERY_THROW
);
1563 OUString sTargetFolder
= m_aURL
.copy ( 0, m_aURL
.lastIndexOf ( u
'/' ) );
1565 sTargetFolder
, uno::Reference
< XCommandEnvironment
>(),
1569 Any aAny
= xPropSet
->getPropertyValue ("Uri");
1573 aInfo
.NameClash
= NameClash::OVERWRITE
;
1574 aInfo
.MoveData
= false;
1575 aInfo
.SourceURL
= sTempURL
;
1576 aInfo
.NewTitle
= rtl::Uri::decode ( m_aURL
.copy ( 1 + m_aURL
.lastIndexOf ( u
'/' ) ),
1577 rtl_UriDecodeWithCharset
,
1578 RTL_TEXTENCODING_UTF8
);
1579 // if the file is still not corrupted, it can become after the next step
1580 aContent
.executeCommand ("transfer", Any(aInfo
) );
1582 catch ( const css::uno::Exception
& r
)
1584 if ( bCanBeCorrupted
)
1585 DisconnectFromTargetAndThrowException_Impl( xTempInStream
);
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 const uno::Sequence
< sal_Int8
> ZipPackage::GetEncryptionKey()
1630 uno::Sequence
< sal_Int8
> aResult
;
1632 if ( m_aStorageEncryptionKeys
.getLength() )
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 ( sal_Int32 nInd
= 0; nInd
< m_aStorageEncryptionKeys
.getLength(); nInd
++ )
1643 if ( m_aStorageEncryptionKeys
[nInd
].Name
== aNameToFind
)
1644 m_aStorageEncryptionKeys
[nInd
].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
.getLength() )
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 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 OUString("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::getUnoTunnelImplementationId()
1712 static ::cppu::OImplementationId implId
;
1714 return implId
.getImplementationId();
1717 sal_Int64 SAL_CALL
ZipPackage::getSomething( const uno::Sequence
< sal_Int8
>& aIdentifier
)
1719 if ( aIdentifier
.getLength() == 16 && 0 == memcmp( getUnoTunnelImplementationId().getConstArray(), aIdentifier
.getConstArray(), 16 ) )
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(THROW_WHERE
);
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
) || ( aKeys
.getLength() && aKeys
.getLength() < 1 ) )
1753 throw IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 2 );
1755 if ( aKeys
.getLength() )
1757 bool bHasSHA256
= false;
1758 bool bHasSHA1
= false;
1759 for ( sal_Int32 nInd
= 0; nInd
< aKeys
.getLength(); nInd
++ )
1761 if ( aKeys
[nInd
].Name
== PACKAGE_ENCRYPTIONDATA_SHA256UTF8
)
1763 if ( aKeys
[nInd
].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
.getLength() == 0 )
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 ( sal_Int32 nInd
= 0; nInd
< aAlgorithms
.getLength(); nInd
++ )
1785 if ( aAlgorithms
[nInd
].Name
== "StartKeyGenerationAlgorithm" )
1788 if ( !( aAlgorithms
[nInd
].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 ( aAlgorithms
[nInd
].Name
== "EncryptionAlgorithm" )
1797 if ( !( aAlgorithms
[nInd
].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 ( aAlgorithms
[nInd
].Name
== "ChecksumAlgorithm" )
1806 if ( !( aAlgorithms
[nInd
].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
.getLength() == 0 )
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(THROW_WHERE
);
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(THROW_WHERE
);
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: */