1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_package.hxx" 30 31 #include <com/sun/star/packages/zip/ZipConstants.hpp> 32 #include <com/sun/star/io/XOutputStream.hpp> 33 #include <comphelper/storagehelper.hxx> 34 35 #include <osl/time.h> 36 37 #include <EncryptionData.hxx> 38 #include <PackageConstants.hxx> 39 #include <ZipEntry.hxx> 40 #include <ZipFile.hxx> 41 #include <ZipPackageStream.hxx> 42 #include <ZipOutputStream.hxx> 43 44 using namespace rtl; 45 using namespace com::sun::star; 46 using namespace com::sun::star::io; 47 using namespace com::sun::star::uno; 48 using namespace com::sun::star::packages; 49 using namespace com::sun::star::packages::zip; 50 using namespace com::sun::star::packages::zip::ZipConstants; 51 52 /** This class is used to write Zip files 53 */ 54 ZipOutputStream::ZipOutputStream( const uno::Reference< lang::XMultiServiceFactory >& xFactory, 55 const uno::Reference < XOutputStream > &xOStream ) 56 : m_xFactory( xFactory ) 57 , xStream(xOStream) 58 , m_aDeflateBuffer(n_ConstBufferSize) 59 , aDeflater(DEFAULT_COMPRESSION, sal_True) 60 , aChucker(xOStream) 61 , pCurrentEntry(NULL) 62 , nMethod(DEFLATED) 63 , bFinished(sal_False) 64 , bEncryptCurrentEntry(sal_False) 65 , m_pCurrentStream(NULL) 66 { 67 } 68 69 ZipOutputStream::~ZipOutputStream( void ) 70 { 71 for (sal_Int32 i = 0, nEnd = aZipList.size(); i < nEnd; i++) 72 delete aZipList[i]; 73 } 74 75 void SAL_CALL ZipOutputStream::setMethod( sal_Int32 nNewMethod ) 76 throw(RuntimeException) 77 { 78 nMethod = static_cast < sal_Int16 > (nNewMethod); 79 } 80 void SAL_CALL ZipOutputStream::setLevel( sal_Int32 nNewLevel ) 81 throw(RuntimeException) 82 { 83 aDeflater.setLevel( nNewLevel); 84 } 85 86 void SAL_CALL ZipOutputStream::putNextEntry( ZipEntry& rEntry, 87 ZipPackageStream* pStream, 88 sal_Bool bEncrypt) 89 throw(IOException, RuntimeException) 90 { 91 if (pCurrentEntry != NULL) 92 closeEntry(); 93 if (rEntry.nTime == -1) 94 rEntry.nTime = getCurrentDosTime(); 95 if (rEntry.nMethod == -1) 96 rEntry.nMethod = nMethod; 97 rEntry.nVersion = 20; 98 rEntry.nFlag = 1 << 11; 99 if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 || 100 rEntry.nCrc == -1) 101 { 102 rEntry.nSize = rEntry.nCompressedSize = 0; 103 rEntry.nFlag |= 8; 104 } 105 106 if (bEncrypt) 107 { 108 bEncryptCurrentEntry = sal_True; 109 110 m_xCipherContext = ZipFile::StaticGetCipher( m_xFactory, pStream->GetEncryptionData(), true ); 111 m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum( m_xFactory, pStream->GetEncryptionData() ); 112 mnDigested = 0; 113 rEntry.nFlag |= 1 << 4; 114 m_pCurrentStream = pStream; 115 } 116 sal_Int32 nLOCLength = writeLOC(rEntry); 117 rEntry.nOffset = static_cast < sal_Int32 > (aChucker.GetPosition()) - nLOCLength; 118 aZipList.push_back( &rEntry ); 119 pCurrentEntry = &rEntry; 120 } 121 122 void SAL_CALL ZipOutputStream::closeEntry( ) 123 throw(IOException, RuntimeException) 124 { 125 ZipEntry *pEntry = pCurrentEntry; 126 if (pEntry) 127 { 128 switch (pEntry->nMethod) 129 { 130 case DEFLATED: 131 aDeflater.finish(); 132 while (!aDeflater.finished()) 133 doDeflate(); 134 if ((pEntry->nFlag & 8) == 0) 135 { 136 if (pEntry->nSize != aDeflater.getTotalIn()) 137 { 138 OSL_ENSURE(false,"Invalid entry size"); 139 } 140 if (pEntry->nCompressedSize != aDeflater.getTotalOut()) 141 { 142 //VOS_DEBUG_ONLY("Invalid entry compressed size"); 143 // Different compression strategies make the merit of this 144 // test somewhat dubious 145 pEntry->nCompressedSize = aDeflater.getTotalOut(); 146 } 147 if (pEntry->nCrc != aCRC.getValue()) 148 { 149 OSL_ENSURE(false,"Invalid entry CRC-32"); 150 } 151 } 152 else 153 { 154 if ( !bEncryptCurrentEntry ) 155 { 156 pEntry->nSize = aDeflater.getTotalIn(); 157 pEntry->nCompressedSize = aDeflater.getTotalOut(); 158 } 159 pEntry->nCrc = aCRC.getValue(); 160 writeEXT(*pEntry); 161 } 162 aDeflater.reset(); 163 aCRC.reset(); 164 break; 165 case STORED: 166 if (!((pEntry->nFlag & 8) == 0)) 167 OSL_ENSURE ( false, "Serious error, one of compressed size, size or CRC was -1 in a STORED stream"); 168 break; 169 default: 170 OSL_ENSURE(false,"Invalid compression method"); 171 break; 172 } 173 174 if (bEncryptCurrentEntry) 175 { 176 bEncryptCurrentEntry = sal_False; 177 178 m_xCipherContext.clear(); 179 180 uno::Sequence< sal_Int8 > aDigestSeq; 181 if ( m_xDigestContext.is() ) 182 { 183 aDigestSeq = m_xDigestContext->finalizeDigestAndDispose(); 184 m_xDigestContext.clear(); 185 } 186 187 if ( m_pCurrentStream ) 188 m_pCurrentStream->setDigest( aDigestSeq ); 189 } 190 pCurrentEntry = NULL; 191 m_pCurrentStream = NULL; 192 } 193 } 194 195 void SAL_CALL ZipOutputStream::write( const Sequence< sal_Int8 >& rBuffer, sal_Int32 nNewOffset, sal_Int32 nNewLength ) 196 throw(IOException, RuntimeException) 197 { 198 switch (pCurrentEntry->nMethod) 199 { 200 case DEFLATED: 201 if (!aDeflater.finished()) 202 { 203 aDeflater.setInputSegment(rBuffer, nNewOffset, nNewLength); 204 while (!aDeflater.needsInput()) 205 doDeflate(); 206 if (!bEncryptCurrentEntry) 207 aCRC.updateSegment(rBuffer, nNewOffset, nNewLength); 208 } 209 break; 210 case STORED: 211 { 212 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength ); 213 aChucker.WriteBytes( aTmpBuffer ); 214 } 215 break; 216 } 217 } 218 219 void SAL_CALL ZipOutputStream::rawWrite( Sequence< sal_Int8 >& rBuffer, sal_Int32 /*nNewOffset*/, sal_Int32 nNewLength ) 220 throw(IOException, RuntimeException) 221 { 222 Sequence < sal_Int8 > aTmpBuffer ( rBuffer.getConstArray(), nNewLength ); 223 aChucker.WriteBytes( aTmpBuffer ); 224 } 225 226 void SAL_CALL ZipOutputStream::rawCloseEntry( ) 227 throw(IOException, RuntimeException) 228 { 229 if ( pCurrentEntry->nMethod == DEFLATED && ( pCurrentEntry->nFlag & 8 ) ) 230 writeEXT(*pCurrentEntry); 231 pCurrentEntry = NULL; 232 } 233 234 void SAL_CALL ZipOutputStream::finish( ) 235 throw(IOException, RuntimeException) 236 { 237 if (bFinished) 238 return; 239 240 if (pCurrentEntry != NULL) 241 closeEntry(); 242 243 if (aZipList.size() < 1) 244 OSL_ENSURE(false,"Zip file must have at least one entry!\n"); 245 246 sal_Int32 nOffset= static_cast < sal_Int32 > (aChucker.GetPosition()); 247 for (sal_Int32 i =0, nEnd = aZipList.size(); i < nEnd; i++) 248 writeCEN( *aZipList[i] ); 249 writeEND( nOffset, static_cast < sal_Int32 > (aChucker.GetPosition()) - nOffset); 250 bFinished = sal_True; 251 xStream->flush(); 252 } 253 254 void ZipOutputStream::doDeflate() 255 { 256 sal_Int32 nLength = aDeflater.doDeflateSegment(m_aDeflateBuffer, 0, m_aDeflateBuffer.getLength()); 257 258 if ( nLength > 0 ) 259 { 260 uno::Sequence< sal_Int8 > aTmpBuffer( m_aDeflateBuffer.getConstArray(), nLength ); 261 if ( bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() ) 262 { 263 // Need to update our digest before encryption... 264 sal_Int32 nDiff = n_ConstDigestLength - mnDigested; 265 if ( nDiff ) 266 { 267 sal_Int32 nEat = ::std::min( nLength, nDiff ); 268 uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat ); 269 m_xDigestContext->updateDigest( aTmpSeq ); 270 mnDigested = mnDigested + static_cast< sal_Int16 >( nEat ); 271 } 272 273 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer ); 274 275 aChucker.WriteBytes( aEncryptionBuffer ); 276 277 // the sizes as well as checksum for encrypted streams is calculated here 278 pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength(); 279 pCurrentEntry->nSize = pCurrentEntry->nCompressedSize; 280 aCRC.update( aEncryptionBuffer ); 281 } 282 else 283 { 284 aChucker.WriteBytes ( aTmpBuffer ); 285 } 286 } 287 288 if ( aDeflater.finished() && bEncryptCurrentEntry && m_xDigestContext.is() && m_xCipherContext.is() ) 289 { 290 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose(); 291 if ( aEncryptionBuffer.getLength() ) 292 { 293 aChucker.WriteBytes( aEncryptionBuffer ); 294 295 // the sizes as well as checksum for encrypted streams is calculated hier 296 pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength(); 297 pCurrentEntry->nSize = pCurrentEntry->nCompressedSize; 298 aCRC.update( aEncryptionBuffer ); 299 } 300 } 301 } 302 303 void ZipOutputStream::writeEND(sal_uInt32 nOffset, sal_uInt32 nLength) 304 throw(IOException, RuntimeException) 305 { 306 aChucker << ENDSIG; 307 aChucker << static_cast < sal_Int16 > ( 0 ); 308 aChucker << static_cast < sal_Int16 > ( 0 ); 309 aChucker << static_cast < sal_Int16 > ( aZipList.size() ); 310 aChucker << static_cast < sal_Int16 > ( aZipList.size() ); 311 aChucker << nLength; 312 aChucker << nOffset; 313 aChucker << static_cast < sal_Int16 > ( 0 ); 314 } 315 void ZipOutputStream::writeCEN( const ZipEntry &rEntry ) 316 throw(IOException, RuntimeException) 317 { 318 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) ) 319 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), uno::Reference< XInterface >() ); 320 321 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 ); 322 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() ); 323 324 aChucker << CENSIG; 325 aChucker << rEntry.nVersion; 326 aChucker << rEntry.nVersion; 327 if (rEntry.nFlag & (1 << 4) ) 328 { 329 // If it's an encrypted entry, we pretend its stored plain text 330 ZipEntry *pEntry = const_cast < ZipEntry * > ( &rEntry ); 331 pEntry->nFlag &= ~(1 <<4 ); 332 aChucker << rEntry.nFlag; 333 aChucker << static_cast < sal_Int16 > ( STORED ); 334 } 335 else 336 { 337 aChucker << rEntry.nFlag; 338 aChucker << rEntry.nMethod; 339 } 340 aChucker << static_cast < sal_uInt32> ( rEntry.nTime ); 341 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc ); 342 aChucker << rEntry.nCompressedSize; 343 aChucker << rEntry.nSize; 344 aChucker << nNameLength; 345 aChucker << static_cast < sal_Int16> (0); 346 aChucker << static_cast < sal_Int16> (0); 347 aChucker << static_cast < sal_Int16> (0); 348 aChucker << static_cast < sal_Int16> (0); 349 aChucker << static_cast < sal_Int32> (0); 350 aChucker << rEntry.nOffset; 351 352 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() ); 353 aChucker.WriteBytes( aSequence ); 354 } 355 void ZipOutputStream::writeEXT( const ZipEntry &rEntry ) 356 throw(IOException, RuntimeException) 357 { 358 aChucker << EXTSIG; 359 aChucker << static_cast < sal_uInt32> ( rEntry.nCrc ); 360 aChucker << rEntry.nCompressedSize; 361 aChucker << rEntry.nSize; 362 } 363 364 sal_Int32 ZipOutputStream::writeLOC( const ZipEntry &rEntry ) 365 throw(IOException, RuntimeException) 366 { 367 if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( rEntry.sPath, sal_True ) ) 368 throw IOException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unexpected character is used in file name." ) ), uno::Reference< XInterface >() ); 369 370 ::rtl::OString sUTF8Name = ::rtl::OUStringToOString( rEntry.sPath, RTL_TEXTENCODING_UTF8 ); 371 sal_Int16 nNameLength = static_cast < sal_Int16 > ( sUTF8Name.getLength() ); 372 373 aChucker << LOCSIG; 374 aChucker << rEntry.nVersion; 375 376 if (rEntry.nFlag & (1 << 4) ) 377 { 378 // If it's an encrypted entry, we pretend its stored plain text 379 sal_Int16 nTmpFlag = rEntry.nFlag; 380 nTmpFlag &= ~(1 <<4 ); 381 aChucker << nTmpFlag; 382 aChucker << static_cast < sal_Int16 > ( STORED ); 383 } 384 else 385 { 386 aChucker << rEntry.nFlag; 387 aChucker << rEntry.nMethod; 388 } 389 390 aChucker << static_cast < sal_uInt32 > (rEntry.nTime); 391 if ((rEntry.nFlag & 8) == 8 ) 392 { 393 aChucker << static_cast < sal_Int32 > (0); 394 aChucker << static_cast < sal_Int32 > (0); 395 aChucker << static_cast < sal_Int32 > (0); 396 } 397 else 398 { 399 aChucker << static_cast < sal_uInt32 > (rEntry.nCrc); 400 aChucker << rEntry.nCompressedSize; 401 aChucker << rEntry.nSize; 402 } 403 aChucker << nNameLength; 404 aChucker << static_cast < sal_Int16 > (0); 405 406 Sequence < sal_Int8 > aSequence( (sal_Int8*)sUTF8Name.getStr(), sUTF8Name.getLength() ); 407 aChucker.WriteBytes( aSequence ); 408 409 return LOCHDR + nNameLength; 410 } 411 sal_uInt32 ZipOutputStream::getCurrentDosTime( ) 412 { 413 oslDateTime aDateTime; 414 TimeValue aTimeValue; 415 osl_getSystemTime ( &aTimeValue ); 416 osl_getDateTimeFromTimeValue( &aTimeValue, &aDateTime); 417 418 sal_uInt32 nYear = static_cast <sal_uInt32> (aDateTime.Year); 419 420 if (nYear>1980) 421 nYear-=1980; 422 else if (nYear>80) 423 nYear-=80; 424 sal_uInt32 nResult = static_cast < sal_uInt32>( ( ( ( aDateTime.Day) + 425 ( 32 * (aDateTime.Month)) + 426 ( 512 * nYear ) ) << 16) | 427 ( ( aDateTime.Seconds/2) + 428 ( 32 * aDateTime.Minutes) + 429 ( 2048 * static_cast <sal_uInt32 > (aDateTime.Hours) ) ) ); 430 return nResult; 431 } 432 /* 433 434 This is actually never used, so I removed it, but thought that the 435 implementation details may be useful in the future...mtg 20010307 436 437 I stopped using the time library and used the OSL version instead, but 438 it might still be useful to have this code here.. 439 440 void ZipOutputStream::dosDateToTMDate ( tm &rTime, sal_uInt32 nDosDate) 441 { 442 sal_uInt32 nDate = static_cast < sal_uInt32 > (nDosDate >> 16); 443 rTime.tm_mday = static_cast < sal_uInt32 > ( nDate & 0x1F); 444 rTime.tm_mon = static_cast < sal_uInt32 > ( ( ( (nDate) & 0x1E0)/0x20)-1); 445 rTime.tm_year = static_cast < sal_uInt32 > ( ( (nDate & 0x0FE00)/0x0200)+1980); 446 447 rTime.tm_hour = static_cast < sal_uInt32 > ( (nDosDate & 0xF800)/0x800); 448 rTime.tm_min = static_cast < sal_uInt32 > ( (nDosDate & 0x7E0)/0x20); 449 rTime.tm_sec = static_cast < sal_uInt32 > ( 2 * (nDosDate & 0x1F) ); 450 } 451 */ 452 453