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