Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <string.h>
21 :
22 : #include <ZipPackageFolder.hxx>
23 : #include <ZipFile.hxx>
24 : #include <ZipOutputStream.hxx>
25 : #include <ZipPackageStream.hxx>
26 : #include <PackageConstants.hxx>
27 : #include <ZipPackageFolderEnumeration.hxx>
28 : #include <com/sun/star/packages/zip/ZipConstants.hpp>
29 : #include <com/sun/star/embed/StorageFormats.hpp>
30 : #include <cppuhelper/supportsservice.hxx>
31 : #include <cppuhelper/typeprovider.hxx>
32 : #include <osl/diagnose.h>
33 : #include <osl/time.h>
34 : #include <rtl/digest.h>
35 : #include <ContentInfo.hxx>
36 : #include <com/sun/star/beans/PropertyValue.hpp>
37 : #include <com/sun/star/io/XSeekable.hpp>
38 : #include <EncryptedDataHeader.hxx>
39 : #include <rtl/random.h>
40 : #include <rtl/instance.hxx>
41 : #include <boost/scoped_ptr.hpp>
42 :
43 : using namespace com::sun::star;
44 : using namespace com::sun::star::packages::zip::ZipConstants;
45 : using namespace com::sun::star::packages::zip;
46 : using namespace com::sun::star::packages;
47 : using namespace com::sun::star::container;
48 : using namespace com::sun::star::beans;
49 : using namespace com::sun::star::lang;
50 : using namespace com::sun::star::io;
51 : using namespace cppu;
52 : using namespace std;
53 : using namespace ::com::sun::star;
54 :
55 : #if OSL_DEBUG_LEVEL > 0
56 : #define THROW_WHERE SAL_WHERE
57 : #else
58 : #define THROW_WHERE ""
59 : #endif
60 :
61 : namespace { struct lcl_CachedImplId : public rtl::Static< cppu::OImplementationId, lcl_CachedImplId > {}; }
62 :
63 0 : ZipPackageFolder::ZipPackageFolder ( sal_Int32 nFormat,
64 : sal_Bool bAllowRemoveOnInsert )
65 0 : : m_nFormat( nFormat )
66 : {
67 0 : this->mbAllowRemoveOnInsert = bAllowRemoveOnInsert;
68 :
69 0 : SetFolder ( true );
70 0 : aEntry.nVersion = -1;
71 0 : aEntry.nFlag = 0;
72 0 : aEntry.nMethod = STORED;
73 0 : aEntry.nTime = -1;
74 0 : aEntry.nCrc = 0;
75 0 : aEntry.nCompressedSize = 0;
76 0 : aEntry.nSize = 0;
77 0 : aEntry.nOffset = -1;
78 0 : }
79 :
80 0 : ZipPackageFolder::~ZipPackageFolder()
81 : {
82 0 : }
83 :
84 0 : sal_Bool ZipPackageFolder::LookForUnexpectedODF12Streams( const OUString& aPath )
85 : {
86 0 : sal_Bool bHasUnexpected = sal_False;
87 :
88 0 : for ( ContentHash::const_iterator aCI = maContents.begin(), aEnd = maContents.end();
89 0 : !bHasUnexpected && aCI != aEnd;
90 : ++aCI)
91 : {
92 0 : const OUString &rShortName = (*aCI).first;
93 0 : const ContentInfo &rInfo = *(*aCI).second;
94 :
95 0 : if ( rInfo.bFolder )
96 : {
97 0 : if ( aPath == "META-INF/" )
98 : {
99 : // META-INF is not allowed to contain subfolders
100 0 : bHasUnexpected = sal_True;
101 : }
102 : else
103 : {
104 0 : OUString sOwnPath = aPath + rShortName + "/";
105 0 : bHasUnexpected = rInfo.pFolder->LookForUnexpectedODF12Streams( sOwnPath );
106 : }
107 : }
108 : else
109 : {
110 0 : if ( aPath == "META-INF/" )
111 : {
112 0 : if ( rShortName != "manifest.xml"
113 0 : && rShortName.indexOf( "signatures" ) == -1 )
114 : {
115 : // a stream from META-INF with unexpected name
116 0 : bHasUnexpected = sal_True;
117 : }
118 :
119 : // streams from META-INF with expected names are allowed not to be registered in manifest.xml
120 : }
121 0 : else if ( !rInfo.pStream->IsFromManifest() )
122 : {
123 : // the stream is not in META-INF and is not registered in manifest.xml,
124 : // check whether it is an internal part of the package format
125 0 : if ( !aPath.isEmpty() || rShortName != "mimetype" )
126 : {
127 : // if it is not "mimetype" from the root it is not a part of the package
128 0 : bHasUnexpected = sal_True;
129 : }
130 : }
131 : }
132 : }
133 :
134 0 : return bHasUnexpected;
135 : }
136 :
137 0 : void ZipPackageFolder::setChildStreamsTypeByExtension( const beans::StringPair& aPair )
138 : {
139 0 : OUString aExt;
140 0 : if ( aPair.First.toChar() == (sal_Unicode)'.' )
141 0 : aExt = aPair.First;
142 : else
143 0 : aExt = "." + aPair.First;
144 :
145 0 : for ( ContentHash::const_iterator aCI = maContents.begin(), aEnd = maContents.end();
146 : aCI != aEnd;
147 : ++aCI)
148 : {
149 0 : const OUString &rShortName = (*aCI).first;
150 0 : const ContentInfo &rInfo = *(*aCI).second;
151 :
152 0 : if ( rInfo.bFolder )
153 0 : rInfo.pFolder->setChildStreamsTypeByExtension( aPair );
154 : else
155 : {
156 0 : sal_Int32 nPathLength = rShortName.getLength();
157 0 : sal_Int32 nExtLength = aExt.getLength();
158 0 : if ( nPathLength >= nExtLength && rShortName.match( aExt, nPathLength - nExtLength ) )
159 0 : rInfo.pStream->SetMediaType( aPair.Second );
160 : }
161 0 : }
162 0 : }
163 :
164 0 : void ZipPackageFolder::copyZipEntry( ZipEntry &rDest, const ZipEntry &rSource)
165 : {
166 0 : rDest.nVersion = rSource.nVersion;
167 0 : rDest.nFlag = rSource.nFlag;
168 0 : rDest.nMethod = rSource.nMethod;
169 0 : rDest.nTime = rSource.nTime;
170 0 : rDest.nCrc = rSource.nCrc;
171 0 : rDest.nCompressedSize = rSource.nCompressedSize;
172 0 : rDest.nSize = rSource.nSize;
173 0 : rDest.nOffset = rSource.nOffset;
174 0 : rDest.sPath = rSource.sPath;
175 0 : rDest.nPathLen = rSource.nPathLen;
176 0 : rDest.nExtraLen = rSource.nExtraLen;
177 0 : }
178 :
179 0 : ::com::sun::star::uno::Sequence < sal_Int8 > ZipPackageFolder::static_getImplementationId()
180 : {
181 0 : return lcl_CachedImplId::get().getImplementationId();
182 : }
183 :
184 : // XNameContainer
185 0 : void SAL_CALL ZipPackageFolder::insertByName( const OUString& aName, const uno::Any& aElement )
186 : throw(IllegalArgumentException, ElementExistException, WrappedTargetException, uno::RuntimeException, std::exception)
187 : {
188 0 : if (hasByName(aName))
189 0 : throw ElementExistException(THROW_WHERE, uno::Reference< uno::XInterface >() );
190 : else
191 : {
192 0 : uno::Reference < XUnoTunnel > xRef;
193 0 : aElement >>= xRef;
194 0 : if ( ( aElement >>= xRef ) )
195 : {
196 : sal_Int64 nTest;
197 : ZipPackageEntry *pEntry;
198 0 : if ( ( nTest = xRef->getSomething ( ZipPackageFolder::static_getImplementationId() ) ) != 0 )
199 : {
200 0 : ZipPackageFolder *pFolder = reinterpret_cast < ZipPackageFolder * > ( nTest );
201 0 : pEntry = static_cast < ZipPackageEntry * > ( pFolder );
202 : }
203 0 : else if ( ( nTest = xRef->getSomething ( ZipPackageStream::static_getImplementationId() ) ) != 0 )
204 : {
205 0 : ZipPackageStream *pStream = reinterpret_cast < ZipPackageStream * > ( nTest );
206 0 : pEntry = static_cast < ZipPackageEntry * > ( pStream );
207 : }
208 : else
209 0 : throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
210 :
211 0 : if (pEntry->getName() != aName )
212 0 : pEntry->setName (aName);
213 0 : doInsertByName ( pEntry, sal_True );
214 : }
215 : else
216 0 : throw IllegalArgumentException(THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
217 : }
218 0 : }
219 0 : void SAL_CALL ZipPackageFolder::removeByName( const OUString& Name )
220 : throw(NoSuchElementException, WrappedTargetException, uno::RuntimeException, std::exception)
221 : {
222 0 : ContentHash::iterator aIter = maContents.find ( Name );
223 0 : if ( aIter == maContents.end() )
224 0 : throw NoSuchElementException(THROW_WHERE, uno::Reference< uno::XInterface >() );
225 0 : maContents.erase( aIter );
226 0 : }
227 : // XEnumerationAccess
228 0 : uno::Reference< XEnumeration > SAL_CALL ZipPackageFolder::createEnumeration( )
229 : throw(uno::RuntimeException, std::exception)
230 : {
231 0 : return uno::Reference < XEnumeration> (new ZipPackageFolderEnumeration(maContents));
232 : }
233 : // XElementAccess
234 0 : uno::Type SAL_CALL ZipPackageFolder::getElementType( )
235 : throw(uno::RuntimeException, std::exception)
236 : {
237 0 : return ::getCppuType ((const uno::Reference< XUnoTunnel > *) 0);
238 : }
239 0 : sal_Bool SAL_CALL ZipPackageFolder::hasElements( )
240 : throw(uno::RuntimeException, std::exception)
241 : {
242 0 : return maContents.size() > 0;
243 : }
244 : // XNameAccess
245 0 : ContentInfo& ZipPackageFolder::doGetByName( const OUString& aName )
246 : throw(NoSuchElementException, WrappedTargetException, uno::RuntimeException)
247 : {
248 0 : ContentHash::iterator aIter = maContents.find ( aName );
249 0 : if ( aIter == maContents.end())
250 0 : throw NoSuchElementException(THROW_WHERE, uno::Reference< uno::XInterface >() );
251 0 : return *(*aIter).second;
252 : }
253 0 : uno::Any SAL_CALL ZipPackageFolder::getByName( const OUString& aName )
254 : throw(NoSuchElementException, WrappedTargetException, uno::RuntimeException, std::exception)
255 : {
256 0 : return uno::makeAny ( doGetByName ( aName ).xTunnel );
257 : }
258 0 : uno::Sequence< OUString > SAL_CALL ZipPackageFolder::getElementNames( )
259 : throw(uno::RuntimeException, std::exception)
260 : {
261 0 : sal_uInt32 i=0, nSize = maContents.size();
262 0 : uno::Sequence < OUString > aSequence ( nSize );
263 0 : for ( ContentHash::const_iterator aIterator = maContents.begin(), aEnd = maContents.end();
264 : aIterator != aEnd;
265 : ++i, ++aIterator)
266 0 : aSequence[i] = (*aIterator).first;
267 0 : return aSequence;
268 : }
269 0 : sal_Bool SAL_CALL ZipPackageFolder::hasByName( const OUString& aName )
270 : throw(uno::RuntimeException, std::exception)
271 : {
272 0 : return maContents.find ( aName ) != maContents.end ();
273 : }
274 : // XNameReplace
275 0 : void SAL_CALL ZipPackageFolder::replaceByName( const OUString& aName, const uno::Any& aElement )
276 : throw(IllegalArgumentException, NoSuchElementException, WrappedTargetException, uno::RuntimeException, std::exception)
277 : {
278 0 : if ( hasByName( aName ) )
279 0 : removeByName( aName );
280 : else
281 0 : throw NoSuchElementException(THROW_WHERE, uno::Reference< uno::XInterface >() );
282 0 : insertByName(aName, aElement);
283 0 : }
284 :
285 0 : static void ImplSetStoredData( ZipEntry & rEntry, uno::Reference< XInputStream> & rStream )
286 : {
287 : // It's very annoying that we have to do this, but lots of zip packages
288 : // don't allow data descriptors for STORED streams, meaning we have to
289 : // know the size and CRC32 of uncompressed streams before we actually
290 : // write them !
291 0 : CRC32 aCRC32;
292 0 : rEntry.nMethod = STORED;
293 0 : rEntry.nCompressedSize = rEntry.nSize = aCRC32.updateStream ( rStream );
294 0 : rEntry.nCrc = aCRC32.getValue();
295 0 : }
296 :
297 0 : bool ZipPackageFolder::saveChild( const OUString &rShortName, const ContentInfo &rInfo, OUString &rPath, std::vector < uno::Sequence < PropertyValue > > &rManList, ZipOutputStream & rZipOut, const uno::Sequence < sal_Int8 >& rEncryptionKey, rtlRandomPool &rRandomPool)
298 : {
299 0 : bool bSuccess = true;
300 :
301 0 : const OUString sMediaTypeProperty ("MediaType");
302 0 : const OUString sVersionProperty ("Version");
303 0 : const OUString sFullPathProperty ("FullPath");
304 0 : const OUString sInitialisationVectorProperty ("InitialisationVector");
305 0 : const OUString sSaltProperty ("Salt");
306 0 : const OUString sIterationCountProperty ("IterationCount");
307 0 : const OUString sSizeProperty ("Size");
308 0 : const OUString sDigestProperty ("Digest");
309 0 : const OUString sEncryptionAlgProperty ("EncryptionAlgorithm");
310 0 : const OUString sStartKeyAlgProperty ("StartKeyAlgorithm");
311 0 : const OUString sDigestAlgProperty ("DigestAlgorithm");
312 0 : const OUString sDerivedKeySizeProperty ("DerivedKeySize");
313 :
314 0 : uno::Sequence < PropertyValue > aPropSet (PKG_SIZE_NOENCR_MNFST);
315 :
316 : OSL_ENSURE( ( rInfo.bFolder && rInfo.pFolder ) || ( !rInfo.bFolder && rInfo.pStream ), "A valid child object is expected!" );
317 0 : if ( rInfo.bFolder )
318 : {
319 0 : OUString sTempName = rPath + rShortName + "/";
320 :
321 0 : if ( !rInfo.pFolder->GetMediaType().isEmpty() )
322 : {
323 0 : aPropSet[PKG_MNFST_MEDIATYPE].Name = sMediaTypeProperty;
324 0 : aPropSet[PKG_MNFST_MEDIATYPE].Value <<= rInfo.pFolder->GetMediaType();
325 0 : aPropSet[PKG_MNFST_VERSION].Name = sVersionProperty;
326 0 : aPropSet[PKG_MNFST_VERSION].Value <<= rInfo.pFolder->GetVersion();
327 0 : aPropSet[PKG_MNFST_FULLPATH].Name = sFullPathProperty;
328 0 : aPropSet[PKG_MNFST_FULLPATH].Value <<= sTempName;
329 : }
330 : else
331 0 : aPropSet.realloc( 0 );
332 :
333 0 : rInfo.pFolder->saveContents( sTempName, rManList, rZipOut, rEncryptionKey, rRandomPool);
334 : }
335 : else
336 : {
337 : // if pTempEntry is necessary, it will be released and passed to the ZipOutputStream
338 : // and be deleted in the ZipOutputStream destructor
339 0 : auto_ptr < ZipEntry > pAutoTempEntry ( new ZipEntry );
340 0 : ZipEntry* pTempEntry = pAutoTempEntry.get();
341 :
342 : // In case the entry we are reading is also the entry we are writing, we will
343 : // store the ZipEntry data in pTempEntry
344 :
345 0 : ZipPackageFolder::copyZipEntry ( *pTempEntry, rInfo.pStream->aEntry );
346 0 : pTempEntry->sPath = rPath + rShortName;
347 0 : pTempEntry->nPathLen = (sal_Int16)( OUStringToOString( pTempEntry->sPath, RTL_TEXTENCODING_UTF8 ).getLength() );
348 :
349 0 : sal_Bool bToBeEncrypted = rInfo.pStream->IsToBeEncrypted() && (rEncryptionKey.getLength() || rInfo.pStream->HasOwnKey());
350 0 : sal_Bool bToBeCompressed = bToBeEncrypted ? sal_True : rInfo.pStream->IsToBeCompressed();
351 :
352 0 : aPropSet[PKG_MNFST_MEDIATYPE].Name = sMediaTypeProperty;
353 0 : aPropSet[PKG_MNFST_MEDIATYPE].Value <<= rInfo.pStream->GetMediaType( );
354 0 : aPropSet[PKG_MNFST_VERSION].Name = sVersionProperty;
355 0 : aPropSet[PKG_MNFST_VERSION].Value <<= OUString(); // no version is stored for streams currently
356 0 : aPropSet[PKG_MNFST_FULLPATH].Name = sFullPathProperty;
357 0 : aPropSet[PKG_MNFST_FULLPATH].Value <<= pTempEntry->sPath;
358 :
359 : OSL_ENSURE( rInfo.pStream->GetStreamMode() != PACKAGE_STREAM_NOTSET, "Unacceptable ZipPackageStream mode!" );
360 :
361 0 : sal_Bool bRawStream = sal_False;
362 0 : if ( rInfo.pStream->GetStreamMode() == PACKAGE_STREAM_DETECT )
363 0 : bRawStream = rInfo.pStream->ParsePackageRawStream();
364 0 : else if ( rInfo.pStream->GetStreamMode() == PACKAGE_STREAM_RAW )
365 0 : bRawStream = sal_True;
366 :
367 0 : sal_Bool bTransportOwnEncrStreamAsRaw = sal_False;
368 : // During the storing the original size of the stream can be changed
369 : // TODO/LATER: get rid of this hack
370 0 : sal_Int64 nOwnStreamOrigSize = bRawStream ? rInfo.pStream->GetMagicalHackSize() : rInfo.pStream->getSize();
371 :
372 0 : sal_Bool bUseNonSeekableAccess = sal_False;
373 0 : uno::Reference < XInputStream > xStream;
374 0 : if ( !rInfo.pStream->IsPackageMember() && !bRawStream && !bToBeEncrypted && bToBeCompressed )
375 : {
376 : // the stream is not a package member, not a raw stream,
377 : // it should not be encrypted and it should be compressed,
378 : // in this case nonseekable access can be used
379 :
380 0 : xStream = rInfo.pStream->GetOwnStreamNoWrap();
381 0 : uno::Reference < XSeekable > xSeek ( xStream, uno::UNO_QUERY );
382 :
383 0 : bUseNonSeekableAccess = ( xStream.is() && !xSeek.is() );
384 : }
385 :
386 0 : if ( !bUseNonSeekableAccess )
387 : {
388 0 : xStream = rInfo.pStream->getRawData();
389 :
390 0 : if ( !xStream.is() )
391 : {
392 : OSL_FAIL( "ZipPackageStream didn't have a stream associated with it, skipping!" );
393 0 : bSuccess = false;
394 0 : return bSuccess;
395 : }
396 :
397 0 : uno::Reference < XSeekable > xSeek ( xStream, uno::UNO_QUERY );
398 : try
399 : {
400 0 : if ( xSeek.is() )
401 : {
402 : // If the stream is a raw one, then we should be positioned
403 : // at the beginning of the actual data
404 0 : if ( !bToBeCompressed || bRawStream )
405 : {
406 : // The raw stream can neither be encrypted nor connected
407 : OSL_ENSURE( !bRawStream || !(bToBeCompressed || bToBeEncrypted), "The stream is already encrypted!\n" );
408 0 : xSeek->seek ( bRawStream ? rInfo.pStream->GetMagicalHackPos() : 0 );
409 0 : ImplSetStoredData ( *pTempEntry, xStream );
410 :
411 : // TODO/LATER: Get rid of hacks related to switching of Flag Method and Size properties!
412 : }
413 0 : else if ( bToBeEncrypted )
414 : {
415 : // this is the correct original size
416 0 : pTempEntry->nSize = xSeek->getLength();
417 0 : nOwnStreamOrigSize = pTempEntry->nSize;
418 : }
419 :
420 0 : xSeek->seek ( 0 );
421 : }
422 : else
423 : {
424 : // Okay, we don't have an xSeekable stream. This is possibly bad.
425 : // check if it's one of our own streams, if it is then we know that
426 : // each time we ask for it we'll get a new stream that will be
427 : // at position zero...otherwise, assert and skip this stream...
428 0 : if ( rInfo.pStream->IsPackageMember() )
429 : {
430 : // if the password has been changed than the stream should not be package member any more
431 0 : if ( rInfo.pStream->IsEncrypted() && rInfo.pStream->IsToBeEncrypted() )
432 : {
433 : // Should be handled close to the raw stream handling
434 0 : bTransportOwnEncrStreamAsRaw = sal_True;
435 0 : pTempEntry->nMethod = STORED;
436 :
437 : // TODO/LATER: get rid of this situation
438 : // this size should be different from the one that will be stored in manifest.xml
439 : // it is used in storing algorithms and after storing the correct size will be set
440 0 : pTempEntry->nSize = pTempEntry->nCompressedSize;
441 : }
442 : }
443 : else
444 : {
445 0 : bSuccess = false;
446 0 : return bSuccess;
447 : }
448 : }
449 : }
450 0 : catch ( uno::Exception& )
451 : {
452 0 : bSuccess = false;
453 0 : return bSuccess;
454 : }
455 :
456 0 : if ( bToBeEncrypted || bRawStream || bTransportOwnEncrStreamAsRaw )
457 : {
458 0 : if ( bToBeEncrypted && !bTransportOwnEncrStreamAsRaw )
459 : {
460 0 : uno::Sequence < sal_Int8 > aSalt( 16 ), aVector( rInfo.pStream->GetBlockSize() );
461 0 : rtl_random_getBytes ( rRandomPool, aSalt.getArray(), 16 );
462 0 : rtl_random_getBytes ( rRandomPool, aVector.getArray(), aVector.getLength() );
463 0 : sal_Int32 nIterationCount = 1024;
464 :
465 0 : if ( !rInfo.pStream->HasOwnKey() )
466 0 : rInfo.pStream->setKey ( rEncryptionKey );
467 :
468 0 : rInfo.pStream->setInitialisationVector ( aVector );
469 0 : rInfo.pStream->setSalt ( aSalt );
470 0 : rInfo.pStream->setIterationCount ( nIterationCount );
471 : }
472 :
473 : // last property is digest, which is inserted later if we didn't have
474 : // a magic header
475 0 : aPropSet.realloc(PKG_SIZE_ENCR_MNFST);
476 :
477 0 : aPropSet[PKG_MNFST_INIVECTOR].Name = sInitialisationVectorProperty;
478 0 : aPropSet[PKG_MNFST_INIVECTOR].Value <<= rInfo.pStream->getInitialisationVector();
479 0 : aPropSet[PKG_MNFST_SALT].Name = sSaltProperty;
480 0 : aPropSet[PKG_MNFST_SALT].Value <<= rInfo.pStream->getSalt();
481 0 : aPropSet[PKG_MNFST_ITERATION].Name = sIterationCountProperty;
482 0 : aPropSet[PKG_MNFST_ITERATION].Value <<= rInfo.pStream->getIterationCount ();
483 :
484 : // Need to store the uncompressed size in the manifest
485 : OSL_ENSURE( nOwnStreamOrigSize >= 0, "The stream size was not correctly initialized!\n" );
486 0 : aPropSet[PKG_MNFST_UCOMPSIZE].Name = sSizeProperty;
487 0 : aPropSet[PKG_MNFST_UCOMPSIZE].Value <<= nOwnStreamOrigSize;
488 :
489 0 : if ( bRawStream || bTransportOwnEncrStreamAsRaw )
490 : {
491 0 : ::rtl::Reference< EncryptionData > xEncData = rInfo.pStream->GetEncryptionData();
492 0 : if ( !xEncData.is() )
493 0 : throw uno::RuntimeException();
494 :
495 0 : aPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
496 0 : aPropSet[PKG_MNFST_DIGEST].Value <<= rInfo.pStream->getDigest();
497 0 : aPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
498 0 : aPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
499 0 : aPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
500 0 : aPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
501 0 : aPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
502 0 : aPropSet[PKG_MNFST_DIGESTALG].Value <<= xEncData->m_nCheckAlg;
503 0 : aPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
504 0 : aPropSet[PKG_MNFST_DERKEYSIZE].Value <<= xEncData->m_nDerivedKeySize;
505 : }
506 0 : }
507 : }
508 :
509 : // If the entry is already stored in the zip file in the format we
510 : // want for this write...copy it raw
511 0 : if ( !bUseNonSeekableAccess
512 0 : && ( bRawStream || bTransportOwnEncrStreamAsRaw
513 0 : || ( rInfo.pStream->IsPackageMember() && !bToBeEncrypted
514 0 : && ( ( rInfo.pStream->aEntry.nMethod == DEFLATED && bToBeCompressed )
515 0 : || ( rInfo.pStream->aEntry.nMethod == STORED && !bToBeCompressed ) ) ) ) )
516 : {
517 : // If it's a PackageMember, then it's an unbuffered stream and we need
518 : // to get a new version of it as we can't seek backwards.
519 0 : if ( rInfo.pStream->IsPackageMember() )
520 : {
521 0 : xStream = rInfo.pStream->getRawData();
522 0 : if ( !xStream.is() )
523 : {
524 : // Make sure that we actually _got_ a new one !
525 0 : bSuccess = false;
526 0 : return bSuccess;
527 : }
528 : }
529 :
530 : try
531 : {
532 0 : if ( bRawStream )
533 0 : xStream->skipBytes( rInfo.pStream->GetMagicalHackPos() );
534 :
535 0 : rZipOut.putNextEntry ( *pTempEntry, rInfo.pStream, sal_False );
536 : // the entry is provided to the ZipOutputStream that will delete it
537 0 : pAutoTempEntry.release();
538 :
539 0 : uno::Sequence < sal_Int8 > aSeq ( n_ConstBufferSize );
540 : sal_Int32 nLength;
541 :
542 0 : do
543 : {
544 0 : nLength = xStream->readBytes( aSeq, n_ConstBufferSize );
545 0 : rZipOut.rawWrite(aSeq, 0, nLength);
546 : }
547 : while ( nLength == n_ConstBufferSize );
548 :
549 0 : rZipOut.rawCloseEntry();
550 : }
551 0 : catch ( ZipException& )
552 : {
553 0 : bSuccess = false;
554 : }
555 0 : catch ( IOException& )
556 : {
557 0 : bSuccess = false;
558 : }
559 : }
560 : else
561 : {
562 : // This stream is defenitly not a raw stream
563 :
564 : // If nonseekable access is used the stream should be at the beginning and
565 : // is useless after the storing. Thus if the storing fails the package should
566 : // be thrown away ( as actually it is done currently )!
567 : // To allow to reuse the package after the error, the optimization must be removed!
568 :
569 : // If it's a PackageMember, then our previous reference held a 'raw' stream
570 : // so we need to re-get it, unencrypted, uncompressed and positioned at the
571 : // beginning of the stream
572 0 : if ( rInfo.pStream->IsPackageMember() )
573 : {
574 0 : xStream = rInfo.pStream->getInputStream();
575 0 : if ( !xStream.is() )
576 : {
577 : // Make sure that we actually _got_ a new one !
578 0 : bSuccess = false;
579 0 : return bSuccess;
580 : }
581 : }
582 :
583 0 : if ( bToBeCompressed )
584 : {
585 0 : pTempEntry->nMethod = DEFLATED;
586 0 : pTempEntry->nCrc = -1;
587 0 : pTempEntry->nCompressedSize = pTempEntry->nSize = -1;
588 : }
589 :
590 : try
591 : {
592 0 : rZipOut.putNextEntry ( *pTempEntry, rInfo.pStream, bToBeEncrypted);
593 : // the entry is provided to the ZipOutputStream that will delete it
594 0 : pAutoTempEntry.release();
595 :
596 : sal_Int32 nLength;
597 0 : uno::Sequence < sal_Int8 > aSeq (n_ConstBufferSize);
598 0 : do
599 : {
600 0 : nLength = xStream->readBytes(aSeq, n_ConstBufferSize);
601 0 : rZipOut.write(aSeq, 0, nLength);
602 : }
603 : while ( nLength == n_ConstBufferSize );
604 :
605 0 : rZipOut.closeEntry();
606 : }
607 0 : catch ( ZipException& )
608 : {
609 0 : bSuccess = false;
610 : }
611 0 : catch ( IOException& )
612 : {
613 0 : bSuccess = false;
614 : }
615 :
616 0 : if ( bToBeEncrypted )
617 : {
618 0 : ::rtl::Reference< EncryptionData > xEncData = rInfo.pStream->GetEncryptionData();
619 0 : if ( !xEncData.is() )
620 0 : throw uno::RuntimeException();
621 :
622 0 : aPropSet[PKG_MNFST_DIGEST].Name = sDigestProperty;
623 0 : aPropSet[PKG_MNFST_DIGEST].Value <<= rInfo.pStream->getDigest();
624 0 : aPropSet[PKG_MNFST_ENCALG].Name = sEncryptionAlgProperty;
625 0 : aPropSet[PKG_MNFST_ENCALG].Value <<= xEncData->m_nEncAlg;
626 0 : aPropSet[PKG_MNFST_STARTALG].Name = sStartKeyAlgProperty;
627 0 : aPropSet[PKG_MNFST_STARTALG].Value <<= xEncData->m_nStartKeyGenID;
628 0 : aPropSet[PKG_MNFST_DIGESTALG].Name = sDigestAlgProperty;
629 0 : aPropSet[PKG_MNFST_DIGESTALG].Value <<= xEncData->m_nCheckAlg;
630 0 : aPropSet[PKG_MNFST_DERKEYSIZE].Name = sDerivedKeySizeProperty;
631 0 : aPropSet[PKG_MNFST_DERKEYSIZE].Value <<= xEncData->m_nDerivedKeySize;
632 :
633 0 : rInfo.pStream->SetIsEncrypted ( sal_True );
634 : }
635 : }
636 :
637 0 : if( bSuccess )
638 : {
639 0 : if ( !rInfo.pStream->IsPackageMember() )
640 : {
641 0 : rInfo.pStream->CloseOwnStreamIfAny();
642 0 : rInfo.pStream->SetPackageMember ( sal_True );
643 : }
644 :
645 0 : if ( bRawStream )
646 : {
647 : // the raw stream was integrated and now behaves
648 : // as usual encrypted stream
649 0 : rInfo.pStream->SetToBeEncrypted( sal_True );
650 : }
651 :
652 : // Then copy it back afterwards...
653 0 : ZipPackageFolder::copyZipEntry ( rInfo.pStream->aEntry, *pTempEntry );
654 :
655 : // Remove hacky bit from entry flags
656 0 : if ( rInfo.pStream->aEntry.nFlag & ( 1 << 4 ) )
657 : {
658 0 : rInfo.pStream->aEntry.nFlag &= ~( 1 << 4 );
659 0 : rInfo.pStream->aEntry.nMethod = STORED;
660 : }
661 :
662 : // TODO/LATER: get rid of this hack ( the encrypted stream size property is changed during saving )
663 0 : if ( rInfo.pStream->IsEncrypted() )
664 0 : rInfo.pStream->setSize( nOwnStreamOrigSize );
665 :
666 0 : rInfo.pStream->aEntry.nOffset *= -1;
667 0 : }
668 : }
669 :
670 : // folder can have a mediatype only in package format
671 0 : if ( aPropSet.getLength()
672 0 : && ( m_nFormat == embed::StorageFormats::PACKAGE || ( m_nFormat == embed::StorageFormats::OFOPXML && !rInfo.bFolder ) ) )
673 0 : rManList.push_back( aPropSet );
674 :
675 0 : return bSuccess;
676 : }
677 :
678 0 : void ZipPackageFolder::saveContents( OUString &rPath, std::vector < uno::Sequence < PropertyValue > > &rManList, ZipOutputStream & rZipOut, const uno::Sequence < sal_Int8 >& rEncryptionKey, rtlRandomPool &rRandomPool )
679 : throw( uno::RuntimeException )
680 : {
681 0 : bool bWritingFailed = false;
682 :
683 0 : if ( maContents.begin() == maContents.end() && !rPath.isEmpty() && m_nFormat != embed::StorageFormats::OFOPXML )
684 : {
685 : // it is an empty subfolder, use workaround to store it
686 0 : ZipEntry* pTempEntry = new ZipEntry();
687 0 : ZipPackageFolder::copyZipEntry ( *pTempEntry, aEntry );
688 0 : pTempEntry->nPathLen = (sal_Int16)( OUStringToOString( rPath, RTL_TEXTENCODING_UTF8 ).getLength() );
689 0 : pTempEntry->nExtraLen = -1;
690 0 : pTempEntry->sPath = rPath;
691 :
692 : try
693 : {
694 0 : rZipOut.putNextEntry( *pTempEntry, NULL, sal_False );
695 0 : rZipOut.rawCloseEntry();
696 : }
697 0 : catch ( ZipException& )
698 : {
699 0 : bWritingFailed = true;
700 : }
701 0 : catch ( IOException& )
702 : {
703 0 : bWritingFailed = true;
704 : }
705 : }
706 :
707 0 : bool bMimeTypeStreamStored = false;
708 0 : OUString aMimeTypeStreamName("mimetype");
709 0 : if ( m_nFormat == embed::StorageFormats::ZIP && rPath.isEmpty() )
710 : {
711 : // let the "mimtype" stream in root folder be stored as the first stream if it is zip format
712 0 : ContentHash::iterator aIter = maContents.find ( aMimeTypeStreamName );
713 0 : if ( aIter != maContents.end() && !(*aIter).second->bFolder )
714 : {
715 0 : bMimeTypeStreamStored = true;
716 0 : bWritingFailed = !saveChild( (*aIter).first, *(*aIter).second, rPath, rManList, rZipOut, rEncryptionKey, rRandomPool );
717 : }
718 : }
719 :
720 0 : for ( ContentHash::const_iterator aCI = maContents.begin(), aEnd = maContents.end();
721 : aCI != aEnd;
722 : ++aCI)
723 : {
724 0 : const OUString &rShortName = (*aCI).first;
725 0 : const ContentInfo &rInfo = *(*aCI).second;
726 :
727 0 : if ( !bMimeTypeStreamStored || !rShortName.equals( aMimeTypeStreamName ) )
728 0 : bWritingFailed = !saveChild( rShortName, rInfo, rPath, rManList, rZipOut, rEncryptionKey, rRandomPool );
729 : }
730 :
731 0 : if( bWritingFailed )
732 0 : throw uno::RuntimeException(THROW_WHERE, uno::Reference< uno::XInterface >() );
733 0 : }
734 :
735 0 : void ZipPackageFolder::releaseUpwardRef( void )
736 : {
737 : // Now it is possible that a package folder is disconnected from the package before removing of the folder.
738 : // Such a scenario is used in storage implementation. When a new version of a folder is provided the old
739 : // one is retrieved, removed from the package but preserved for the error handling.
740 : // In this scenario the referencing to the parent is not really useful, since it requires disposing.
741 :
742 : // Actually there is no need in having a reference to the parent, it even make things more complicated and
743 : // requires disposing mechanics. Using of a simple pointer seems to be easier solution and also a safe enough.
744 :
745 0 : clearParent();
746 :
747 : #if 0
748 : for ( ContentHash::const_iterator aCI = maContents.begin();
749 : aCI!=maContents.end();
750 : aCI++)
751 : {
752 : ContentInfo &rInfo = * (*aCI).second;
753 : if ( rInfo.bFolder )// && ! rInfo.pFolder->HasReleased () )
754 : rInfo.pFolder->releaseUpwardRef();
755 : else //if ( !rInfo.bFolder && !rInfo.pStream->HasReleased() )
756 : rInfo.pStream->clearParent();
757 : }
758 : clearParent();
759 :
760 : OSL_ENSURE ( m_refCount == 1, "Ref-count is not 1!" );
761 : #endif
762 0 : }
763 :
764 0 : sal_Int64 SAL_CALL ZipPackageFolder::getSomething( const uno::Sequence< sal_Int8 >& aIdentifier )
765 : throw(uno::RuntimeException, std::exception)
766 : {
767 0 : sal_Int64 nMe = 0;
768 0 : if ( aIdentifier.getLength() == 16 &&
769 0 : 0 == memcmp(static_getImplementationId().getConstArray(), aIdentifier.getConstArray(), 16 ) )
770 0 : nMe = reinterpret_cast < sal_Int64 > ( this );
771 0 : return nMe;
772 : }
773 0 : void SAL_CALL ZipPackageFolder::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
774 : throw(UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, uno::RuntimeException, std::exception)
775 : {
776 0 : if ( aPropertyName == "MediaType" )
777 : {
778 : // TODO/LATER: activate when zip ucp is ready
779 : // if ( m_nFormat != embed::StorageFormats::PACKAGE )
780 : // throw UnknownPropertyException(THROW_WHERE, uno::Reference< uno::XInterface >() );
781 :
782 0 : aValue >>= sMediaType;
783 : }
784 0 : else if ( aPropertyName == "Version" )
785 0 : aValue >>= m_sVersion;
786 0 : else if ( aPropertyName == "Size" )
787 0 : aValue >>= aEntry.nSize;
788 : else
789 0 : throw UnknownPropertyException(THROW_WHERE, uno::Reference< uno::XInterface >() );
790 0 : }
791 0 : uno::Any SAL_CALL ZipPackageFolder::getPropertyValue( const OUString& PropertyName )
792 : throw(UnknownPropertyException, WrappedTargetException, uno::RuntimeException, std::exception)
793 : {
794 0 : if ( PropertyName == "MediaType" )
795 : {
796 : // TODO/LATER: activate when zip ucp is ready
797 : // if ( m_nFormat != embed::StorageFormats::PACKAGE )
798 : // throw UnknownPropertyException(THROW_WHERE, uno::Reference< uno::XInterface >() );
799 :
800 0 : return uno::makeAny ( sMediaType );
801 : }
802 0 : else if ( PropertyName == "Version" )
803 0 : return uno::makeAny( m_sVersion );
804 0 : else if ( PropertyName == "Size" )
805 0 : return uno::makeAny ( aEntry.nSize );
806 : else
807 0 : throw UnknownPropertyException(THROW_WHERE, uno::Reference< uno::XInterface >() );
808 : }
809 :
810 0 : void ZipPackageFolder::doInsertByName ( ZipPackageEntry *pEntry, sal_Bool bSetParent )
811 : throw(IllegalArgumentException, ElementExistException, WrappedTargetException, uno::RuntimeException)
812 : {
813 : try
814 : {
815 0 : if ( pEntry->IsFolder() )
816 0 : maContents[pEntry->getName()] = new ContentInfo ( static_cast < ZipPackageFolder *> ( pEntry ) );
817 : else
818 0 : maContents[pEntry->getName()] = new ContentInfo ( static_cast < ZipPackageStream *> ( pEntry ) );
819 : }
820 0 : catch(const uno::Exception& rEx)
821 : {
822 : (void)rEx;
823 0 : throw;
824 : }
825 0 : if ( bSetParent )
826 0 : pEntry->setParent ( *this );
827 0 : }
828 0 : OUString ZipPackageFolder::getImplementationName()
829 : throw (uno::RuntimeException, std::exception)
830 : {
831 0 : return OUString("ZipPackageFolder");
832 : }
833 :
834 0 : uno::Sequence< OUString > ZipPackageFolder::getSupportedServiceNames()
835 : throw (uno::RuntimeException, std::exception)
836 : {
837 0 : uno::Sequence< OUString > aNames(1);
838 0 : aNames[0] = "com.sun.star.packages.PackageFolder";
839 0 : return aNames;
840 : }
841 :
842 0 : sal_Bool SAL_CALL ZipPackageFolder::supportsService( OUString const & rServiceName )
843 : throw (uno::RuntimeException, std::exception)
844 : {
845 0 : return cppu::supportsService(this, rServiceName);
846 : }
847 :
848 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|