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 .
20 #include <com/sun/star/packages/zip/ZipConstants.hpp>
21 #include <com/sun/star/packages/zip/ZipIOException.hpp>
22 #include <com/sun/star/xml/crypto/CipherID.hpp>
24 #include <XUnbufferedStream.hxx>
25 #include <EncryptionData.hxx>
26 #include <PackageConstants.hxx>
27 #include <ZipFile.hxx>
28 #include <EncryptedDataHeader.hxx>
32 #include <osl/diagnose.h>
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
,
50 const OUString
& aMediaType
,
52 : maMutexHolder( aMutexHolder
.Is() ? aMutexHolder
: SotMutexHolderRef( new SotMutexHolder
) )
53 , mxZipStream ( xNewZipStream
)
54 , mxZipSeek ( xNewZipStream
, UNO_QUERY
)
59 , mbRawStream ( nStreamMode
== UNBUFF_STREAM_RAW
|| nStreamMode
== UNBUFF_STREAM_WRAPPEDRAW
)
60 , mbWrappedRaw ( nStreamMode
== UNBUFF_STREAM_WRAPPEDRAW
)
61 , mbFinished ( 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("The stream seems to be broken!");
84 bool bHaveEncryptData
= rData
.is() && rData
->m_aSalt
.getLength() && rData
->m_aInitVector
.getLength() && rData
->m_nIterationCount
!= 0;
85 bool bMustDecrypt
= nStreamMode
== UNBUFF_STREAM_DATA
&& bHaveEncryptData
&& bIsEncrypted
;
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 ( true )
122 , mbRawStream ( false )
123 , mbWrappedRaw ( false )
124 , mbFinished ( false )
125 , mnHeaderToRead ( 0 )
130 , mbCheckCRC( 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
, std::exception
)
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 than requested!" );
235 if ( maInflater
.finished() || maInflater
.getLastInflateError() )
236 throw ZipIOException("The stream seems to be broken!" );
238 if ( maInflater
.needsDictionary() )
239 throw ZipIOException("Dictionaries are not supported!" );
241 sal_Int32 nDiff
= static_cast< sal_Int32
>( mnZipEnd
- mnZipCurrent
);
244 mxZipSeek
->seek ( mnZipCurrent
);
246 sal_Int32 nToRead
= std::max( nRequestedBytes
, static_cast< sal_Int32
>( 8192 ) );
247 if ( mnBlockSize
> 1 )
248 nToRead
= nToRead
+ mnBlockSize
- nToRead
% mnBlockSize
;
249 nToRead
= std::min( nDiff
, nToRead
);
251 sal_Int32 nZipRead
= mxZipStream
->readBytes( maCompBuffer
, nToRead
);
252 if ( nZipRead
< nToRead
)
253 throw ZipIOException("No expected data!" );
255 mnZipCurrent
+= nZipRead
;
256 // maCompBuffer now has the data, check if we need to decrypt
257 // before passing to the Inflater
258 if ( m_xCipherContext
.is() )
261 maCRC
.update( maCompBuffer
);
263 maCompBuffer
= m_xCipherContext
->convertWithCipherContext( maCompBuffer
);
264 if ( mnZipCurrent
== mnZipEnd
)
266 uno::Sequence
< sal_Int8
> aSuffix
= m_xCipherContext
->finalizeCipherContextAndDispose();
267 if ( aSuffix
.getLength() )
269 sal_Int32 nOldLen
= maCompBuffer
.getLength();
270 maCompBuffer
.realloc( nOldLen
+ aSuffix
.getLength() );
271 memcpy( maCompBuffer
.getArray() + nOldLen
, aSuffix
.getConstArray(), aSuffix
.getLength() );
275 maInflater
.setInput ( maCompBuffer
);
279 throw ZipIOException("The stream seems to be broken!" );
284 mnMyCurrent
+= nRead
+ nLastRead
;
285 nTotal
= nRead
+ nLastRead
;
286 if ( nTotal
< nRequestedBytes
)
287 aData
.realloc ( nTotal
);
289 if ( mbCheckCRC
&& ( !mbRawStream
|| mbWrappedRaw
) )
291 if ( !m_xCipherContext
.is() && !mbWrappedRaw
)
292 maCRC
.update( aData
);
294 if ( mnZipSize
+ maHeader
.getLength() == mnMyCurrent
&& maCRC
.getValue() != maEntry
.nCrc
)
295 throw ZipIOException("The stream seems to be broken!" );
302 sal_Int32 SAL_CALL
XUnbufferedStream::readSomeBytes( Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
303 throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
)
305 return readBytes ( aData
, nMaxBytesToRead
);
307 void SAL_CALL
XUnbufferedStream::skipBytes( sal_Int32 nBytesToSkip
)
308 throw( NotConnectedException
, BufferSizeExceededException
, IOException
, RuntimeException
, std::exception
)
312 Sequence
< sal_Int8
> aSequence ( nBytesToSkip
);
313 readBytes ( aSequence
, nBytesToSkip
);
317 sal_Int32 SAL_CALL
XUnbufferedStream::available( )
318 throw( NotConnectedException
, IOException
, RuntimeException
, std::exception
)
320 return static_cast < sal_Int32
> ( mnZipSize
- mnMyCurrent
);
323 void SAL_CALL
XUnbufferedStream::closeInput( )
324 throw( NotConnectedException
, IOException
, RuntimeException
, std::exception
)
328 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */