Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / package / source / zippackage / ZipPackage.cxx
bloba04580dcaeae4bd81ab5476ea5b72b4fdc37beb8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ZipPackage.hxx>
21 #include "ZipPackageSink.hxx"
22 #include <ZipEnumeration.hxx>
23 #include <ZipPackageStream.hxx>
24 #include <ZipPackageFolder.hxx>
25 #include <ZipOutputEntry.hxx>
26 #include <ZipOutputStream.hxx>
27 #include <ZipPackageBuffer.hxx>
28 #include <ZipFile.hxx>
29 #include <PackageConstants.hxx>
30 #include <com/sun/star/beans/PropertyValue.hpp>
31 #include <com/sun/star/packages/zip/ZipConstants.hpp>
32 #include <com/sun/star/packages/manifest/ManifestReader.hpp>
33 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
34 #include <com/sun/star/io/TempFile.hpp>
35 #include <com/sun/star/io/XStream.hpp>
36 #include <com/sun/star/io/XInputStream.hpp>
37 #include <com/sun/star/io/XOutputStream.hpp>
38 #include <com/sun/star/io/XTruncate.hpp>
39 #include <com/sun/star/io/XSeekable.hpp>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
42 #include <com/sun/star/container/XNameContainer.hpp>
43 #include <com/sun/star/ucb/IOErrorCode.hpp>
44 #include <comphelper/fileurl.hxx>
45 #include <ucbhelper/content.hxx>
46 #include <cppuhelper/factory.hxx>
47 #include <cppuhelper/exc_hlp.hxx>
48 #include <com/sun/star/ucb/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>
69 #include <cstring>
70 #include <memory>
71 #include <vector>
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>
83 using namespace std;
84 using namespace osl;
85 using namespace cppu;
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
103 #else
104 #define THROW_WHERE ""
105 #endif
107 class ActiveDataStreamer : public ::cppu::WeakImplHelper< XActiveDataStreamer >
109 uno::Reference< XStream > mStream;
110 public:
112 virtual uno::Reference< XStream > SAL_CALL getStream() override
113 { return mStream; }
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
122 { return 0; }
124 virtual sal_Int32 SAL_CALL readSomeBytes( uno::Sequence< sal_Int8 >&, sal_Int32 ) override
125 { return 0; }
127 virtual void SAL_CALL skipBytes( sal_Int32 ) override
130 virtual sal_Int32 SAL_CALL available() override
131 { return 0; }
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");
174 try {
175 uno::Reference< XUnoTunnel > xTunnel;
176 Any aAny = m_xRootFolder->getByName( sMeta );
177 aAny >>= xTunnel;
178 uno::Reference< XNameContainer > xMetaInfFolder( xTunnel, UNO_QUERY );
179 if ( xMetaInfFolder.is() && xMetaInfFolder->hasByName( sManifest ) )
181 aAny = xMetaInfFolder->getByName( sManifest );
182 aAny >>= xTunnel;
183 uno::Reference < XActiveDataSink > xSink ( xTunnel, UNO_QUERY );
184 if ( xSink.is() )
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;
248 aAny >>= xUnoTunnel;
249 sal_Int64 nTest=0;
250 if ( (nTest = xUnoTunnel->getSomething( ZipPackageFolder::static_getImplementationId() )) != 0 )
252 pFolder = reinterpret_cast < ZipPackageFolder* > ( nTest );
253 pFolder->SetMediaType ( sMediaType );
254 pFolder->SetVersion ( sVersion );
256 else
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;
265 sal_Int64 nSize = 0;
266 sal_Int32 nDigestAlg = 0, nEncryptionAlg = 0;
268 pStream->SetToBeEncrypted ( true );
270 *pVector >>= aSequence;
271 pStream->setInitialisationVector ( aSequence );
273 *pSize >>= nSize;
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;
309 sal_Int64 nSize = 0;
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 );
321 *pCount >>= nCount;
322 pStream->setIterationCount ( nCount );
324 *pSize >>= nSize;
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 );
340 if ( pStartKeyAlg )
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;
355 else
356 m_bHasNonEncryptedEntries = true;
361 bManifestParsed = true;
364 // now hide the manifest.xml file from user
365 xMetaInfFolder->removeByName( sManifest );
368 catch( Exception& )
370 if ( !m_bForceRecovery )
371 throw;
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();
398 if ( nRead )
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(
418 THROW_WHERE
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");
457 try {
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 );
464 aAny >>= xTunnel;
465 uno::Reference < io::XActiveDataSink > xSink( xTunnel, UNO_QUERY );
466 if ( xSink.is() )
468 uno::Reference< io::XInputStream > xInStream = xSink->getInputStream();
469 if ( xInStream.is() )
471 sal_Int32 nInd = 0;
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++ )
486 OUString aPath;
487 if ( aContentTypeInfo[1][nInd].First.toChar() == '/' )
488 aPath = aContentTypeInfo[1][nInd].First.copy( 1 );
489 else
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() );
498 if ( nTest != 0 )
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 )
514 throw;
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())
528 nOldIndex = 0;
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() )
551 sal_Int32 nIndex;
552 while ( ( nIndex = rName.indexOf( '/', nOldIndex ) ) != -1 )
554 sTemp = rName.copy ( nOldIndex, nIndex - nOldIndex );
555 if ( nIndex == nOldIndex )
556 break;
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;
564 else
566 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
567 if (!rInfo.bFolder)
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 )
578 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 )
593 parseManifest();
594 else if ( m_nFormat == embed::StorageFormats::OFOPXML )
595 parseContentType();
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++ )
608 OUString aParamUrl;
609 if ( aArguments[ind] >>= aParamUrl )
611 m_eMode = e_IMode_URL;
614 sal_Int32 nParam = aParamUrl.indexOf( '?' );
615 if ( nParam >= 0 )
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;
627 break;
629 else if ( aCommand == "purezip" )
631 m_nFormat = embed::StorageFormats::ZIP;
632 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
633 break;
635 else if ( aCommand == "ofopxml" )
637 m_nFormat = embed::StorageFormats::OFOPXML;
638 m_xRootFolder->setPackageFormat_Impl( m_nFormat );
639 break;
642 while ( nIndex >= 0 );
644 else
645 m_aURL = aParamUrl;
647 Content aContent(
648 m_aURL, uno::Reference< XCommandEnvironment >(),
649 m_xContext );
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();
661 else
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;
693 if ( !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;
710 else
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;
722 else
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" )
738 else
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;
756 else
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;
766 if ( bHaveZipFile )
768 bool bBadZipFile = false;
769 OUString message;
772 m_pZipFile = o3tl::make_unique<ZipFile>(m_aMutexHolder, m_xContentStream, m_xContext, true, m_bForceRecovery);
773 getZipFileContents();
775 catch ( IOException & e )
777 bBadZipFile = true;
778 message = "IOException: " + e.Message;
780 catch ( ZipException & e )
782 bBadZipFile = true;
783 message = "ZipException: " + e.Message;
785 catch ( Exception & )
787 m_pZipFile.reset();
788 throw;
791 if ( bBadZipFile )
793 // clean up the memory, and tell the UCB about the error
794 m_pZipFile.reset();
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();
812 if (aName == "/")
813 // root directory.
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;
830 if ( bFolder )
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));
839 else
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.
857 nOldIndex = 0;
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 )
867 break;
868 if ( !pCurrent->hasByName( sTemp ) )
869 throw NoSuchElementException(THROW_WHERE );
871 pPrevious = pCurrent;
872 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
873 if (!rInfo.bFolder)
874 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
875 pCurrent = rInfo.pFolder;
876 nOldIndex = nIndex+1;
879 if ( bFolder )
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();
906 if (aName == "/")
907 // root directory
908 return true;
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() )
920 if ( bFolder )
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() )
925 return true;
926 else
927 m_aRecent.erase ( aIter );
929 else
931 sTemp = aName.copy ( nStreamIndex + 1 );
932 if ( ( *aIter ).second->hasByName( sTemp ) )
933 return true;
934 else
935 m_aRecent.erase( aIter );
939 else
941 if ( m_xRootFolder->hasByName ( aName ) )
942 return true;
944 ZipPackageFolder * pCurrent = m_xRootFolder.get();
945 ZipPackageFolder * pPrevious = nullptr;
946 nOldIndex = 0;
947 while ( ( nIndex = aName.indexOf( '/', nOldIndex )) != -1 )
949 sTemp = aName.copy ( nOldIndex, nIndex - nOldIndex );
950 if ( nIndex == nOldIndex )
951 break;
952 if ( pCurrent->hasByName( sTemp ) )
954 pPrevious = pCurrent;
955 ZipContentInfo& rInfo = pCurrent->doGetByName(sTemp);
956 if (!rInfo.bFolder)
957 throw css::packages::zip::ZipIOException("Bad Zip File, stream as folder");
958 pCurrent = rInfo.pFolder;
960 else
961 return false;
962 nOldIndex = nIndex+1;
964 if ( bFolder )
966 m_aRecent[sDirName] = pPrevious;
967 return true;
969 else
971 sTemp = aName.copy( nOldIndex );
973 if ( pCurrent->hasByName( sTemp ) )
975 m_aRecent[sDirName] = pCurrent;
976 return true;
980 catch (const uno::RuntimeException &)
982 throw;
984 catch (const uno::Exception&)
986 uno::Any e(::cppu::getCaughtException());
987 throw lang::WrappedTargetRuntimeException("ZipPackage::hasByHierarchicalName", nullptr, e);
989 return false;
992 uno::Reference< XInterface > SAL_CALL ZipPackage::createInstance()
994 uno::Reference < XInterface > xRef = *( new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert ) );
995 return xRef;
998 uno::Reference< XInterface > SAL_CALL ZipPackage::createInstanceWithArguments( const uno::Sequence< Any >& aArguments )
1000 bool bArg = false;
1001 uno::Reference < XInterface > xRef;
1002 if ( aArguments.getLength() )
1003 aArguments[0] >>= bArg;
1004 if ( bArg )
1005 xRef = *new ZipPackageFolder( m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1006 else
1007 xRef = *new ZipPackageStream( *this, m_xContext, m_nFormat, m_bAllowRemoveOnInsert );
1009 return xRef;
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()),
1022 nBufferLength );
1024 pEntry->sPath = sMime;
1025 pEntry->nMethod = STORED;
1026 pEntry->nSize = pEntry->nCompressedSize = nBufferLength;
1027 pEntry->nTime = ZipOutputStream::getCurrentDosTime();
1029 CRC32 aCRC32;
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 ),
1045 makeAny( r ) );
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;
1059 pEntry->nCrc = -1;
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;
1085 pEntry->nCrc = -1;
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();
1106 aIter != aEnd;
1107 ++aIter)
1109 OUString aPath;
1110 OUString aType;
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;
1121 ++nOverSeqLength;
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 );
1148 if ( m_pZipFile )
1149 m_pZipFile->setInputStream( m_xContentStream );
1150 else
1151 m_pZipFile = o3tl::make_unique<ZipFile>(m_aMutexHolder, m_xContentStream, m_xContext, false);
1154 namespace
1156 class RandomPool
1158 private:
1159 rtlRandomPool m_aRandomPool;
1160 public:
1161 RandomPool()
1163 m_aRandomPool = rtl_random_createPool ();
1165 rtlRandomPool get()
1167 return m_aRandomPool;
1169 ~RandomPool()
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();
1194 if( xSink.is() )
1196 uno::Reference< io::XStream > xStr = xSink->getStream();
1197 if( xStr.is() )
1199 xTempOut = xStr->getOutputStream();
1200 if( xTempOut.is() )
1201 bUseTemp = false;
1205 else if ( m_eMode == e_IMode_XStream && !m_pZipFile )
1207 // write directly to an empty stream
1208 xTempOut = m_xStream->getOutputStream();
1209 if( xTempOut.is() )
1210 bUseTemp = false;
1213 if( bUseTemp )
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 );
1238 aAny >>= xTunnel;
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("/");
1277 if( bIsGpgEncrypt )
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 );
1305 aZipOut.finish();
1307 if( bUseTemp )
1308 xResult = xTempIn;
1310 // Update our References to point to the new temp file
1311 if( !bUseTemp )
1313 // the case when the original contents were written directly
1314 xTempOut->flush();
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& )
1335 if( bUseTemp )
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 )
1343 throw aException;
1345 throw WrappedTargetException(
1346 THROW_WHERE "Problem writing the original content!",
1347 static_cast < OWeakObject * > ( this ),
1348 aCaught );
1350 else
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 ) );
1362 return xResult;
1365 uno::Reference< XActiveDataStreamer > ZipPackage::openOriginalForOutput()
1367 // open and truncate the original file
1368 Content aOriginalContent(
1369 m_aURL, uno::Reference< XCommandEnvironment >(),
1370 m_xContext );
1371 uno::Reference< XActiveDataStreamer > xSink = new ActiveDataStreamer;
1373 if ( m_eMode == e_IMode_URL )
1377 bool bTruncSuccess = false;
1381 Exception aDetect;
1382 Any aAny = aOriginalContent.setPropertyValue("Size", makeAny( sal_Int64(0) ) );
1383 if( !( aAny >>= aDetect ) )
1384 bTruncSuccess = true;
1386 catch( Exception& )
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
1402 aArg.Sink = xSink;
1403 aArg.Properties = uno::Sequence< Property >( 0 ); // unused
1405 aOriginalContent.executeCommand("open", makeAny( aArg ) );
1407 catch( Exception& )
1409 // seems to be nonlocal file
1410 // temporary file mechanics should be used
1414 return xSink;
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
1477 // meantime.
1478 uno::Reference<io::XSeekable> xSeekable(xOutputStream, uno::UNO_QUERY);
1479 if (xSeekable.is())
1480 xSeekable->seek(0);
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;
1517 if( isLocalFile() )
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& )
1546 try {
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'/' ) );
1564 Content aContent(
1565 sTargetFolder, uno::Reference< XCommandEnvironment >(),
1566 m_xContext );
1568 OUString sTempURL;
1569 Any aAny = xPropSet->getPropertyValue ("Uri");
1570 aAny >>= sTempURL;
1572 TransferInfo aInfo;
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 ),
1590 makeAny ( r ) );
1596 // after successful storing it can be set to false
1597 m_bMediaTypeFallbackUsed = false;
1600 void ZipPackage::DisconnectFromTargetAndThrowException_Impl( const uno::Reference< io::XInputStream >& xTempStream )
1602 m_xStream.set( xTempStream, uno::UNO_QUERY );
1603 if ( m_xStream.is() )
1604 m_eMode = e_IMode_XStream;
1605 else
1606 m_eMode = e_IMode_XInputStream;
1608 OUString aTempURL;
1609 try {
1610 uno::Reference< beans::XPropertySet > xTempFile( xTempStream, uno::UNO_QUERY_THROW );
1611 uno::Any aUrl = xTempFile->getPropertyValue("Uri");
1612 aUrl >>= aTempURL;
1613 xTempFile->setPropertyValue("RemoveFile",
1614 uno::makeAny( false ) );
1616 catch ( uno::Exception& )
1618 OSL_FAIL( "These calls are pretty simple, they should not fail!" );
1621 OUString aErrTxt(THROW_WHERE "This package is read only!");
1622 embed::UseBackupException aException( aErrTxt, uno::Reference< uno::XInterface >(), aTempURL );
1623 throw WrappedTargetException( aErrTxt,
1624 static_cast < OWeakObject * > ( this ),
1625 makeAny ( aException ) );
1628 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;
1639 else
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!" );
1651 else
1652 aResult = m_aEncryptionKey;
1654 return aResult;
1657 sal_Bool SAL_CALL ZipPackage::hasPendingChanges()
1659 return false;
1661 Sequence< ElementChange > SAL_CALL ZipPackage::getPendingChanges()
1663 return uno::Sequence < ElementChange > ();
1667 * Function to create a new component instance; is needed by factory helper implementation.
1668 * @param xMgr service manager to if the components needs other component instances
1670 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" };
1684 return aNames;
1687 OUString ZipPackage::getImplementationName()
1689 return static_getImplementationName();
1692 Sequence< OUString > ZipPackage::getSupportedServiceNames()
1694 return static_getSupportedServiceNames();
1697 sal_Bool SAL_CALL ZipPackage::supportsService( OUString const & rServiceName )
1699 return cppu::supportsService(this, rServiceName);
1702 uno::Reference < XSingleServiceFactory > ZipPackage::createServiceFactory( uno::Reference < XMultiServiceFactory > const & rServiceFactory )
1704 return cppu::createSingleFactory ( rServiceFactory,
1705 static_getImplementationName(),
1706 ZipPackage_createInstance,
1707 static_getSupportedServiceNames() );
1710 Sequence< sal_Int8 > ZipPackage::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 );
1721 return 0;
1724 uno::Reference< XPropertySetInfo > SAL_CALL ZipPackage::getPropertySetInfo()
1726 return uno::Reference < XPropertySetInfo > ();
1729 void SAL_CALL ZipPackage::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
1731 if ( m_nFormat != embed::StorageFormats::PACKAGE )
1732 throw UnknownPropertyException(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 )
1762 bHasSHA256 = true;
1763 if ( aKeys[nInd].Name == PACKAGE_ENCRYPTIONDATA_SHA1UTF8 )
1764 bHasSHA1 = true;
1767 if ( !bHasSHA256 && !bHasSHA1 )
1768 throw IllegalArgumentException(THROW_WHERE "Expected keys are not provided!", uno::Reference< uno::XInterface >(), 2 );
1771 m_aStorageEncryptionKeys = aKeys;
1772 m_aEncryptionKey.realloc( 0 );
1774 else if ( aPropertyName == ENCRYPTION_ALGORITHMS_PROPERTY )
1776 uno::Sequence< beans::NamedValue > aAlgorithms;
1777 if ( m_pZipFile || !( aValue >>= aAlgorithms ) || aAlgorithms.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" )
1787 sal_Int32 nID = 0;
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" )
1796 sal_Int32 nID = 0;
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" )
1805 sal_Int32 nID = 0;
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;
1812 else
1814 OSL_ENSURE( false, "Unexpected encryption algorithm is provided!" );
1815 throw IllegalArgumentException(THROW_WHERE "unexpected algorithms list is provided.", uno::Reference< uno::XInterface >(), 2 );
1819 else if ( aPropertyName == ENCRYPTION_GPG_PROPERTIES )
1821 uno::Sequence< uno::Sequence< beans::NamedValue > > aGpgProps;
1822 if ( !( aValue >>= aGpgProps ) || aGpgProps.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;
1835 else
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: */