1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <com/sun/star/packages/zip/ZipConstants.hpp>
22 #include <com/sun/star/packages/zip/ZipIOException.hpp>
23 #include <com/sun/star/xml/crypto/CipherID.hpp>
25 #include <XUnbufferedStream.hxx>
26 #include <EncryptionData.hxx>
27 #include <PackageConstants.hxx>
28 #include <ZipFile.hxx>
29 #include <EncryptedDataHeader.hxx>
33 #include <osl/mutex.hxx>
35 using namespace ::com::sun::star
;
36 using namespace com::sun::star::packages::zip::ZipConstants
;
37 using namespace com::sun::star::io
;
38 using namespace com::sun::star::uno
;
39 using com::sun::star::lang::IllegalArgumentException
;
40 using com::sun::star::packages::zip::ZipIOException
;
42 XUnbufferedStream::XUnbufferedStream(
43 const uno::Reference
< uno::XComponentContext
>& xContext
,
44 SotMutexHolderRef aMutexHolder
,
46 Reference
< XInputStream
> xNewZipStream
,
47 const ::rtl::Reference
< EncryptionData
>& rData
,
49 sal_Bool bIsEncrypted
,
50 const OUString
& aMediaType
,
51 sal_Bool bRecoveryMode
)
52 : maMutexHolder( aMutexHolder
.Is() ? aMutexHolder
: SotMutexHolderRef( new SotMutexHolder
) )
53 , mxZipStream ( xNewZipStream
)
54 , mxZipSeek ( xNewZipStream
, UNO_QUERY
)
58 , maInflater ( sal_True
)
59 , mbRawStream ( nStreamMode
== UNBUFF_STREAM_RAW
|| nStreamMode
== UNBUFF_STREAM_WRAPPEDRAW
)
60 , mbWrappedRaw ( nStreamMode
== UNBUFF_STREAM_WRAPPEDRAW
)
61 , mbFinished ( sal_False
)
62 , mnHeaderToRead ( 0 )
67 , mbCheckCRC( !bRecoveryMode
)
69 mnZipCurrent
= maEntry
.nOffset
;
72 mnZipSize
= maEntry
.nMethod
== DEFLATED
? maEntry
.nCompressedSize
: maEntry
.nSize
;
73 mnZipEnd
= maEntry
.nOffset
+ mnZipSize
;
77 mnZipSize
= maEntry
.nSize
;
78 mnZipEnd
= maEntry
.nMethod
== DEFLATED
? maEntry
.nOffset
+ maEntry
.nCompressedSize
: maEntry
.nOffset
+ maEntry
.nSize
;
82 throw ZipIOException(OUString("The stream seems to be broken!"), uno::Reference
< XInterface
>());
84 sal_Bool bHaveEncryptData
= ( rData
.is() && rData
->m_aSalt
.getLength() && rData
->m_aInitVector
.getLength() && rData
->m_nIterationCount
!= 0 ) ? sal_True
: sal_False
;
85 sal_Bool bMustDecrypt
= ( nStreamMode
== UNBUFF_STREAM_DATA
&& bHaveEncryptData
&& bIsEncrypted
) ? sal_True
: sal_False
;
89 m_xCipherContext
= ZipFile::StaticGetCipher( xContext
, rData
, false );
90 mnBlockSize
= ( rData
->m_nEncAlg
== xml::crypto::CipherID::AES_CBC_W3C_PADDING
? 16 : 1 );
93 if ( bHaveEncryptData
&& mbWrappedRaw
&& bIsEncrypted
)
95 // if we have the data needed to decrypt it, but didn't want it decrypted (or
96 // we couldn't decrypt it due to wrong password), then we prepend this
99 // Make a buffer big enough to hold both the header and the data itself
100 maHeader
.realloc ( n_ConstHeaderSize
+
101 rData
->m_aInitVector
.getLength() +
102 rData
->m_aSalt
.getLength() +
103 rData
->m_aDigest
.getLength() +
104 aMediaType
.getLength() * sizeof( sal_Unicode
) );
105 sal_Int8
* pHeader
= maHeader
.getArray();
106 ZipFile::StaticFillHeader( rData
, rEntry
.nSize
, aMediaType
, pHeader
);
107 mnHeaderToRead
= static_cast < sal_Int16
> ( maHeader
.getLength() );
111 // allows to read package raw stream
112 XUnbufferedStream::XUnbufferedStream(
113 const uno::Reference
< uno::XComponentContext
>& /*xContext*/,
114 const Reference
< XInputStream
>& xRawStream
,
115 const ::rtl::Reference
< EncryptionData
>& rData
)
116 : maMutexHolder( new SotMutexHolder
)
117 , mxZipStream ( xRawStream
)
118 , mxZipSeek ( xRawStream
, UNO_QUERY
)
121 , maInflater ( sal_True
)
122 , mbRawStream ( sal_False
)
123 , mbWrappedRaw ( sal_False
)
124 , mbFinished ( sal_False
)
125 , mnHeaderToRead ( 0 )
130 , mbCheckCRC( sal_False
)
132 // for this scenario maEntry is not set !!!
133 OSL_ENSURE( mxZipSeek
.is(), "The stream must be seekable!\n" );
135 // skip raw header, it must be already parsed to rData
136 mnZipCurrent
= n_ConstHeaderSize
+ rData
->m_aInitVector
.getLength() +
137 rData
->m_aSalt
.getLength() + rData
->m_aDigest
.getLength();
140 if ( mxZipSeek
.is() )
141 mnZipSize
= mxZipSeek
->getLength();
142 } catch( Exception
& e
)
144 // in case of problem the size will stay set to 0
145 SAL_WARN("package", "ignoring Exception " + e
.Message
);
148 mnZipEnd
= mnZipCurrent
+ mnZipSize
;
150 // the raw data will not be decrypted, no need for the cipher
151 // m_xCipherContext = ZipFile::StaticGetCipher( xContext, rData, false );
154 XUnbufferedStream::~XUnbufferedStream()
158 sal_Int32 SAL_CALL
XUnbufferedStream::readBytes( Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
)
159 throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
)
161 ::osl::MutexGuard
aGuard( maMutexHolder
->GetMutex() );
163 sal_Int32 nRequestedBytes
= nBytesToRead
;
164 OSL_ENSURE( !mnHeaderToRead
|| mbWrappedRaw
, "Only encrypted raw stream can be provided with header!" );
165 if ( mnMyCurrent
+ nRequestedBytes
> mnZipSize
+ maHeader
.getLength() )
166 nRequestedBytes
= static_cast < sal_Int32
> ( mnZipSize
+ maHeader
.getLength() - mnMyCurrent
);
168 sal_Int32 nTotal
= 0;
169 aData
.realloc ( nRequestedBytes
);
170 if ( nRequestedBytes
)
173 sal_Int32 nLastRead
= 0;
176 sal_Int64 nDiff
= mnZipEnd
- mnZipCurrent
;
178 if ( mbWrappedRaw
&& mnHeaderToRead
)
180 sal_Int16 nHeadRead
= static_cast< sal_Int16
>(( nRequestedBytes
> mnHeaderToRead
?
181 mnHeaderToRead
: nRequestedBytes
));
182 memcpy ( aData
.getArray(), maHeader
.getConstArray() + maHeader
.getLength() - mnHeaderToRead
, nHeadRead
);
183 mnHeaderToRead
= mnHeaderToRead
- nHeadRead
;
185 if ( nHeadRead
< nRequestedBytes
)
187 sal_Int32 nToRead
= nRequestedBytes
- nHeadRead
;
188 nToRead
= ( nDiff
< nToRead
) ? sal::static_int_cast
< sal_Int32
>( nDiff
) : nToRead
;
190 Sequence
< sal_Int8
> aPureData( nToRead
);
191 mxZipSeek
->seek ( mnZipCurrent
);
192 nRead
= mxZipStream
->readBytes ( aPureData
, nToRead
);
193 mnZipCurrent
+= nRead
;
195 aPureData
.realloc( nRead
);
197 maCRC
.update( aPureData
);
199 aData
.realloc( nHeadRead
+ nRead
);
201 sal_Int8
* pPureBuffer
= aPureData
.getArray();
202 sal_Int8
* pBuffer
= aData
.getArray();
203 for ( sal_Int32 nInd
= 0; nInd
< nRead
; nInd
++ )
204 pBuffer
[ nHeadRead
+ nInd
] = pPureBuffer
[ nInd
];
211 mxZipSeek
->seek ( mnZipCurrent
);
213 nRead
= mxZipStream
->readBytes (
215 static_cast < sal_Int32
> ( nDiff
< nRequestedBytes
? nDiff
: nRequestedBytes
) );
217 mnZipCurrent
+= nRead
;
219 aData
.realloc( nRead
);
220 if ( mbWrappedRaw
&& mbCheckCRC
)
221 maCRC
.update( aData
);
226 while ( 0 == ( nLastRead
= maInflater
.doInflateSegment( aData
, nRead
, aData
.getLength() - nRead
) ) ||
227 ( nRead
+ nLastRead
!= nRequestedBytes
&& mnZipCurrent
< mnZipEnd
) )
231 if ( nRead
> nRequestedBytes
)
232 throw RuntimeException(
233 "Should not be possible to read more then requested!",
234 Reference
< XInterface
>() );
236 if ( maInflater
.finished() || maInflater
.getLastInflateError() )
237 throw ZipIOException("The stream seems to be broken!",
238 Reference
< XInterface
>() );
240 if ( maInflater
.needsDictionary() )
241 throw ZipIOException("Dictionaries are not supported!",
242 Reference
< XInterface
>() );
244 sal_Int32 nDiff
= static_cast< sal_Int32
>( mnZipEnd
- mnZipCurrent
);
247 mxZipSeek
->seek ( mnZipCurrent
);
249 sal_Int32 nToRead
= std::max( nRequestedBytes
, static_cast< sal_Int32
>( 8192 ) );
250 if ( mnBlockSize
> 1 )
251 nToRead
= nToRead
+ mnBlockSize
- nToRead
% mnBlockSize
;
252 nToRead
= std::min( nDiff
, nToRead
);
254 sal_Int32 nZipRead
= mxZipStream
->readBytes( maCompBuffer
, nToRead
);
255 if ( nZipRead
< nToRead
)
256 throw ZipIOException("No expected data!",
257 Reference
< XInterface
>() );
259 mnZipCurrent
+= nZipRead
;
260 // maCompBuffer now has the data, check if we need to decrypt
261 // before passing to the Inflater
262 if ( m_xCipherContext
.is() )
265 maCRC
.update( maCompBuffer
);
267 maCompBuffer
= m_xCipherContext
->convertWithCipherContext( maCompBuffer
);
268 if ( mnZipCurrent
== mnZipEnd
)
270 uno::Sequence
< sal_Int8
> aSuffix
= m_xCipherContext
->finalizeCipherContextAndDispose();
271 if ( aSuffix
.getLength() )
273 sal_Int32 nOldLen
= maCompBuffer
.getLength();
274 maCompBuffer
.realloc( nOldLen
+ aSuffix
.getLength() );
275 memcpy( maCompBuffer
.getArray() + nOldLen
, aSuffix
.getConstArray(), aSuffix
.getLength() );
279 maInflater
.setInput ( maCompBuffer
);
283 throw ZipIOException("The stream seems to be broken!",
284 Reference
< XInterface
>() );
289 mnMyCurrent
+= nRead
+ nLastRead
;
290 nTotal
= nRead
+ nLastRead
;
291 if ( nTotal
< nRequestedBytes
)
292 aData
.realloc ( nTotal
);
294 if ( mbCheckCRC
&& ( !mbRawStream
|| mbWrappedRaw
) )
296 if ( !m_xCipherContext
.is() && !mbWrappedRaw
)
297 maCRC
.update( aData
);
299 if ( mnZipSize
+ maHeader
.getLength() == mnMyCurrent
&& maCRC
.getValue() != maEntry
.nCrc
)
300 throw ZipIOException("The stream seems to be broken!",
301 Reference
< XInterface
>() );
308 sal_Int32 SAL_CALL
XUnbufferedStream::readSomeBytes( Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
309 throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
)
311 return readBytes ( aData
, nMaxBytesToRead
);
313 void SAL_CALL
XUnbufferedStream::skipBytes( sal_Int32 nBytesToSkip
)
314 throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
)
318 Sequence
< sal_Int8
> aSequence ( nBytesToSkip
);
319 readBytes ( aSequence
, nBytesToSkip
);
323 sal_Int32 SAL_CALL
XUnbufferedStream::available( )
324 throw( NotConnectedException
, IOException
, RuntimeException
)
326 return static_cast < sal_Int32
> ( mnZipSize
- mnMyCurrent
);
329 void SAL_CALL
XUnbufferedStream::closeInput( )
330 throw( NotConnectedException
, IOException
, RuntimeException
)
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */