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 #include <osl/diagnose.h>
27 
28 #include <comphelper/storagehelper.hxx>
29 #include <switchpersistencestream.hxx>
30 
31 using namespace ::com::sun::star;
32 
33 // ========================================================================
34 struct SPStreamData_Impl
35 {
36 	uno::Reference< lang::XMultiServiceFactory > m_xFactory;
37 
38 	sal_Bool m_bInStreamBased;
39 
40 	// the streams below are not visible from outside so there is no need to remember position
41 
42 	// original stream related members
43 	uno::Reference< io::XStream > m_xOrigStream;
44 	uno::Reference< io::XTruncate > m_xOrigTruncate;
45 	uno::Reference< io::XSeekable > m_xOrigSeekable;
46 	uno::Reference< io::XInputStream > m_xOrigInStream;
47 	uno::Reference< io::XOutputStream > m_xOrigOutStream;
48 
49 	sal_Bool m_bInOpen;
50 	sal_Bool m_bOutOpen;
51 
52 
SPStreamData_ImplSPStreamData_Impl53 	SPStreamData_Impl(
54 			const uno::Reference< lang::XMultiServiceFactory >& xFactory,
55 			sal_Bool bInStreamBased,
56 			const uno::Reference< io::XStream >& xOrigStream,
57 			const uno::Reference< io::XTruncate >& xOrigTruncate,
58 			const uno::Reference< io::XSeekable >& xOrigSeekable,
59 			const uno::Reference< io::XInputStream >& xOrigInStream,
60 			const uno::Reference< io::XOutputStream >& xOrigOutStream,
61 			sal_Bool bInOpen,
62 			sal_Bool bOutOpen )
63 	: m_xFactory( xFactory )
64 	, m_bInStreamBased( bInStreamBased )
65 	, m_xOrigStream( xOrigStream )
66 	, m_xOrigTruncate( xOrigTruncate )
67 	, m_xOrigSeekable( xOrigSeekable )
68 	, m_xOrigInStream( xOrigInStream )
69 	, m_xOrigOutStream( xOrigOutStream )
70 	, m_bInOpen( bInOpen )
71 	, m_bOutOpen( bOutOpen )
72 	{
73 	}
74 };
75 
76 // ========================================================================
77 // ------------------------------------------------------------------------
SwitchablePersistenceStream(const uno::Reference<lang::XMultiServiceFactory> & xFactory,const uno::Reference<io::XStream> & xStream)78 SwitchablePersistenceStream::SwitchablePersistenceStream(
79 		const uno::Reference< lang::XMultiServiceFactory >& xFactory,
80 		const uno::Reference< io::XStream >& xStream )
81 : m_xFactory( xFactory )
82 , m_pStreamData( NULL )
83 {
84 	SwitchPersistenceTo( xStream );
85 }
86 
87 // ------------------------------------------------------------------------
SwitchablePersistenceStream(const uno::Reference<lang::XMultiServiceFactory> & xFactory,const uno::Reference<io::XInputStream> & xInputStream)88 SwitchablePersistenceStream::SwitchablePersistenceStream(
89 		const uno::Reference< lang::XMultiServiceFactory >& xFactory,
90 		const uno::Reference< io::XInputStream >& xInputStream )
91 : m_xFactory( xFactory )
92 , m_pStreamData( NULL )
93 {
94 	SwitchPersistenceTo( xInputStream );
95 }
96 
97 // ------------------------------------------------------------------------
~SwitchablePersistenceStream()98 SwitchablePersistenceStream::~SwitchablePersistenceStream()
99 {
100 	CloseAll_Impl();
101 }
102 
103 // ------------------------------------------------------------------------
SwitchPersistenceTo(const uno::Reference<io::XStream> & xStream)104 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
105 {
106 	uno::Reference< io::XTruncate > xNewTruncate( xStream, uno::UNO_QUERY_THROW );
107 	uno::Reference< io::XSeekable > xNewSeekable( xStream, uno::UNO_QUERY_THROW );
108 	uno::Reference< io::XInputStream > xNewInStream = xStream->getInputStream();
109 	uno::Reference< io::XOutputStream > xNewOutStream = xStream->getOutputStream();
110 	if ( !xNewInStream.is() || !xNewOutStream.is() )
111 		throw uno::RuntimeException();
112 
113 	sal_Int64 nPos = 0;
114 	sal_Bool bInOpen = sal_False;
115 	sal_Bool bOutOpen = sal_False;
116 
117 	if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
118 	{
119 		// check that the length is the same
120 		if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
121 			throw uno::RuntimeException();
122 
123 		// get the current position
124 		nPos = m_pStreamData->m_xOrigSeekable->getPosition();
125 		bInOpen = m_pStreamData->m_bInOpen;
126 		bOutOpen = m_pStreamData->m_bOutOpen;
127 	}
128 
129 	xNewSeekable->seek( nPos );
130 
131 	CloseAll_Impl();
132 
133 	m_pStreamData = new SPStreamData_Impl( m_xFactory, sal_False,
134 											xStream, xNewTruncate, xNewSeekable, xNewInStream, xNewOutStream,
135 											bInOpen, bOutOpen );
136 }
137 
138 // ------------------------------------------------------------------------
SwitchPersistenceTo(const uno::Reference<io::XInputStream> & xInputStream)139 void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XInputStream >& xInputStream )
140 {
141 	uno::Reference< io::XStream > xNewStream;
142 	uno::Reference< io::XTruncate > xNewTruncate;
143 	uno::Reference< io::XSeekable > xNewSeekable( xInputStream, uno::UNO_QUERY_THROW );
144 	uno::Reference< io::XOutputStream > xNewOutStream;
145 	if ( !xInputStream.is() )
146 		throw uno::RuntimeException();
147 
148 	sal_Int64 nPos = 0;
149 	sal_Bool bInOpen = sal_False;
150 	sal_Bool bOutOpen = sal_False;
151 
152 	if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
153 	{
154 		// check that the length is the same
155 		if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
156 			throw uno::RuntimeException();
157 
158 		// get the current position
159 		nPos = m_pStreamData->m_xOrigSeekable->getPosition();
160 		bInOpen = m_pStreamData->m_bInOpen;
161 		bOutOpen = m_pStreamData->m_bOutOpen;
162 	}
163 
164 	xNewSeekable->seek( nPos );
165 
166 	CloseAll_Impl();
167 
168 	m_pStreamData = new SPStreamData_Impl( m_xFactory, sal_True,
169 											xNewStream, xNewTruncate, xNewSeekable, xInputStream, xNewOutStream,
170 											bInOpen, bOutOpen );
171 
172 }
173 
174 // ------------------------------------------------------------------------
CopyAndSwitchPersistenceTo(const uno::Reference<io::XStream> & xStream)175 void SwitchablePersistenceStream::CopyAndSwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
176 {
177 	uno::Reference< io::XStream > xTargetStream = xStream;
178 	uno::Reference< io::XSeekable > xTargetSeek;
179 
180 	if ( !xTargetStream.is() )
181 	{
182 		xTargetStream = uno::Reference < io::XStream >(
183 			m_xFactory->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.io.TempFile" ) ),
184 			uno::UNO_QUERY_THROW );
185 
186 		xTargetSeek = uno::Reference< io::XSeekable >( xTargetStream, uno::UNO_QUERY_THROW );
187 	}
188 	else
189 	{
190 		// the provided stream must be empty
191 		xTargetSeek = uno::Reference< io::XSeekable >( xTargetStream, uno::UNO_QUERY_THROW );
192 		if ( xTargetSeek->getLength() )
193 			throw io::IOException();
194 	}
195 
196 	uno::Reference< io::XTruncate > xTargetTruncate( xTargetStream, uno::UNO_QUERY_THROW );
197 	uno::Reference< io::XInputStream > xTargetInStream = xTargetStream->getInputStream();
198 	uno::Reference< io::XOutputStream > xTargetOutStream = xTargetStream->getOutputStream();
199 	if ( !xTargetInStream.is() || !xTargetOutStream.is() )
200 		throw uno::RuntimeException();
201 
202 	if ( !m_pStreamData->m_xOrigInStream.is() || !m_pStreamData->m_xOrigSeekable.is() )
203 		throw uno::RuntimeException();
204 
205 	sal_Int64 nPos = m_pStreamData->m_xOrigSeekable->getPosition();
206 	m_pStreamData->m_xOrigSeekable->seek( 0 );
207 	::comphelper::OStorageHelper::CopyInputToOutput( m_pStreamData->m_xOrigInStream, xTargetOutStream );
208 	xTargetOutStream->flush();
209 	xTargetSeek->seek( nPos );
210 
211 	sal_Bool bInOpen = m_pStreamData->m_bInOpen;
212 	sal_Bool bOutOpen = m_pStreamData->m_bOutOpen;
213 
214 	CloseAll_Impl();
215 
216 	m_pStreamData = new SPStreamData_Impl( m_xFactory, sal_False,
217 										xTargetStream, xTargetTruncate, xTargetSeek, xTargetInStream, xTargetOutStream,
218 										bInOpen, bOutOpen );
219 }
220 
221 // ------------------------------------------------------------------------
CloseAll_Impl()222 void SwitchablePersistenceStream::CloseAll_Impl()
223 {
224 	if ( m_pStreamData )
225 	{
226 		delete m_pStreamData;
227 		m_pStreamData = NULL;
228 	}
229 }
230 
231 // com::sun::star::io::XStream
232 // ------------------------------------------------------------------------
getInputStream()233 uno::Reference< io::XInputStream > SAL_CALL SwitchablePersistenceStream::getInputStream(  )
234 	throw (uno::RuntimeException)
235 {
236 	::osl::MutexGuard aGuard( m_aMutex );
237 
238     if ( m_pStreamData )
239         m_pStreamData->m_bInOpen = sal_True;
240 	return static_cast< io::XInputStream* >( this );
241 }
242 
243 
244 // ------------------------------------------------------------------------
getOutputStream()245 uno::Reference< io::XOutputStream > SAL_CALL SwitchablePersistenceStream::getOutputStream(  )
246 	throw (uno::RuntimeException)
247 {
248 	::osl::MutexGuard aGuard( m_aMutex );
249 
250     if ( m_pStreamData )
251         m_pStreamData->m_bOutOpen = sal_True;
252 	return static_cast< io::XOutputStream* >( this );
253 }
254 
255 
256 
257 // com::sun::star::io::XInputStream
258 // ------------------------------------------------------------------------
readBytes(uno::Sequence<::sal_Int8> & aData,::sal_Int32 nBytesToRead)259 ::sal_Int32 SAL_CALL SwitchablePersistenceStream::readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead )
260 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
261 {
262 	::osl::MutexGuard aGuard( m_aMutex );
263 
264 	if ( !m_pStreamData )
265 		throw io::NotConnectedException();
266 
267 	// the original stream data should be provided
268 	if ( !m_pStreamData->m_xOrigInStream.is() )
269 		throw uno::RuntimeException();
270 
271 	return m_pStreamData->m_xOrigInStream->readBytes( aData, nBytesToRead );
272 }
273 
274 
275 // ------------------------------------------------------------------------
readSomeBytes(uno::Sequence<::sal_Int8> & aData,::sal_Int32 nMaxBytesToRead)276 ::sal_Int32 SAL_CALL SwitchablePersistenceStream::readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead )
277 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
278 {
279 	::osl::MutexGuard aGuard( m_aMutex );
280 
281 	if ( !m_pStreamData )
282 		throw io::NotConnectedException();
283 
284 	// the original stream data should be provided
285 	if ( !m_pStreamData->m_xOrigInStream.is() )
286 		throw uno::RuntimeException();
287 
288 	return m_pStreamData->m_xOrigInStream->readBytes( aData, nMaxBytesToRead );
289 }
290 
291 // ------------------------------------------------------------------------
skipBytes(::sal_Int32 nBytesToSkip)292 void SAL_CALL SwitchablePersistenceStream::skipBytes( ::sal_Int32 nBytesToSkip )
293 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
294 {
295 	::osl::MutexGuard aGuard( m_aMutex );
296 
297 	if ( !m_pStreamData )
298 		throw io::NotConnectedException();
299 
300 	// the original stream data should be provided
301 	if ( !m_pStreamData->m_xOrigInStream.is() )
302 		throw uno::RuntimeException();
303 
304 	m_pStreamData->m_xOrigInStream->skipBytes( nBytesToSkip );
305 }
306 
307 
308 // ------------------------------------------------------------------------
available()309 ::sal_Int32 SAL_CALL SwitchablePersistenceStream::available(  )
310 	throw (io::NotConnectedException, io::IOException, uno::RuntimeException)
311 {
312 	::osl::MutexGuard aGuard( m_aMutex );
313 
314 	if ( !m_pStreamData )
315 		throw io::NotConnectedException();
316 
317 	// the original stream data should be provided
318 	if ( !m_pStreamData->m_xOrigInStream.is() )
319 		throw uno::RuntimeException();
320 
321 	return m_pStreamData->m_xOrigInStream->available();
322 }
323 
324 
325 // ------------------------------------------------------------------------
closeInput()326 void SAL_CALL SwitchablePersistenceStream::closeInput()
327 	throw (io::NotConnectedException, io::IOException, uno::RuntimeException)
328 {
329 	::osl::MutexGuard aGuard( m_aMutex );
330 
331 	if ( !m_pStreamData )
332 		throw io::NotConnectedException();
333 
334 	m_pStreamData->m_bInOpen = sal_False;
335 	if ( !m_pStreamData->m_bOutOpen )
336 		CloseAll_Impl();
337 }
338 
339 
340 
341 // com::sun::star::io::XOutputStream
342 // ------------------------------------------------------------------------
writeBytes(const uno::Sequence<::sal_Int8> & aData)343 void SAL_CALL SwitchablePersistenceStream::writeBytes( const uno::Sequence< ::sal_Int8 >& aData )
344 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
345 {
346 	::osl::MutexGuard aGuard( m_aMutex );
347 
348 	if ( !m_pStreamData )
349 		throw io::NotConnectedException();
350 
351 	if ( m_pStreamData->m_bInStreamBased )
352 		throw io::IOException();
353 
354 	// the original stream data should be provided
355 	if ( !m_pStreamData->m_xOrigOutStream.is() )
356 		throw uno::RuntimeException();
357 
358 	m_pStreamData->m_xOrigOutStream->writeBytes( aData );
359 }
360 
361 
362 // ------------------------------------------------------------------------
flush()363 void SAL_CALL SwitchablePersistenceStream::flush(  )
364 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
365 {
366 	::osl::MutexGuard aGuard( m_aMutex );
367 
368 	if ( !m_pStreamData || m_pStreamData->m_bInStreamBased )
369 	{
370 		OSL_ENSURE( sal_False, "flush() is not acceptable!\n" );
371 		return;
372 		// in future throw exception, for now some code might call flush() on closed stream
373 		// since file ucp implementation allows it
374 		// throw io::NotConnectedException();
375 	}
376 
377 	// the original stream data should be provided
378 	if ( !m_pStreamData->m_xOrigOutStream.is() )
379 		throw uno::RuntimeException();
380 
381 	m_pStreamData->m_xOrigOutStream->flush();
382 }
383 
384 
385 // ------------------------------------------------------------------------
closeOutput()386 void SAL_CALL SwitchablePersistenceStream::closeOutput(  )
387 	throw (io::NotConnectedException, io::BufferSizeExceededException, io::IOException, uno::RuntimeException)
388 {
389 	::osl::MutexGuard aGuard( m_aMutex );
390 
391 	if ( !m_pStreamData )
392 		throw io::NotConnectedException();
393 
394 	m_pStreamData->m_bOutOpen = sal_False;
395 	if ( !m_pStreamData->m_bInOpen )
396 		CloseAll_Impl();
397 }
398 
399 
400 
401 // com::sun::star::io::XTruncate
402 // ------------------------------------------------------------------------
truncate()403 void SAL_CALL SwitchablePersistenceStream::truncate(  )
404 	throw (io::IOException, uno::RuntimeException)
405 {
406 	::osl::MutexGuard aGuard( m_aMutex );
407 
408 	if ( !m_pStreamData )
409 		throw io::NotConnectedException();
410 
411 	if ( m_pStreamData->m_bInStreamBased )
412 		throw io::IOException();
413 
414 	// the original stream data should be provided
415 	if ( !m_pStreamData->m_xOrigTruncate.is() )
416 		throw uno::RuntimeException();
417 
418 	m_pStreamData->m_xOrigTruncate->truncate();
419 }
420 
421 
422 // com::sun::star::io::XSeekable
423 // ------------------------------------------------------------------------
seek(::sal_Int64 location)424 void SAL_CALL SwitchablePersistenceStream::seek( ::sal_Int64 location )
425 	throw (lang::IllegalArgumentException, io::IOException, uno::RuntimeException)
426 {
427 	::osl::MutexGuard aGuard( m_aMutex );
428 
429 	if ( !m_pStreamData )
430 		throw io::NotConnectedException();
431 
432 	// the original stream data should be provided
433 	if ( !m_pStreamData->m_xOrigSeekable.is() )
434 		throw uno::RuntimeException();
435 
436 	m_pStreamData->m_xOrigSeekable->seek( location );
437 }
438 
439 
440 // ------------------------------------------------------------------------
getPosition()441 ::sal_Int64 SAL_CALL SwitchablePersistenceStream::getPosition(  )
442 	throw (io::IOException, uno::RuntimeException)
443 {
444 	::osl::MutexGuard aGuard( m_aMutex );
445 
446 	if ( !m_pStreamData )
447 		throw io::NotConnectedException();
448 
449 	// the original stream data should be provided
450 	if ( !m_pStreamData->m_xOrigSeekable.is() )
451 		throw uno::RuntimeException();
452 
453 	return m_pStreamData->m_xOrigSeekable->getPosition();
454 }
455 
456 
457 // ------------------------------------------------------------------------
getLength()458 ::sal_Int64 SAL_CALL SwitchablePersistenceStream::getLength(  )
459 	throw (io::IOException, uno::RuntimeException)
460 {
461 	::osl::MutexGuard aGuard( m_aMutex );
462 
463 	if ( !m_pStreamData )
464 		throw io::NotConnectedException();
465 
466 	// the original stream data should be provided
467 	if ( !m_pStreamData->m_xOrigSeekable.is() )
468 		throw uno::RuntimeException();
469 
470 	return m_pStreamData->m_xOrigSeekable->getLength();
471 }
472 
473 // ------------------------------------------------------------------------
waitForCompletion()474 void SAL_CALL SwitchablePersistenceStream::waitForCompletion()
475 	throw (::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException)
476 {
477 	if ( !m_pStreamData )
478 		throw io::NotConnectedException();
479 
480 	uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( m_pStreamData->m_xOrigOutStream, uno::UNO_QUERY );
481 	if ( asyncOutputMonitor.is() )
482 		asyncOutputMonitor->waitForCompletion();
483 }
484 
485