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