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/zip/ZipException.hpp>
33 #include <com/sun/star/packages/zip/ZipIOException.hpp>
34 #include <com/sun/star/packages/manifest/ManifestReader.hpp>
35 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
36 #include <com/sun/star/io/TempFile.hpp>
37 #include <com/sun/star/io/XStream.hpp>
38 #include <com/sun/star/io/XInputStream.hpp>
39 #include <com/sun/star/io/XOutputStream.hpp>
40 #include <com/sun/star/io/XTruncate.hpp>
41 #include <com/sun/star/io/XSeekable.hpp>
42 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
43 #include <com/sun/star/container/XNameContainer.hpp>
44 #include <comphelper/fileurl.hxx>
45 #include <comphelper/processfactory.hxx>
46 #include <ucbhelper/content.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/SimpleFileAccess.hpp>
54 #include <com/sun/star/io/XActiveDataStreamer.hpp>
55 #include <com/sun/star/embed/UseBackupException.hpp>
56 #include <com/sun/star/embed/StorageFormats.hpp>
57 #include <com/sun/star/beans/NamedValue.hpp>
58 #include <com/sun/star/xml/crypto/DigestID.hpp>
59 #include <cppuhelper/implbase.hxx>
60 #include <rtl/uri.hxx>
61 #include <rtl/random.h>
62 #include <o3tl/string_view.hxx>
63 #include <osl/diagnose.h>
64 #include <sal/log.hxx>
65 #include <unotools/streamwrap.hxx>
66 #include <unotools/tempfile.hxx>
67 #include <com/sun/star/io/XAsyncOutputMonitor.hpp>
69 #include <string_view>
71 #include <comphelper/seekableinput.hxx>
72 #include <comphelper/storagehelper.hxx>
73 #include <comphelper/ofopxmlhelper.hxx>
74 #include <comphelper/documentconstants.hxx>
75 #include <comphelper/sequenceashashmap.hxx>
76 #include <cppuhelper/supportsservice.hxx>
77 #include <comphelper/sequence.hxx>
78 #include <comphelper/servicehelper.hxx>
83 using namespace ucbhelper
;
84 using namespace com::sun::star
;
85 using namespace com::sun::star::io
;
86 using namespace com::sun::star::uno
;
87 using namespace com::sun::star::ucb
;
88 using namespace com::sun::star::util
;
89 using namespace com::sun::star::lang
;
90 using namespace com::sun::star::task
;
91 using namespace com::sun::star::beans
;
92 using namespace com::sun::star::packages
;
93 using namespace com::sun::star::container
;
94 using namespace com::sun::star::packages::zip
;
95 using namespace com::sun::star::packages::manifest
;
96 using namespace com::sun::star::packages::zip::ZipConstants
;
98 #if OSL_DEBUG_LEVEL > 0
99 #define THROW_WHERE SAL_WHERE
101 #define THROW_WHERE ""
106 class ActiveDataStreamer
: public ::cppu::WeakImplHelper
< XActiveDataStreamer
>
108 uno::Reference
< XStream
> mStream
;
111 virtual uno::Reference
< XStream
> SAL_CALL
getStream() override
114 virtual void SAL_CALL
setStream( const uno::Reference
< XStream
>& stream
) override
115 { mStream
= stream
; }
118 class DummyInputStream
: public ::cppu::WeakImplHelper
< XInputStream
>
120 virtual sal_Int32 SAL_CALL
readBytes( uno::Sequence
< sal_Int8
>&, sal_Int32
) override
123 virtual sal_Int32 SAL_CALL
readSomeBytes( uno::Sequence
< sal_Int8
>&, sal_Int32
) override
126 virtual void SAL_CALL
skipBytes( sal_Int32
) override
129 virtual sal_Int32 SAL_CALL
available() override
132 virtual void SAL_CALL
closeInput() override
138 ZipPackage::ZipPackage ( uno::Reference
< XComponentContext
> xContext
)
139 : m_aMutexHolder( new comphelper::RefCountedMutex
)
140 , m_nStartKeyGenerationID( xml::crypto::DigestID::SHA1
)
141 , m_nChecksumDigestID( xml::crypto::DigestID::SHA1_1K
)
142 , m_nCommonEncryptionID( xml::crypto::CipherID::BLOWFISH_CFB_8
)
143 , m_bHasEncryptedEntries ( false )
144 , m_bHasNonEncryptedEntries ( false )
145 , m_bInconsistent ( false )
146 , m_bForceRecovery ( false )
147 , m_bMediaTypeFallbackUsed ( false )
148 , m_nFormat( embed::StorageFormats::PACKAGE
) // package is the default format
149 , m_bAllowRemoveOnInsert( true )
150 , m_eMode ( e_IMode_None
)
151 , m_xContext(std::move( xContext
))
153 m_xRootFolder
= new ZipPackageFolder( m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
156 ZipPackage::~ZipPackage()
160 bool ZipPackage::isLocalFile() const
162 return comphelper::isFileUrl(m_aURL
);
165 void ZipPackage::parseManifest()
167 if ( m_nFormat
!= embed::StorageFormats::PACKAGE
)
170 bool bManifestParsed
= false;
171 static const OUStringLiteral
sMeta (u
"META-INF");
172 if ( m_xRootFolder
->hasByName( sMeta
) )
175 static const OUStringLiteral
sManifest (u
"manifest.xml");
176 Any aAny
= m_xRootFolder
->getByName( sMeta
);
177 uno::Reference
< XNameContainer
> xMetaInfFolder
;
178 aAny
>>= xMetaInfFolder
;
179 if ( xMetaInfFolder
.is() && xMetaInfFolder
->hasByName( sManifest
) )
181 uno::Reference
< XActiveDataSink
> xSink
;
182 aAny
= xMetaInfFolder
->getByName( sManifest
);
186 uno::Reference
< XManifestReader
> xReader
= ManifestReader::create( m_xContext
);
188 static const OUStringLiteral
sPropFullPath (u
"FullPath");
189 static const OUStringLiteral
sPropVersion (u
"Version");
190 static const OUStringLiteral
sPropMediaType (u
"MediaType");
191 static const OUStringLiteral
sPropInitialisationVector (u
"InitialisationVector");
192 static const OUStringLiteral
sPropSalt (u
"Salt");
193 static const OUStringLiteral
sPropIterationCount (u
"IterationCount");
194 static const OUStringLiteral
sPropSize (u
"Size");
195 static const OUStringLiteral
sPropDigest (u
"Digest");
196 static const OUStringLiteral
sPropDerivedKeySize (u
"DerivedKeySize");
197 static const OUStringLiteral
sPropDigestAlgorithm (u
"DigestAlgorithm");
198 static const OUStringLiteral
sPropEncryptionAlgorithm (u
"EncryptionAlgorithm");
199 static const OUStringLiteral
sPropStartKeyAlgorithm (u
"StartKeyAlgorithm");
200 static const OUStringLiteral
sKeyInfo (u
"KeyInfo");
202 const uno::Sequence
< uno::Sequence
< PropertyValue
> > aManifestSequence
= xReader
->readManifestSequence ( xSink
->getInputStream() );
203 const Any
*pKeyInfo
= nullptr;
205 for ( const uno::Sequence
<PropertyValue
>& rSequence
: aManifestSequence
)
207 OUString sPath
, sMediaType
, sVersion
;
208 const Any
*pSalt
= nullptr, *pVector
= nullptr, *pCount
= nullptr, *pSize
= nullptr, *pDigest
= nullptr, *pDigestAlg
= nullptr, *pEncryptionAlg
= nullptr, *pStartKeyAlg
= nullptr, *pDerivedKeySize
= nullptr;
209 for ( const PropertyValue
& rValue
: rSequence
)
211 if ( rValue
.Name
== sPropFullPath
)
212 rValue
.Value
>>= sPath
;
213 else if ( rValue
.Name
== sPropVersion
)
214 rValue
.Value
>>= sVersion
;
215 else if ( rValue
.Name
== sPropMediaType
)
216 rValue
.Value
>>= sMediaType
;
217 else if ( rValue
.Name
== sPropSalt
)
218 pSalt
= &( rValue
.Value
);
219 else if ( rValue
.Name
== sPropInitialisationVector
)
220 pVector
= &( rValue
.Value
);
221 else if ( rValue
.Name
== sPropIterationCount
)
222 pCount
= &( rValue
.Value
);
223 else if ( rValue
.Name
== sPropSize
)
224 pSize
= &( rValue
.Value
);
225 else if ( rValue
.Name
== sPropDigest
)
226 pDigest
= &( rValue
.Value
);
227 else if ( rValue
.Name
== sPropDigestAlgorithm
)
228 pDigestAlg
= &( rValue
.Value
);
229 else if ( rValue
.Name
== sPropEncryptionAlgorithm
)
230 pEncryptionAlg
= &( rValue
.Value
);
231 else if ( rValue
.Name
== sPropStartKeyAlgorithm
)
232 pStartKeyAlg
= &( rValue
.Value
);
233 else if ( rValue
.Name
== sPropDerivedKeySize
)
234 pDerivedKeySize
= &( rValue
.Value
);
235 else if ( rValue
.Name
== sKeyInfo
)
236 pKeyInfo
= &( rValue
.Value
);
239 if ( !sPath
.isEmpty() && hasByHierarchicalName ( sPath
) )
241 aAny
= getByHierarchicalName( sPath
);
242 uno::Reference
< XInterface
> xTmp
;
244 if (auto pFolder
= dynamic_cast<ZipPackageFolder
*>(xTmp
.get()))
246 pFolder
->SetMediaType ( sMediaType
);
247 pFolder
->SetVersion ( sVersion
);
249 else if (auto pStream
= dynamic_cast<ZipPackageStream
*>(xTmp
.get()))
251 pStream
->SetMediaType ( sMediaType
);
252 pStream
->SetFromManifest( true );
254 if ( pKeyInfo
&& pVector
&& pSize
&& pDigest
&& pDigestAlg
&& pEncryptionAlg
)
256 uno::Sequence
< sal_Int8
> aSequence
;
258 sal_Int32 nDigestAlg
= 0, nEncryptionAlg
= 0;
260 pStream
->SetToBeEncrypted ( true );
262 *pVector
>>= aSequence
;
263 pStream
->setInitialisationVector ( aSequence
);
266 pStream
->setSize ( nSize
);
268 *pDigest
>>= aSequence
;
269 pStream
->setDigest ( aSequence
);
271 *pDigestAlg
>>= nDigestAlg
;
272 pStream
->SetImportedChecksumAlgorithm( nDigestAlg
);
274 *pEncryptionAlg
>>= nEncryptionAlg
;
275 pStream
->SetImportedEncryptionAlgorithm( nEncryptionAlg
);
277 *pKeyInfo
>>= m_aGpgProps
;
279 pStream
->SetToBeCompressed ( true );
280 pStream
->SetToBeEncrypted ( true );
281 pStream
->SetIsEncrypted ( true );
282 pStream
->setIterationCount(0);
284 // clamp to default SHA256 start key magic value,
285 // c.f. ZipPackageStream::GetEncryptionKey()
286 // trying to get key value from properties
287 const sal_Int32 nStartKeyAlg
= xml::crypto::DigestID::SHA256
;
288 pStream
->SetImportedStartKeyAlgorithm( nStartKeyAlg
);
290 if ( !m_bHasEncryptedEntries
&& pStream
->getName() == "content.xml" )
292 m_bHasEncryptedEntries
= true;
293 m_nChecksumDigestID
= nDigestAlg
;
294 m_nCommonEncryptionID
= nEncryptionAlg
;
295 m_nStartKeyGenerationID
= nStartKeyAlg
;
298 else if ( pSalt
&& pVector
&& pCount
&& pSize
&& pDigest
&& pDigestAlg
&& pEncryptionAlg
)
300 uno::Sequence
< sal_Int8
> aSequence
;
302 sal_Int32 nCount
= 0, nDigestAlg
= 0, nEncryptionAlg
= 0;
303 sal_Int32 nDerivedKeySize
= 16, nStartKeyAlg
= xml::crypto::DigestID::SHA1
;
305 pStream
->SetToBeEncrypted ( true );
307 *pSalt
>>= aSequence
;
308 pStream
->setSalt ( aSequence
);
310 *pVector
>>= aSequence
;
311 pStream
->setInitialisationVector ( aSequence
);
314 pStream
->setIterationCount ( nCount
);
317 pStream
->setSize ( nSize
);
319 *pDigest
>>= aSequence
;
320 pStream
->setDigest ( aSequence
);
322 *pDigestAlg
>>= nDigestAlg
;
323 pStream
->SetImportedChecksumAlgorithm( nDigestAlg
);
325 *pEncryptionAlg
>>= nEncryptionAlg
;
326 pStream
->SetImportedEncryptionAlgorithm( nEncryptionAlg
);
328 if ( pDerivedKeySize
)
329 *pDerivedKeySize
>>= nDerivedKeySize
;
330 pStream
->SetImportedDerivedKeySize( nDerivedKeySize
);
333 *pStartKeyAlg
>>= nStartKeyAlg
;
334 pStream
->SetImportedStartKeyAlgorithm( nStartKeyAlg
);
336 pStream
->SetToBeCompressed ( true );
337 pStream
->SetToBeEncrypted ( true );
338 pStream
->SetIsEncrypted ( true );
339 if ( !m_bHasEncryptedEntries
&& pStream
->getName() == "content.xml" )
341 m_bHasEncryptedEntries
= true;
342 m_nStartKeyGenerationID
= nStartKeyAlg
;
343 m_nChecksumDigestID
= nDigestAlg
;
344 m_nCommonEncryptionID
= nEncryptionAlg
;
348 m_bHasNonEncryptedEntries
= true;
351 throw ZipIOException(THROW_WHERE
"Wrong content");
355 bManifestParsed
= true;
358 // now hide the manifest.xml file from user
359 xMetaInfFolder
->removeByName( sManifest
);
364 if ( !m_bForceRecovery
)
369 if ( !bManifestParsed
&& !m_bForceRecovery
)
370 throw ZipIOException(
371 THROW_WHERE
"Could not parse manifest.xml" );
373 static const OUStringLiteral
sMimetype (u
"mimetype");
374 if ( m_xRootFolder
->hasByName( sMimetype
) )
376 // get mediatype from the "mimetype" stream
377 OUString aPackageMediatype
;
378 uno::Reference
< io::XActiveDataSink
> xMimeSink
;
379 m_xRootFolder
->getByName( sMimetype
) >>= xMimeSink
;
380 if ( xMimeSink
.is() )
382 uno::Reference
< io::XInputStream
> xMimeInStream
= xMimeSink
->getInputStream();
383 if ( xMimeInStream
.is() )
385 // Mediatypes longer than 1024 symbols should not appear here
386 uno::Sequence
< sal_Int8
> aData( 1024 );
387 sal_Int32 nRead
= xMimeInStream
->readBytes( aData
, 1024 );
388 if ( nRead
> aData
.getLength() )
389 nRead
= aData
.getLength();
392 aPackageMediatype
= OUString( reinterpret_cast<char const *>(aData
.getConstArray()), nRead
, RTL_TEXTENCODING_ASCII_US
);
396 if ( !bManifestParsed
)
398 // the manifest.xml could not be successfully parsed, this is an inconsistent package
399 if ( aPackageMediatype
.startsWith("application/vnd.") )
401 // accept only types that look similar to own mediatypes
402 m_xRootFolder
->SetMediaType( aPackageMediatype
);
403 m_bMediaTypeFallbackUsed
= true;
406 else if ( !m_bForceRecovery
)
408 // the mimetype stream should contain the information from manifest.xml
409 if ( m_xRootFolder
->GetMediaType() != aPackageMediatype
)
410 throw ZipIOException(
412 "mimetype conflicts with manifest.xml, \""
413 + m_xRootFolder
->GetMediaType() + "\" vs. \""
414 + aPackageMediatype
+ "\"" );
417 m_xRootFolder
->removeByName( sMimetype
);
420 m_bInconsistent
= m_xRootFolder
->LookForUnexpectedODF12Streams( std::u16string_view() );
422 bool bODF12AndNewer
= ( m_xRootFolder
->GetVersion().compareTo( ODFVER_012_TEXT
) >= 0 );
423 if ( !m_bForceRecovery
&& bODF12AndNewer
)
425 if ( m_bInconsistent
)
427 // this is an ODF1.2 document that contains streams not referred in the manifest.xml;
428 // in case of ODF1.2 documents without version in manifest.xml the property IsInconsistent
429 // should be checked later
430 throw ZipIOException(
431 THROW_WHERE
"there are streams not referred in manifest.xml" );
433 // all the streams should be encrypted with the same StartKey in ODF1.2
434 // TODO/LATER: in future the exception should be thrown
435 // throw ZipIOException( THROW_WHERE "More than one Start Key Generation algorithm is specified!" );
438 // in case it is a correct ODF1.2 document, the version must be set
439 // and the META-INF folder is reserved for package format
440 if ( bODF12AndNewer
)
441 m_xRootFolder
->removeByName( sMeta
);
444 void ZipPackage::parseContentType()
446 if ( m_nFormat
!= embed::StorageFormats::OFOPXML
)
450 static const OUStringLiteral
aContentTypes(u
"[Content_Types].xml");
451 // the content type must exist in OFOPXML format!
452 if ( !m_xRootFolder
->hasByName( aContentTypes
) )
453 throw io::IOException(THROW_WHERE
"Wrong format!" );
455 uno::Reference
< io::XActiveDataSink
> xSink
;
456 uno::Any aAny
= m_xRootFolder
->getByName( aContentTypes
);
460 uno::Reference
< io::XInputStream
> xInStream
= xSink
->getInputStream();
461 if ( xInStream
.is() )
463 // here aContentTypeInfo[0] - Defaults, and aContentTypeInfo[1] - Overrides
464 const uno::Sequence
< uno::Sequence
< beans::StringPair
> > aContentTypeInfo
=
465 ::comphelper::OFOPXMLHelper::ReadContentTypeSequence( xInStream
, m_xContext
);
467 if ( aContentTypeInfo
.getLength() != 2 )
468 throw io::IOException(THROW_WHERE
);
470 // set the implicit types first
471 for ( const auto& rPair
: aContentTypeInfo
[0] )
472 m_xRootFolder
->setChildStreamsTypeByExtension( rPair
);
474 // now set the explicit types
475 for ( const auto& rPair
: aContentTypeInfo
[1] )
478 if ( rPair
.First
.toChar() == '/' )
479 aPath
= rPair
.First
.copy( 1 );
483 if ( !aPath
.isEmpty() && hasByHierarchicalName( aPath
) )
485 uno::Any aIterAny
= getByHierarchicalName( aPath
);
486 uno::Reference
< XInterface
> xIterTmp
;
487 aIterAny
>>= xIterTmp
;
488 if (auto pStream
= dynamic_cast<ZipPackageStream
*>(xIterTmp
.get()))
490 // this is a package stream, in OFOPXML format only streams can have mediatype
491 pStream
->SetMediaType( rPair
.Second
);
498 m_xRootFolder
->removeByName( aContentTypes
);
500 catch( uno::Exception
& )
502 if ( !m_bForceRecovery
)
507 void ZipPackage::getZipFileContents()
509 ZipEnumeration aEnum
= m_pZipFile
->entries();
510 OUString sTemp
, sDirName
;
511 sal_Int32 nOldIndex
, nStreamIndex
;
512 FolderHash::iterator aIter
;
514 while (aEnum
.hasMoreElements())
517 ZipPackageFolder
* pCurrent
= m_xRootFolder
.get();
518 const ZipEntry
& rEntry
= *aEnum
.nextElement();
519 OUString rName
= rEntry
.sPath
;
521 if ( m_bForceRecovery
)
523 // the PKZIP Application note version 6.2 does not allows to use '\' as separator
524 // unfortunately it is used by some implementations, so we have to support it in recovery mode
525 rName
= rName
.replace( '\\', '/' );
528 nStreamIndex
= rName
.lastIndexOf ( '/' );
529 if ( nStreamIndex
!= -1 )
531 sDirName
= rName
.copy ( 0, nStreamIndex
);
532 aIter
= m_aRecent
.find ( sDirName
);
533 if ( aIter
!= m_aRecent
.end() )
534 pCurrent
= ( *aIter
).second
;
537 if ( pCurrent
== m_xRootFolder
.get() )
540 while ( ( nIndex
= rName
.indexOf( '/', nOldIndex
) ) != -1 )
542 sTemp
= rName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
543 if ( nIndex
== nOldIndex
)
545 if ( !pCurrent
->hasByName( sTemp
) )
547 rtl::Reference
<ZipPackageFolder
> pPkgFolder
= new ZipPackageFolder(m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
548 pPkgFolder
->setName( sTemp
);
549 pPkgFolder
->doSetParent( pCurrent
);
550 pCurrent
= pPkgFolder
.get();
554 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
556 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
557 pCurrent
= rInfo
.pFolder
;
559 nOldIndex
= nIndex
+1;
561 if ( nStreamIndex
!= -1 && !sDirName
.isEmpty() )
562 m_aRecent
[ sDirName
] = pCurrent
;
564 if ( rName
.getLength() -1 != nStreamIndex
)
567 sTemp
= rName
.copy( nStreamIndex
);
569 if (!pCurrent
->hasByName(sTemp
))
571 rtl::Reference
<ZipPackageStream
> pPkgStream
= new ZipPackageStream(*this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
572 pPkgStream
->SetPackageMember(true);
573 pPkgStream
->setZipEntryOnLoading(rEntry
);
574 pPkgStream
->setName(sTemp
);
575 pPkgStream
->doSetParent(pCurrent
);
580 if ( m_nFormat
== embed::StorageFormats::PACKAGE
)
582 else if ( m_nFormat
== embed::StorageFormats::OFOPXML
)
586 void SAL_CALL
ZipPackage::initialize( const uno::Sequence
< Any
>& aArguments
)
588 beans::NamedValue aNamedValue
;
590 if ( !aArguments
.hasElements() )
593 bool bHaveZipFile
= true;
595 for( const auto& rArgument
: aArguments
)
598 if ( rArgument
>>= aParamUrl
)
600 m_eMode
= e_IMode_URL
;
603 sal_Int32 nParam
= aParamUrl
.indexOf( '?' );
606 m_aURL
= aParamUrl
.copy( 0, nParam
);
607 std::u16string_view aParam
= aParamUrl
.subView( nParam
+ 1 );
609 sal_Int32 nIndex
= 0;
612 std::u16string_view aCommand
= o3tl::getToken(aParam
, 0, '&', nIndex
);
613 if ( aCommand
== u
"repairpackage" )
615 m_bForceRecovery
= true;
618 else if ( aCommand
== u
"purezip" )
620 m_nFormat
= embed::StorageFormats::ZIP
;
621 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
624 else if ( aCommand
== u
"ofopxml" )
626 m_nFormat
= embed::StorageFormats::OFOPXML
;
627 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
631 while ( nIndex
>= 0 );
637 m_aURL
, uno::Reference
< XCommandEnvironment
>(),
639 Any aAny
= aContent
.getPropertyValue("Size");
640 sal_uInt64 aSize
= 0;
641 // kind of optimization: treat empty files as nonexistent files
642 // and write to such files directly. Note that "Size" property is optional.
643 bool bHasSizeProperty
= aAny
>>= aSize
;
644 if( !bHasSizeProperty
|| aSize
)
646 uno::Reference
< XActiveDataSink
> xSink
= new ZipPackageSink
;
647 if ( aContent
.openStream ( xSink
) )
648 m_xContentStream
= xSink
->getInputStream();
651 bHaveZipFile
= false;
653 catch ( css::uno::Exception
& )
655 // Exception derived from uno::Exception thrown. This probably
656 // means the file doesn't exist...we'll create it at
657 // commitChanges time
658 bHaveZipFile
= false;
661 else if ( rArgument
>>= m_xStream
)
663 // a writable stream can implement both XStream & XInputStream
664 m_eMode
= e_IMode_XStream
;
665 m_xContentStream
= m_xStream
->getInputStream();
667 else if ( rArgument
>>= m_xContentStream
)
669 m_eMode
= e_IMode_XInputStream
;
671 else if ( rArgument
>>= aNamedValue
)
673 if ( aNamedValue
.Name
== "RepairPackage" )
674 aNamedValue
.Value
>>= m_bForceRecovery
;
675 else if ( aNamedValue
.Name
== "PackageFormat" )
677 // setting this argument to true means Package format
678 // setting it to false means plain Zip format
680 bool bPackFormat
= true;
681 aNamedValue
.Value
>>= bPackFormat
;
683 m_nFormat
= embed::StorageFormats::ZIP
;
685 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
687 else if ( aNamedValue
.Name
== "StorageFormat" )
689 OUString aFormatName
;
690 sal_Int32 nFormatID
= 0;
691 if ( aNamedValue
.Value
>>= aFormatName
)
693 if ( aFormatName
== PACKAGE_STORAGE_FORMAT_STRING
)
694 m_nFormat
= embed::StorageFormats::PACKAGE
;
695 else if ( aFormatName
== ZIP_STORAGE_FORMAT_STRING
)
696 m_nFormat
= embed::StorageFormats::ZIP
;
697 else if ( aFormatName
== OFOPXML_STORAGE_FORMAT_STRING
)
698 m_nFormat
= embed::StorageFormats::OFOPXML
;
700 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
702 else if ( aNamedValue
.Value
>>= nFormatID
)
704 if (nFormatID
!= embed::StorageFormats::PACKAGE
705 && nFormatID
!= embed::StorageFormats::ZIP
706 && nFormatID
!= embed::StorageFormats::OFOPXML
)
707 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
709 m_nFormat
= nFormatID
;
712 throw lang::IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 1 );
714 m_xRootFolder
->setPackageFormat_Impl( m_nFormat
);
716 else if ( aNamedValue
.Name
== "AllowRemoveOnInsert" )
718 aNamedValue
.Value
>>= m_bAllowRemoveOnInsert
;
719 m_xRootFolder
->setRemoveOnInsertMode_Impl( m_bAllowRemoveOnInsert
);
721 else if (aNamedValue
.Name
== "NoFileSync")
722 aNamedValue
.Value
>>= m_bDisableFileSync
;
724 // for now the progress handler is not used, probably it will never be
725 // if ( aNamedValue.Name == "ProgressHandler" )
729 // The URL is not acceptable
730 throw css::uno::Exception (THROW_WHERE
"Bad arguments.",
731 static_cast < ::cppu::OWeakObject
* > ( this ) );
737 if ( m_xContentStream
.is() )
739 // the stream must be seekable, if it is not it will be wrapped
740 m_xContentStream
= ::comphelper::OSeekableInputWrapper::CheckSeekableCanWrap( m_xContentStream
, m_xContext
);
741 m_xContentSeek
.set( m_xContentStream
, UNO_QUERY_THROW
);
742 if ( !m_xContentSeek
->getLength() )
743 bHaveZipFile
= false;
746 bHaveZipFile
= false;
748 catch ( css::uno::Exception
& )
750 // Exception derived from uno::Exception thrown. This probably
751 // means the file doesn't exist...we'll create it at
752 // commitChanges time
753 bHaveZipFile
= false;
758 bool bBadZipFile
= false;
762 m_pZipFile
.emplace(m_aMutexHolder
, m_xContentStream
, m_xContext
, true, m_bForceRecovery
);
763 getZipFileContents();
765 catch ( IOException
& e
)
768 message
= "IOException: " + e
.Message
;
770 catch ( ZipException
& e
)
773 message
= "ZipException: " + e
.Message
;
775 catch ( Exception
& )
783 // clean up the memory, and tell the UCB about the error
786 throw css::packages::zip::ZipIOException (
787 THROW_WHERE
"Bad Zip File, " + message
,
788 static_cast < ::cppu::OWeakObject
* > ( this ) );
792 Any SAL_CALL
ZipPackage::getByHierarchicalName( const OUString
& aName
)
794 OUString sTemp
, sDirName
;
795 sal_Int32 nOldIndex
, nStreamIndex
;
796 FolderHash::iterator aIter
;
798 sal_Int32 nIndex
= aName
.getLength();
802 return Any ( uno::Reference
< XInterface
> ( static_cast<cppu::OWeakObject
*>(m_xRootFolder
.get()) ) );
804 nStreamIndex
= aName
.lastIndexOf ( '/' );
805 bool bFolder
= nStreamIndex
== nIndex
-1; // last character is '/'.
807 if ( nStreamIndex
!= -1 )
809 // The name contains '/'.
810 sDirName
= aName
.copy ( 0, nStreamIndex
);
811 aIter
= m_aRecent
.find ( sDirName
);
812 if ( aIter
!= m_aRecent
.end() )
814 // There is a cached entry for this name.
816 ZipPackageFolder
* pFolder
= aIter
->second
;
820 // Determine the directory name.
821 sal_Int32 nDirIndex
= aName
.lastIndexOf ( '/', nStreamIndex
);
822 sTemp
= aName
.copy ( nDirIndex
== -1 ? 0 : nDirIndex
+1, nStreamIndex
-nDirIndex
-1 );
824 if (pFolder
&& sTemp
== pFolder
->getName())
825 return Any(uno::Reference
<XInterface
>(static_cast<cppu::OWeakObject
*>(pFolder
)));
829 // Determine the file name.
830 sTemp
= aName
.copy ( nStreamIndex
+ 1 );
832 if (pFolder
&& pFolder
->hasByName(sTemp
))
833 return pFolder
->getByName(sTemp
);
836 m_aRecent
.erase( aIter
);
839 else if ( m_xRootFolder
->hasByName ( aName
) )
840 // top-level element.
841 return m_xRootFolder
->getByName ( aName
);
843 // Not in the cache. Search normally.
846 ZipPackageFolder
* pCurrent
= m_xRootFolder
.get();
847 ZipPackageFolder
* pPrevious
= nullptr;
849 // Find the right directory for the given path.
851 while ( ( nIndex
= aName
.indexOf( '/', nOldIndex
)) != -1 )
853 sTemp
= aName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
854 if ( nIndex
== nOldIndex
)
856 if ( !pCurrent
->hasByName( sTemp
) )
857 throw NoSuchElementException(THROW_WHERE
);
859 pPrevious
= pCurrent
;
860 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
862 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
863 pCurrent
= rInfo
.pFolder
;
864 nOldIndex
= nIndex
+1;
869 if ( nStreamIndex
!= -1 )
870 m_aRecent
[sDirName
] = pPrevious
; // cache it.
871 return Any ( uno::Reference
< XInterface
> ( static_cast<cppu::OWeakObject
*>(pCurrent
) ) );
874 sTemp
= aName
.copy( nOldIndex
);
876 if ( pCurrent
->hasByName ( sTemp
) )
878 if ( nStreamIndex
!= -1 )
879 m_aRecent
[sDirName
] = pCurrent
; // cache it.
880 return pCurrent
->getByName( sTemp
);
883 throw NoSuchElementException(THROW_WHERE
);
886 sal_Bool SAL_CALL
ZipPackage::hasByHierarchicalName( const OUString
& aName
)
890 FolderHash::iterator aIter
;
892 sal_Int32 nIndex
= aName
.getLength();
901 sal_Int32 nStreamIndex
;
902 nStreamIndex
= aName
.lastIndexOf ( '/' );
903 bool bFolder
= nStreamIndex
== nIndex
-1;
904 if ( nStreamIndex
!= -1 )
906 sDirName
= aName
.copy ( 0, nStreamIndex
);
907 aIter
= m_aRecent
.find ( sDirName
);
908 if ( aIter
!= m_aRecent
.end() )
912 sal_Int32 nDirIndex
= aName
.lastIndexOf ( '/', nStreamIndex
);
913 sTemp
= aName
.copy ( nDirIndex
== -1 ? 0 : nDirIndex
+1, nStreamIndex
-nDirIndex
-1 );
914 if ( sTemp
== ( *aIter
).second
->getName() )
917 m_aRecent
.erase ( aIter
);
921 sTemp
= aName
.copy ( nStreamIndex
+ 1 );
922 if ( ( *aIter
).second
->hasByName( sTemp
) )
925 m_aRecent
.erase( aIter
);
931 if ( m_xRootFolder
->hasByName ( aName
) )
934 ZipPackageFolder
* pCurrent
= m_xRootFolder
.get();
935 ZipPackageFolder
* pPrevious
= nullptr;
937 while ( ( nIndex
= aName
.indexOf( '/', nOldIndex
)) != -1 )
939 sTemp
= aName
.copy ( nOldIndex
, nIndex
- nOldIndex
);
940 if ( nIndex
== nOldIndex
)
942 if ( pCurrent
->hasByName( sTemp
) )
944 pPrevious
= pCurrent
;
945 ZipContentInfo
& rInfo
= pCurrent
->doGetByName(sTemp
);
947 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
948 pCurrent
= rInfo
.pFolder
;
952 nOldIndex
= nIndex
+1;
956 m_aRecent
[sDirName
] = pPrevious
;
961 sTemp
= aName
.copy( nOldIndex
);
963 if ( pCurrent
->hasByName( sTemp
) )
965 m_aRecent
[sDirName
] = pCurrent
;
970 catch (const uno::RuntimeException
&)
974 catch (const uno::Exception
&)
976 uno::Any
e(::cppu::getCaughtException());
977 throw lang::WrappedTargetRuntimeException("ZipPackage::hasByHierarchicalName", nullptr, e
);
982 uno::Reference
< XInterface
> SAL_CALL
ZipPackage::createInstance()
984 uno::Reference
< XInterface
> xRef
= *( new ZipPackageStream( *this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
) );
988 uno::Reference
< XInterface
> SAL_CALL
ZipPackage::createInstanceWithArguments( const uno::Sequence
< Any
>& aArguments
)
991 uno::Reference
< XInterface
> xRef
;
992 if ( aArguments
.hasElements() )
993 aArguments
[0] >>= bArg
;
995 xRef
= *new ZipPackageFolder( m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
997 xRef
= *new ZipPackageStream( *this, m_xContext
, m_nFormat
, m_bAllowRemoveOnInsert
);
1002 void ZipPackage::WriteMimetypeMagicFile( ZipOutputStream
& aZipOut
)
1004 static const OUStringLiteral
sMime (u
"mimetype");
1005 if ( m_xRootFolder
->hasByName( sMime
) )
1006 m_xRootFolder
->removeByName( sMime
);
1008 ZipEntry
* pEntry
= new ZipEntry
;
1009 sal_Int32 nBufferLength
= m_xRootFolder
->GetMediaType().getLength();
1010 OString sMediaType
= OUStringToOString( m_xRootFolder
->GetMediaType(), RTL_TEXTENCODING_ASCII_US
);
1011 const uno::Sequence
< sal_Int8
> aType( reinterpret_cast<sal_Int8
const *>(sMediaType
.getStr()),
1014 pEntry
->sPath
= sMime
;
1015 pEntry
->nMethod
= STORED
;
1016 pEntry
->nSize
= pEntry
->nCompressedSize
= nBufferLength
;
1017 pEntry
->nTime
= ZipOutputStream::getCurrentDosTime();
1020 aCRC32
.update( aType
);
1021 pEntry
->nCrc
= aCRC32
.getValue();
1025 ZipOutputStream::setEntry(pEntry
);
1026 aZipOut
.writeLOC(pEntry
);
1027 aZipOut
.rawWrite(aType
);
1028 aZipOut
.rawCloseEntry();
1030 catch ( const css::io::IOException
& )
1032 css::uno::Any anyEx
= cppu::getCaughtException();
1033 throw WrappedTargetException(
1034 THROW_WHERE
"Error adding mimetype to the ZipOutputStream!",
1035 static_cast < OWeakObject
* > ( this ),
1040 void ZipPackage::WriteManifest( ZipOutputStream
& aZipOut
, const std::vector
< uno::Sequence
< PropertyValue
> >& aManList
)
1042 // Write the manifest
1043 uno::Reference
< XManifestWriter
> xWriter
= ManifestWriter::create( m_xContext
);
1044 ZipEntry
* pEntry
= new ZipEntry
;
1045 rtl::Reference
<ZipPackageBuffer
> pBuffer
= new ZipPackageBuffer
;
1047 pEntry
->sPath
= "META-INF/manifest.xml";
1048 pEntry
->nMethod
= DEFLATED
;
1050 pEntry
->nSize
= pEntry
->nCompressedSize
= -1;
1051 pEntry
->nTime
= ZipOutputStream::getCurrentDosTime();
1053 xWriter
->writeManifestSequence ( pBuffer
, comphelper::containerToSequence(aManList
) );
1055 sal_Int32 nBufferLength
= static_cast < sal_Int32
> ( pBuffer
->getPosition() );
1056 pBuffer
->realloc( nBufferLength
);
1058 // the manifest.xml is never encrypted - so pass an empty reference
1059 ZipOutputStream::setEntry(pEntry
);
1060 aZipOut
.writeLOC(pEntry
);
1061 ZipOutputEntry
aZipEntry(aZipOut
.getStream(), m_xContext
, *pEntry
, nullptr, /*bEncrypt*/false);
1062 aZipEntry
.write(pBuffer
->getSequence());
1063 aZipEntry
.closeEntry();
1064 aZipOut
.rawCloseEntry();
1067 void ZipPackage::WriteContentTypes( ZipOutputStream
& aZipOut
, const std::vector
< uno::Sequence
< PropertyValue
> >& aManList
)
1069 ZipEntry
* pEntry
= new ZipEntry
;
1070 rtl::Reference
<ZipPackageBuffer
> pBuffer
= new ZipPackageBuffer
;
1072 pEntry
->sPath
= "[Content_Types].xml";
1073 pEntry
->nMethod
= DEFLATED
;
1075 pEntry
->nSize
= pEntry
->nCompressedSize
= -1;
1076 pEntry
->nTime
= ZipOutputStream::getCurrentDosTime();
1078 // Add default entries, the count must be updated manually when appending.
1079 // Add at least the standard default entries.
1080 uno::Sequence
< beans::StringPair
> aDefaultsSequence
1082 { "xml", "application/xml" },
1083 { "rels", "application/vnd.openxmlformats-package.relationships+xml" },
1084 { "png", "image/png" },
1085 { "jpeg", "image/jpeg" }
1088 uno::Sequence
< beans::StringPair
> aOverridesSequence(aManList
.size());
1089 auto aOverridesSequenceRange
= asNonConstRange(aOverridesSequence
);
1090 sal_Int32 nOverSeqLength
= 0;
1091 for (const auto& rMan
: aManList
)
1094 OSL_ENSURE( rMan
[PKG_MNFST_MEDIATYPE
].Name
== "MediaType" && rMan
[PKG_MNFST_FULLPATH
].Name
== "FullPath",
1095 "The mediatype sequence format is wrong!" );
1096 rMan
[PKG_MNFST_MEDIATYPE
].Value
>>= aType
;
1097 if ( !aType
.isEmpty() )
1100 // only nonempty type makes sense here
1101 rMan
[PKG_MNFST_FULLPATH
].Value
>>= aPath
;
1102 //FIXME: For now we have no way of differentiating defaults from others.
1103 aOverridesSequenceRange
[nOverSeqLength
].First
= "/" + aPath
;
1104 aOverridesSequenceRange
[nOverSeqLength
].Second
= aType
;
1108 aOverridesSequence
.realloc(nOverSeqLength
);
1110 ::comphelper::OFOPXMLHelper::WriteContentSequence(
1111 pBuffer
, aDefaultsSequence
, aOverridesSequence
, m_xContext
);
1113 sal_Int32 nBufferLength
= static_cast < sal_Int32
> ( pBuffer
->getPosition() );
1114 pBuffer
->realloc( nBufferLength
);
1116 // there is no encryption in this format currently
1117 ZipOutputStream::setEntry(pEntry
);
1118 aZipOut
.writeLOC(pEntry
);
1119 ZipOutputEntry
aZipEntry(aZipOut
.getStream(), m_xContext
, *pEntry
, nullptr, /*bEncrypt*/false);
1120 aZipEntry
.write(pBuffer
->getSequence());
1121 aZipEntry
.closeEntry();
1122 aZipOut
.rawCloseEntry();
1125 void ZipPackage::ConnectTo( const uno::Reference
< io::XInputStream
>& xInStream
)
1127 m_xContentSeek
.set( xInStream
, uno::UNO_QUERY_THROW
);
1128 m_xContentStream
= xInStream
;
1130 // seek back to the beginning of the temp file so we can read segments from it
1131 m_xContentSeek
->seek( 0 );
1133 m_pZipFile
->setInputStream( m_xContentStream
);
1135 m_pZipFile
.emplace(m_aMutexHolder
, m_xContentStream
, m_xContext
, false);
1143 rtlRandomPool m_aRandomPool
;
1145 RandomPool() : m_aRandomPool(rtl_random_createPool ())
1150 return m_aRandomPool
;
1154 // Clean up random pool memory
1155 rtl_random_destroyPool(m_aRandomPool
);
1160 uno::Reference
< io::XInputStream
> ZipPackage::writeTempFile()
1162 // In case the target local file does not exist or empty
1163 // write directly to it otherwise create a temporary file to write to.
1164 // If a temporary file is created it is returned back by the method.
1165 // If the data written directly, xComponentStream will be switched here
1167 bool bUseTemp
= true;
1168 uno::Reference
< io::XInputStream
> xResult
;
1169 uno::Reference
< io::XInputStream
> xTempIn
;
1171 uno::Reference
< io::XOutputStream
> xTempOut
;
1172 uno::Reference
< io::XActiveDataStreamer
> xSink
;
1174 if ( m_eMode
== e_IMode_URL
&& !m_pZipFile
&& isLocalFile() )
1176 xSink
= openOriginalForOutput();
1179 uno::Reference
< io::XStream
> xStr
= xSink
->getStream();
1182 xTempOut
= xStr
->getOutputStream();
1188 else if ( m_eMode
== e_IMode_XStream
&& !m_pZipFile
)
1190 // write directly to an empty stream
1191 xTempOut
= m_xStream
->getOutputStream();
1198 // create temporary file
1199 rtl::Reference
< utl::TempFileFastService
> xTempFile( new utl::TempFileFastService
);
1200 xTempOut
.set( xTempFile
);
1201 xTempIn
.set( xTempFile
);
1204 // Hand it to the ZipOutputStream:
1205 ZipOutputStream
aZipOut( xTempOut
);
1208 if ( m_nFormat
== embed::StorageFormats::PACKAGE
)
1210 // Remove the old manifest.xml file as the
1211 // manifest will be re-generated and the
1212 // META-INF directory implicitly created if does not exist
1213 static constexpr OUStringLiteral
sMeta (u
"META-INF");
1215 if ( m_xRootFolder
->hasByName( sMeta
) )
1217 static const OUStringLiteral
sManifest (u
"manifest.xml");
1219 uno::Reference
< XNameContainer
> xMetaInfFolder
;
1220 Any aAny
= m_xRootFolder
->getByName( sMeta
);
1221 aAny
>>= xMetaInfFolder
;
1222 if ( xMetaInfFolder
.is() && xMetaInfFolder
->hasByName( sManifest
) )
1223 xMetaInfFolder
->removeByName( sManifest
);
1226 // Write a magic file with mimetype
1227 WriteMimetypeMagicFile( aZipOut
);
1229 else if ( m_nFormat
== embed::StorageFormats::OFOPXML
)
1231 // Remove the old [Content_Types].xml file as the
1232 // file will be re-generated
1234 static constexpr OUStringLiteral
aContentTypes(u
"[Content_Types].xml");
1236 if ( m_xRootFolder
->hasByName( aContentTypes
) )
1237 m_xRootFolder
->removeByName( aContentTypes
);
1240 // Create a vector to store data for the manifest.xml file
1241 std::vector
< uno::Sequence
< PropertyValue
> > aManList
;
1243 static constexpr OUStringLiteral
sMediaType(u
"MediaType");
1244 static constexpr OUStringLiteral
sVersion(u
"Version");
1245 static constexpr OUStringLiteral
sFullPath(u
"FullPath");
1246 const bool bIsGpgEncrypt
= m_aGpgProps
.hasElements();
1248 if ( m_nFormat
== embed::StorageFormats::PACKAGE
)
1250 uno::Sequence
< PropertyValue
> aPropSeq(
1251 bIsGpgEncrypt
? PKG_SIZE_NOENCR_MNFST
+1 : PKG_SIZE_NOENCR_MNFST
);
1252 auto pPropSeq
= aPropSeq
.getArray();
1253 pPropSeq
[PKG_MNFST_MEDIATYPE
].Name
= sMediaType
;
1254 pPropSeq
[PKG_MNFST_MEDIATYPE
].Value
<<= m_xRootFolder
->GetMediaType();
1255 pPropSeq
[PKG_MNFST_VERSION
].Name
= sVersion
;
1256 pPropSeq
[PKG_MNFST_VERSION
].Value
<<= m_xRootFolder
->GetVersion();
1257 pPropSeq
[PKG_MNFST_FULLPATH
].Name
= sFullPath
;
1258 pPropSeq
[PKG_MNFST_FULLPATH
].Value
<<= OUString("/");
1262 pPropSeq
[PKG_SIZE_NOENCR_MNFST
].Name
= "KeyInfo";
1263 pPropSeq
[PKG_SIZE_NOENCR_MNFST
].Value
<<= m_aGpgProps
;
1265 aManList
.push_back( aPropSeq
);
1269 // This will be used to generate random salt and initialisation vectors
1270 // for encrypted streams
1271 RandomPool aRandomPool
;
1273 sal_Int32
const nPBKDF2IterationCount
= 100000;
1275 // call saveContents ( it will recursively save sub-directories
1276 m_xRootFolder
->saveContents("", aManList
, aZipOut
, GetEncryptionKey(), bIsGpgEncrypt
? 0 : nPBKDF2IterationCount
, aRandomPool
.get());
1279 if( m_nFormat
== embed::StorageFormats::PACKAGE
)
1281 WriteManifest( aZipOut
, aManList
);
1283 else if( m_nFormat
== embed::StorageFormats::OFOPXML
)
1285 WriteContentTypes( aZipOut
, aManList
);
1293 // Update our References to point to the new temp file
1296 // the case when the original contents were written directly
1299 // in case the stream is based on a file it will implement the following interface
1300 // the call should be used to be sure that the contents are written to the file system
1301 uno::Reference
< io::XAsyncOutputMonitor
> asyncOutputMonitor( xTempOut
, uno::UNO_QUERY
);
1302 if (asyncOutputMonitor
.is() && !m_bDisableFileSync
)
1303 asyncOutputMonitor
->waitForCompletion();
1305 // no need to postpone switching to the new stream since the target was written directly
1306 uno::Reference
< io::XInputStream
> xNewStream
;
1307 if ( m_eMode
== e_IMode_URL
)
1308 xNewStream
= xSink
->getStream()->getInputStream();
1309 else if ( m_eMode
== e_IMode_XStream
&& m_xStream
.is() )
1310 xNewStream
= m_xStream
->getInputStream();
1312 if ( xNewStream
.is() )
1313 ConnectTo( xNewStream
);
1316 catch ( uno::Exception
& )
1320 // no information loss appears, thus no special handling is required
1321 uno::Any
aCaught( ::cppu::getCaughtException() );
1323 // it is allowed to throw WrappedTargetException
1324 WrappedTargetException aException
;
1325 if ( aCaught
>>= aException
)
1328 throw WrappedTargetException(
1329 THROW_WHERE
"Problem writing the original content!",
1330 static_cast < OWeakObject
* > ( this ),
1335 // the document is written directly, although it was empty it is important to notify that the writing has failed
1336 // TODO/LATER: let the package be able to recover in this situation
1337 OUString
aErrTxt(THROW_WHERE
"This package is unusable!");
1338 embed::UseBackupException
aException( aErrTxt
, uno::Reference
< uno::XInterface
>(), OUString() );
1339 throw WrappedTargetException( aErrTxt
,
1340 static_cast < OWeakObject
* > ( this ),
1341 Any ( aException
) );
1348 uno::Reference
< XActiveDataStreamer
> ZipPackage::openOriginalForOutput()
1350 // open and truncate the original file
1351 Content
aOriginalContent(
1352 m_aURL
, uno::Reference
< XCommandEnvironment
>(),
1354 uno::Reference
< XActiveDataStreamer
> xSink
= new ActiveDataStreamer
;
1356 if ( m_eMode
== e_IMode_URL
)
1360 bool bTruncSuccess
= false;
1365 Any aAny
= aOriginalContent
.setPropertyValue("Size", Any( sal_Int64(0) ) );
1366 if( !( aAny
>>= aDetect
) )
1367 bTruncSuccess
= true;
1373 if( !bTruncSuccess
)
1375 // the file is not accessible
1376 // just try to write an empty stream to it
1378 uno::Reference
< XInputStream
> xTempIn
= new DummyInputStream
; //uno::Reference< XInputStream >( xTempOut, UNO_QUERY );
1379 aOriginalContent
.writeStream( xTempIn
, true );
1382 OpenCommandArgument2 aArg
;
1383 aArg
.Mode
= OpenMode::DOCUMENT
;
1384 aArg
.Priority
= 0; // unused
1386 aArg
.Properties
= uno::Sequence
< Property
>( 0 ); // unused
1388 aOriginalContent
.executeCommand("open", Any( aArg
) );
1392 // seems to be nonlocal file
1393 // temporary file mechanics should be used
1400 void SAL_CALL
ZipPackage::commitChanges()
1402 // lock the component for the time of committing
1403 ::osl::MutexGuard
aGuard( m_aMutexHolder
->GetMutex() );
1405 if ( m_eMode
== e_IMode_XInputStream
)
1407 IOException aException
;
1408 throw WrappedTargetException(THROW_WHERE
"This package is read only!",
1409 static_cast < OWeakObject
* > ( this ), Any ( aException
) );
1411 // first the writeTempFile is called, if it returns a stream the stream should be written to the target
1412 // if no stream was returned, the file was written directly, nothing should be done
1413 uno::Reference
< io::XInputStream
> xTempInStream
;
1416 xTempInStream
= writeTempFile();
1418 catch (const ucb::ContentCreationException
&)
1420 css::uno::Any anyEx
= cppu::getCaughtException();
1421 throw WrappedTargetException(THROW_WHERE
"Temporary file should be creatable!",
1422 static_cast < OWeakObject
* > ( this ), anyEx
);
1424 if ( xTempInStream
.is() )
1426 uno::Reference
< io::XSeekable
> xTempSeek( xTempInStream
, uno::UNO_QUERY_THROW
);
1430 xTempSeek
->seek( 0 );
1432 catch( const uno::Exception
& )
1434 css::uno::Any anyEx
= cppu::getCaughtException();
1435 throw WrappedTargetException(THROW_WHERE
"Temporary file should be seekable!",
1436 static_cast < OWeakObject
* > ( this ), anyEx
);
1441 // connect to the temporary stream
1442 ConnectTo( xTempInStream
);
1444 catch( const io::IOException
& )
1446 css::uno::Any anyEx
= cppu::getCaughtException();
1447 throw WrappedTargetException(THROW_WHERE
"Temporary file should be connectable!",
1448 static_cast < OWeakObject
* > ( this ), anyEx
);
1451 if ( m_eMode
== e_IMode_XStream
)
1453 // First truncate our output stream
1454 uno::Reference
< XOutputStream
> xOutputStream
;
1456 // preparation for copy step
1459 xOutputStream
= m_xStream
->getOutputStream();
1461 // Make sure we avoid a situation where the current position is
1462 // not zero, but the underlying file is truncated in the
1464 uno::Reference
<io::XSeekable
> xSeekable(xOutputStream
, uno::UNO_QUERY
);
1468 uno::Reference
< XTruncate
> xTruncate ( xOutputStream
, UNO_QUERY_THROW
);
1470 // after successful truncation the original file contents are already lost
1471 xTruncate
->truncate();
1473 catch( const uno::Exception
& )
1475 css::uno::Any anyEx
= cppu::getCaughtException();
1476 throw WrappedTargetException(THROW_WHERE
"This package is read only!",
1477 static_cast < OWeakObject
* > ( this ), anyEx
);
1482 // then copy the contents of the tempfile to our output stream
1483 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream
, xOutputStream
);
1484 xOutputStream
->flush();
1485 uno::Reference
< io::XAsyncOutputMonitor
> asyncOutputMonitor(
1486 xOutputStream
, uno::UNO_QUERY
);
1487 if ( asyncOutputMonitor
.is() ) {
1488 asyncOutputMonitor
->waitForCompletion();
1491 catch( uno::Exception
& )
1493 // if anything goes wrong in this block the target file becomes corrupted
1494 // so an exception should be thrown as a notification about it
1495 // and the package must disconnect from the stream
1496 DisconnectFromTargetAndThrowException_Impl( xTempInStream
);
1499 else if ( m_eMode
== e_IMode_URL
)
1501 uno::Reference
< XOutputStream
> aOrigFileStream
;
1502 bool bCanBeCorrupted
= false;
1506 // write directly in case of local file
1507 uno::Reference
< css::ucb::XSimpleFileAccess3
> xSimpleAccess(
1508 SimpleFileAccess::create( m_xContext
) );
1509 OSL_ENSURE( xSimpleAccess
.is(), "Can't instantiate SimpleFileAccess service!" );
1510 uno::Reference
< io::XTruncate
> xOrigTruncate
;
1511 if ( xSimpleAccess
.is() )
1515 aOrigFileStream
= xSimpleAccess
->openFileWrite( m_aURL
);
1516 xOrigTruncate
.set( aOrigFileStream
, uno::UNO_QUERY_THROW
);
1517 // after successful truncation the file is already corrupted
1518 xOrigTruncate
->truncate();
1520 catch( uno::Exception
& )
1524 if( xOrigTruncate
.is() )
1528 ::comphelper::OStorageHelper::CopyInputToOutput( xTempInStream
, aOrigFileStream
);
1529 aOrigFileStream
->closeOutput();
1531 catch( uno::Exception
& )
1534 aOrigFileStream
->closeOutput();
1535 } catch ( uno::Exception
& ) {}
1537 aOrigFileStream
.clear();
1538 // the original file can already be corrupted
1539 bCanBeCorrupted
= true;
1544 if( !aOrigFileStream
.is() )
1548 uno::Reference
< XPropertySet
> xPropSet ( xTempInStream
, UNO_QUERY_THROW
);
1550 OUString sTargetFolder
= m_aURL
.copy ( 0, m_aURL
.lastIndexOf ( u
'/' ) );
1552 sTargetFolder
, uno::Reference
< XCommandEnvironment
>(),
1556 Any aAny
= xPropSet
->getPropertyValue ("Uri");
1560 aInfo
.NameClash
= NameClash::OVERWRITE
;
1561 aInfo
.MoveData
= false;
1562 aInfo
.SourceURL
= sTempURL
;
1563 aInfo
.NewTitle
= rtl::Uri::decode ( m_aURL
.copy ( 1 + m_aURL
.lastIndexOf ( u
'/' ) ),
1564 rtl_UriDecodeWithCharset
,
1565 RTL_TEXTENCODING_UTF8
);
1566 // if the file is still not corrupted, it can become after the next step
1567 aContent
.executeCommand ("transfer", Any(aInfo
) );
1569 catch ( const css::uno::Exception
& )
1571 if ( bCanBeCorrupted
)
1572 DisconnectFromTargetAndThrowException_Impl( xTempInStream
);
1574 css::uno::Any anyEx
= cppu::getCaughtException();
1575 throw WrappedTargetException(
1576 THROW_WHERE
"This package may be read only!",
1577 static_cast < OWeakObject
* > ( this ),
1584 // after successful storing it can be set to false
1585 m_bMediaTypeFallbackUsed
= false;
1588 void ZipPackage::DisconnectFromTargetAndThrowException_Impl( const uno::Reference
< io::XInputStream
>& xTempStream
)
1590 m_xStream
.set( xTempStream
, uno::UNO_QUERY
);
1591 if ( m_xStream
.is() )
1592 m_eMode
= e_IMode_XStream
;
1594 m_eMode
= e_IMode_XInputStream
;
1598 uno::Reference
< beans::XPropertySet
> xTempFile( xTempStream
, uno::UNO_QUERY_THROW
);
1599 uno::Any aUrl
= xTempFile
->getPropertyValue("Uri");
1601 xTempFile
->setPropertyValue("RemoveFile",
1602 uno::Any( false ) );
1604 catch ( uno::Exception
& )
1606 OSL_FAIL( "These calls are pretty simple, they should not fail!" );
1609 OUString
aErrTxt(THROW_WHERE
"This package is read only!");
1610 embed::UseBackupException
aException( aErrTxt
, uno::Reference
< uno::XInterface
>(), aTempURL
);
1611 throw WrappedTargetException( aErrTxt
,
1612 static_cast < OWeakObject
* > ( this ),
1613 Any ( aException
) );
1616 uno::Sequence
< sal_Int8
> ZipPackage::GetEncryptionKey()
1618 uno::Sequence
< sal_Int8
> aResult
;
1620 if ( m_aStorageEncryptionKeys
.hasElements() )
1622 OUString aNameToFind
;
1623 if ( m_nStartKeyGenerationID
== xml::crypto::DigestID::SHA256
)
1624 aNameToFind
= PACKAGE_ENCRYPTIONDATA_SHA256UTF8
;
1625 else if ( m_nStartKeyGenerationID
== xml::crypto::DigestID::SHA1
)
1626 aNameToFind
= PACKAGE_ENCRYPTIONDATA_SHA1CORRECT
;
1628 throw uno::RuntimeException(THROW_WHERE
"No expected key is provided!" );
1630 for ( const auto& rKey
: std::as_const(m_aStorageEncryptionKeys
) )
1631 if ( rKey
.Name
== aNameToFind
)
1632 rKey
.Value
>>= aResult
;
1635 aResult
= m_aEncryptionKey
;
1640 sal_Bool SAL_CALL
ZipPackage::hasPendingChanges()
1644 Sequence
< ElementChange
> SAL_CALL
ZipPackage::getPendingChanges()
1646 return uno::Sequence
< ElementChange
> ();
1650 OUString
ZipPackage::getImplementationName()
1652 return "com.sun.star.packages.comp.ZipPackage";
1655 Sequence
< OUString
> ZipPackage::getSupportedServiceNames()
1657 return { "com.sun.star.packages.Package" };
1660 sal_Bool SAL_CALL
ZipPackage::supportsService( OUString
const & rServiceName
)
1662 return cppu::supportsService(this, rServiceName
);
1665 uno::Reference
< XPropertySetInfo
> SAL_CALL
ZipPackage::getPropertySetInfo()
1667 return uno::Reference
< XPropertySetInfo
> ();
1670 void SAL_CALL
ZipPackage::setPropertyValue( const OUString
& aPropertyName
, const Any
& aValue
)
1672 if ( m_nFormat
!= embed::StorageFormats::PACKAGE
)
1673 throw UnknownPropertyException(aPropertyName
);
1675 if (aPropertyName
== HAS_ENCRYPTED_ENTRIES_PROPERTY
1676 ||aPropertyName
== HAS_NONENCRYPTED_ENTRIES_PROPERTY
1677 ||aPropertyName
== IS_INCONSISTENT_PROPERTY
1678 ||aPropertyName
== MEDIATYPE_FALLBACK_USED_PROPERTY
)
1679 throw PropertyVetoException(THROW_WHERE
);
1680 else if ( aPropertyName
== ENCRYPTION_KEY_PROPERTY
)
1682 if ( !( aValue
>>= m_aEncryptionKey
) )
1683 throw IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 2 );
1685 m_aStorageEncryptionKeys
.realloc( 0 );
1687 else if ( aPropertyName
== STORAGE_ENCRYPTION_KEYS_PROPERTY
)
1689 // this property is only necessary to support raw passwords in storage API;
1690 // because of this support the storage has to operate with more than one key dependent on storage generation algorithm;
1691 // when this support is removed, the storage will get only one key from outside
1692 if ( !( aValue
>>= m_aStorageEncryptionKeys
) )
1693 throw IllegalArgumentException(THROW_WHERE
, uno::Reference
< uno::XInterface
>(), 2 );
1695 m_aEncryptionKey
.realloc( 0 );
1697 else if ( aPropertyName
== ENCRYPTION_ALGORITHMS_PROPERTY
)
1699 uno::Sequence
< beans::NamedValue
> aAlgorithms
;
1700 if ( m_pZipFile
|| !( aValue
>>= aAlgorithms
) || !aAlgorithms
.hasElements() )
1702 // the algorithms can not be changed if the file has a persistence based on the algorithms ( m_pZipFile )
1703 throw IllegalArgumentException(THROW_WHERE
"unexpected algorithms list is provided.", uno::Reference
< uno::XInterface
>(), 2 );
1706 for ( const auto& rAlgorithm
: std::as_const(aAlgorithms
) )
1708 if ( rAlgorithm
.Name
== "StartKeyGenerationAlgorithm" )
1711 if ( !( rAlgorithm
.Value
>>= nID
)
1712 || ( nID
!= xml::crypto::DigestID::SHA256
&& nID
!= xml::crypto::DigestID::SHA1
) )
1713 throw IllegalArgumentException(THROW_WHERE
"Unexpected start key generation algorithm is provided!", uno::Reference
< uno::XInterface
>(), 2 );
1715 m_nStartKeyGenerationID
= nID
;
1717 else if ( rAlgorithm
.Name
== "EncryptionAlgorithm" )
1720 if ( !( rAlgorithm
.Value
>>= nID
)
1721 || ( nID
!= xml::crypto::CipherID::AES_CBC_W3C_PADDING
&& nID
!= xml::crypto::CipherID::BLOWFISH_CFB_8
) )
1722 throw IllegalArgumentException(THROW_WHERE
"Unexpected start key generation algorithm is provided!", uno::Reference
< uno::XInterface
>(), 2 );
1724 m_nCommonEncryptionID
= nID
;
1726 else if ( rAlgorithm
.Name
== "ChecksumAlgorithm" )
1729 if ( !( rAlgorithm
.Value
>>= nID
)
1730 || ( nID
!= xml::crypto::DigestID::SHA1_1K
&& nID
!= xml::crypto::DigestID::SHA256_1K
) )
1731 throw IllegalArgumentException(THROW_WHERE
"Unexpected start key generation algorithm is provided!", uno::Reference
< uno::XInterface
>(), 2 );
1733 m_nChecksumDigestID
= nID
;
1737 OSL_ENSURE( false, "Unexpected encryption algorithm is provided!" );
1738 throw IllegalArgumentException(THROW_WHERE
"unexpected algorithms list is provided.", uno::Reference
< uno::XInterface
>(), 2 );
1742 else if ( aPropertyName
== ENCRYPTION_GPG_PROPERTIES
)
1744 uno::Sequence
< uno::Sequence
< beans::NamedValue
> > aGpgProps
;
1745 if ( !( aValue
>>= aGpgProps
) || !aGpgProps
.hasElements() )
1747 throw IllegalArgumentException(THROW_WHERE
"unexpected Gpg properties are provided.", uno::Reference
< uno::XInterface
>(), 2 );
1750 m_aGpgProps
= aGpgProps
;
1752 // override algorithm defaults (which are some legacy ODF
1753 // defaults) with reasonable values
1754 m_nStartKeyGenerationID
= 0; // this is unused for PGP
1755 m_nCommonEncryptionID
= xml::crypto::CipherID::AES_CBC_W3C_PADDING
;
1756 m_nChecksumDigestID
= xml::crypto::DigestID::SHA512_1K
;
1759 throw UnknownPropertyException(aPropertyName
);
1762 Any SAL_CALL
ZipPackage::getPropertyValue( const OUString
& PropertyName
)
1764 // TODO/LATER: Activate the check when zip-ucp is ready
1765 // if ( m_nFormat != embed::StorageFormats::PACKAGE )
1766 // throw UnknownPropertyException(THROW_WHERE );
1768 if ( PropertyName
== ENCRYPTION_KEY_PROPERTY
)
1770 return Any(m_aEncryptionKey
);
1772 else if ( PropertyName
== ENCRYPTION_ALGORITHMS_PROPERTY
)
1774 ::comphelper::SequenceAsHashMap aAlgorithms
;
1775 aAlgorithms
["StartKeyGenerationAlgorithm"] <<= m_nStartKeyGenerationID
;
1776 aAlgorithms
["EncryptionAlgorithm"] <<= m_nCommonEncryptionID
;
1777 aAlgorithms
["ChecksumAlgorithm"] <<= m_nChecksumDigestID
;
1778 return Any(aAlgorithms
.getAsConstNamedValueList());
1780 if ( PropertyName
== STORAGE_ENCRYPTION_KEYS_PROPERTY
)
1782 return Any(m_aStorageEncryptionKeys
);
1784 else if ( PropertyName
== HAS_ENCRYPTED_ENTRIES_PROPERTY
)
1786 return Any(m_bHasEncryptedEntries
);
1788 else if ( PropertyName
== ENCRYPTION_GPG_PROPERTIES
)
1790 return Any(m_aGpgProps
);
1792 else if ( PropertyName
== HAS_NONENCRYPTED_ENTRIES_PROPERTY
)
1794 return Any(m_bHasNonEncryptedEntries
);
1796 else if ( PropertyName
== IS_INCONSISTENT_PROPERTY
)
1798 return Any(m_bInconsistent
);
1800 else if ( PropertyName
== MEDIATYPE_FALLBACK_USED_PROPERTY
)
1802 return Any(m_bMediaTypeFallbackUsed
);
1804 throw UnknownPropertyException(PropertyName
);
1806 void SAL_CALL
ZipPackage::addPropertyChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
< XPropertyChangeListener
>& /*xListener*/ )
1809 void SAL_CALL
ZipPackage::removePropertyChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
< XPropertyChangeListener
>& /*aListener*/ )
1812 void SAL_CALL
ZipPackage::addVetoableChangeListener( const OUString
& /*PropertyName*/, const uno::Reference
< XVetoableChangeListener
>& /*aListener*/ )
1815 void SAL_CALL
ZipPackage::removeVetoableChangeListener( const OUString
& /*PropertyName*/, const uno::Reference
< XVetoableChangeListener
>& /*aListener*/ )
1819 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1820 package_ZipPackage_get_implementation(
1821 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
1823 return cppu::acquire(new ZipPackage(context
));
1826 extern "C" bool TestImportZip(SvStream
& rStream
)
1828 // explicitly tests the "RepairPackage" recovery mode
1829 rtl::Reference
<ZipPackage
> xPackage(new ZipPackage(comphelper::getProcessComponentContext()));
1830 css::uno::Reference
<css::io::XInputStream
> xStream(new utl::OInputStreamWrapper(rStream
));
1831 css::uno::Sequence
<Any
> aArgs
{ Any(xStream
), Any(NamedValue("RepairPackage", Any(true))) };
1832 xPackage
->initialize(aArgs
);
1836 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */