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 #include "precompiled_unotools.hxx"
28 #include <XTempFile.hxx>
29 #include <cppuhelper/factory.hxx>
30 #include <cppuhelper/typeprovider.hxx>
31 #include <unotools/tempfile.hxx>
32 #include <osl/file.hxx>
33 #include <unotools/configmgr.hxx>
34 #include <tools/urlobj.hxx>
35 #include <tools/debug.hxx>
36 
37 namespace css = com::sun::star;
38 
39 // copy define from desktop\source\app\appinit.cxx
40 
41 #define DESKTOP_TEMPNAMEBASE_DIR	"/temp/soffice.tmp"
42 
43 OTempFileService::OTempFileService(::css::uno::Reference< ::css::uno::XComponentContext > const & context)
44 : ::cppu::PropertySetMixin< ::css::io::XTempFile >(
45     context
46     , static_cast< Implements >( IMPLEMENTS_PROPERTY_SET | IMPLEMENTS_FAST_PROPERTY_SET | IMPLEMENTS_PROPERTY_ACCESS )
47     , com::sun::star::uno::Sequence< rtl::OUString >() )
48 , mpStream( NULL )
49 , mbRemoveFile( sal_True )
50 , mbInClosed( sal_False )
51 , mbOutClosed( sal_False )
52 , mnCachedPos( 0 )
53 , mbHasCachedPos( sal_False )
54 
55 {
56 	mpTempFile = new ::utl::TempFile;
57 	mpTempFile->EnableKillingFile ( sal_True );
58 }
59 
60 OTempFileService::~OTempFileService ()
61 {
62 	if ( mpTempFile )
63 		delete mpTempFile;
64 }
65 
66 
67 // XInterface
68 
69 ::css::uno::Any SAL_CALL OTempFileService::queryInterface( ::css::uno::Type const & aType )
70 throw ( ::css::uno::RuntimeException )
71 {
72 	::css::uno::Any aResult( OTempFileBase::queryInterface( aType ) );
73 	if (!aResult.hasValue())
74 		aResult = cppu::PropertySetMixin< ::css::io::XTempFile >::queryInterface( aType ) ;
75 	return aResult;
76 };
77 void SAL_CALL OTempFileService::acquire(  )
78 throw ()
79 {
80 	OTempFileBase::acquire();
81 }
82 void SAL_CALL OTempFileService::release(  )
83 throw ()
84 {
85 	OTempFileBase::release();
86 }
87 
88 //	XTypeProvider
89 
90 ::css::uno::Sequence< ::css::uno::Type > SAL_CALL OTempFileService::getTypes(  )
91 throw ( ::css::uno::RuntimeException )
92 {
93 	static ::cppu::OTypeCollection* pTypeCollection = NULL;
94 	if ( pTypeCollection == NULL )
95 	{
96 		::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ) ;
97 
98 		if ( pTypeCollection == NULL )
99 		{
100 			static ::cppu::OTypeCollection aTypeCollection(
101 				::getCppuType( ( const ::css::uno::Reference< ::css::beans::XPropertySet >*)NULL )
102 				,OTempFileBase::getTypes() );
103 			pTypeCollection = &aTypeCollection;
104 		}
105 	}
106 	return pTypeCollection->getTypes();
107 };
108 ::css::uno::Sequence< sal_Int8 > SAL_CALL OTempFileService::getImplementationId(  )
109 throw ( ::css::uno::RuntimeException )
110 {
111 	return OTempFileBase::getImplementationId();
112 }
113 
114 //	XTempFile
115 
116 sal_Bool SAL_CALL OTempFileService::getRemoveFile()
117 throw ( ::css::uno::RuntimeException )
118 {
119 	::osl::MutexGuard aGuard( maMutex );
120 
121 	if ( !mpTempFile )
122 	{
123 		// the stream is already disconnected
124 		throw ::css::uno::RuntimeException();
125 		}
126 
127 	return mbRemoveFile;
128 };
129 void SAL_CALL OTempFileService::setRemoveFile( sal_Bool _removefile )
130 throw ( ::css::uno::RuntimeException )
131 {
132 	::osl::MutexGuard aGuard( maMutex );
133 
134 	if ( !mpTempFile )
135 	{
136 		// the stream is already disconnected
137 		throw ::css::uno::RuntimeException();
138 	}
139 
140 	mbRemoveFile = _removefile;
141 	mpTempFile->EnableKillingFile( mbRemoveFile );
142 };
143 ::rtl::OUString SAL_CALL OTempFileService::getUri()
144 throw ( ::css::uno::RuntimeException )
145 {
146 	::osl::MutexGuard aGuard( maMutex );
147 
148 	if ( !mpTempFile )
149 	{
150 		throw ::css::uno::RuntimeException();
151 	}
152 
153 	return ::rtl::OUString( mpTempFile->GetURL() );
154 
155 };
156 ::rtl::OUString SAL_CALL OTempFileService::getResourceName()
157 throw ( ::css::uno::RuntimeException )
158 {
159 	::osl::MutexGuard aGuard( maMutex );
160 
161 	if ( !mpTempFile )
162 	{
163 		throw ::css::uno::RuntimeException();
164 }
165 
166 	return ::rtl::OUString( mpTempFile->GetFileName() );
167 };
168 
169 
170 
171 // XInputStream
172 
173 sal_Int32 SAL_CALL OTempFileService::readBytes( ::css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
174 throw (::css::io::NotConnectedException, ::css::io::BufferSizeExceededException, ::css::io::IOException, ::css::uno::RuntimeException )
175 {
176 	::osl::MutexGuard aGuard( maMutex );
177 	if ( mbInClosed )
178 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
179 
180 	checkConnected();
181 	if (nBytesToRead < 0)
182 		throw ::css::io::BufferSizeExceededException( ::rtl::OUString(), static_cast< ::css::uno::XWeak * >(this));
183 
184 	aData.realloc(nBytesToRead);
185 
186 	sal_uInt32 nRead = mpStream->Read(static_cast < void* > ( aData.getArray() ), nBytesToRead);
187 	checkError();
188 
189 	if (nRead < static_cast < sal_uInt32 > ( nBytesToRead ) )
190 		aData.realloc( nRead );
191 
192 	if ( sal::static_int_cast<sal_uInt32>(nBytesToRead) > nRead )
193 	{
194 		// usually that means that the stream was read till the end
195 		// TODO/LATER: it is better to get rid of this optimization by avoiding using of multiple temporary files ( there should be only one temporary file? )
196 		mnCachedPos = mpStream->Tell();
197 		mbHasCachedPos = sal_True;
198 
199 		mpStream = NULL;
200 		if ( mpTempFile )
201 			mpTempFile->CloseStream();
202 	}
203 
204 	return nRead;
205 }
206 sal_Int32 SAL_CALL OTempFileService::readSomeBytes( ::css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
207 throw ( ::css::io::NotConnectedException, ::css::io::BufferSizeExceededException, ::css::io::IOException, ::css::uno::RuntimeException )
208 {
209 	::osl::MutexGuard aGuard( maMutex );
210 	if ( mbInClosed )
211 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
212 
213 	checkConnected();
214 	checkError();
215 
216 	if (nMaxBytesToRead < 0)
217 		throw ::css::io::BufferSizeExceededException( ::rtl::OUString(), static_cast < ::css::uno::XWeak * >( this ) );
218 
219 	if (mpStream->IsEof())
220 	{
221 		aData.realloc(0);
222 		return 0;
223 	}
224 	else
225 		return readBytes(aData, nMaxBytesToRead);
226 }
227 void SAL_CALL OTempFileService::skipBytes( sal_Int32 nBytesToSkip )
228 throw ( ::css::io::NotConnectedException, ::css::io::BufferSizeExceededException, ::css::io::IOException, ::css::uno::RuntimeException )
229 {
230 	::osl::MutexGuard aGuard( maMutex );
231 	if ( mbInClosed )
232 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
233 
234 	checkConnected();
235 	checkError();
236 	mpStream->SeekRel(nBytesToSkip);
237 	checkError();
238 }
239 sal_Int32 SAL_CALL OTempFileService::available(  )
240 throw ( ::css::io::NotConnectedException, ::css::io::IOException, ::css::uno::RuntimeException )
241 {
242 	::osl::MutexGuard aGuard( maMutex );
243 	if ( mbInClosed )
244 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
245 
246 	checkConnected();
247 
248 	sal_uInt32 nPos = mpStream->Tell();
249 	checkError();
250 
251 	mpStream->Seek(STREAM_SEEK_TO_END);
252 	checkError();
253 
254 	sal_Int32 nAvailable = (sal_Int32)mpStream->Tell() - nPos;
255 	mpStream->Seek(nPos);
256 	checkError();
257 
258 	return nAvailable;
259 }
260 void SAL_CALL OTempFileService::closeInput(  )
261 throw ( ::css::io::NotConnectedException, ::css::io::IOException, ::css::uno::RuntimeException )
262 {
263 	::osl::MutexGuard aGuard( maMutex );
264 	if ( mbInClosed )
265 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak  * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
266 
267 	mbInClosed = sal_True;
268 
269 	if ( mbOutClosed )
270 	{
271 		// stream will be deleted by TempFile implementation
272 		mpStream = NULL;
273 
274 		if ( mpTempFile )
275 		{
276 			delete mpTempFile;
277 			mpTempFile = NULL;
278 		}
279 	}
280 }
281 
282 // XOutputStream
283 
284 void SAL_CALL OTempFileService::writeBytes( const ::css::uno::Sequence< sal_Int8 >& aData )
285 throw ( ::css::io::NotConnectedException, ::css::io::BufferSizeExceededException, ::css::io::IOException, ::css::uno::RuntimeException )
286 {
287 	::osl::MutexGuard aGuard( maMutex );
288 	if ( mbOutClosed )
289 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
290 
291 	checkConnected();
292 	sal_uInt32 nWritten = mpStream->Write(aData.getConstArray(),aData.getLength());
293 	checkError();
294 	if	( nWritten != (sal_uInt32)aData.getLength())
295 		throw ::css::io::BufferSizeExceededException( ::rtl::OUString(),static_cast < ::css::uno::XWeak * > ( this ) );
296 }
297 void SAL_CALL OTempFileService::flush(  )
298 throw ( ::css::io::NotConnectedException, ::css::io::BufferSizeExceededException, ::css::io::IOException, ::css::uno::RuntimeException )
299 {
300 	::osl::MutexGuard aGuard( maMutex );
301 	if ( mbOutClosed )
302 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
303 
304 	checkConnected();
305 	mpStream->Flush();
306 	checkError();
307 }
308 void SAL_CALL OTempFileService::closeOutput(  )
309 throw ( ::css::io::NotConnectedException, ::css::io::BufferSizeExceededException, ::css::io::IOException, ::css::uno::RuntimeException )
310 {
311 	::osl::MutexGuard aGuard( maMutex );
312 	if ( mbOutClosed )
313 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
314 
315 	mbOutClosed = sal_True;
316 
317 	// TODO/LATER: it is better to get rid of this optimization by avoiding using of multiple temporary files ( there should be only one temporary file? )
318 	if ( mpStream )
319 	{
320 		mnCachedPos = mpStream->Tell();
321 		mbHasCachedPos = sal_True;
322 
323 		mpStream = NULL;
324 		if ( mpTempFile )
325 			mpTempFile->CloseStream();
326 	}
327 
328 	if ( mbInClosed )
329 	{
330 		// stream will be deleted by TempFile implementation
331 		mpStream = NULL;
332 
333 		if ( mpTempFile )
334 		{
335 			delete mpTempFile;
336 			mpTempFile = NULL;
337 		}
338 	}
339 }
340 
341 
342 void OTempFileService::checkError () const
343 {
344 	if (!mpStream || mpStream->SvStream::GetError () != ERRCODE_NONE )
345 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
346 }
347 void OTempFileService::checkConnected ()
348 {
349 	if (!mpStream && mpTempFile)
350 	{
351 		mpStream = mpTempFile->GetStream( STREAM_STD_READWRITE );
352 		if ( mpStream && mbHasCachedPos )
353 		{
354 			mpStream->Seek( sal::static_int_cast<sal_Size>(mnCachedPos) );
355 			if ( mpStream->SvStream::GetError () == ERRCODE_NONE )
356 			{
357 				mbHasCachedPos = sal_False;
358 				mnCachedPos = 0;
359 			}
360 			else
361 			{
362 				mpStream = NULL;
363 				mpTempFile->CloseStream();
364 			}
365 		}
366 	}
367 
368 	if (!mpStream)
369 		throw ::css::io::NotConnectedException ( ::rtl::OUString(), const_cast < ::css::uno::XWeak * > ( static_cast < const ::css::uno::XWeak * > (this ) ) );
370 }
371 
372 // XSeekable
373 
374 void SAL_CALL OTempFileService::seek( sal_Int64 nLocation )
375 throw ( ::css::lang::IllegalArgumentException, ::css::io::IOException, ::css::uno::RuntimeException )
376 {
377 	::osl::MutexGuard aGuard( maMutex );
378 	checkConnected();
379 	if ( nLocation < 0 || nLocation > getLength() )
380 		throw ::css::lang::IllegalArgumentException();
381 
382 	mpStream->Seek((sal_uInt32) nLocation );
383 	checkError();
384 }
385 sal_Int64 SAL_CALL OTempFileService::getPosition(  )
386 throw ( ::css::io::IOException, ::css::uno::RuntimeException )
387 {
388 	::osl::MutexGuard aGuard( maMutex );
389 	checkConnected();
390 
391 	sal_uInt32 nPos = mpStream->Tell();
392 	checkError();
393 	return (sal_Int64)nPos;
394 }
395 sal_Int64 SAL_CALL OTempFileService::getLength(  )
396 throw ( ::css::io::IOException, ::css::uno::RuntimeException )
397 {
398 	::osl::MutexGuard aGuard( maMutex );
399 	checkConnected();
400 
401 	sal_uInt32 nCurrentPos = mpStream->Tell();
402 	checkError();
403 
404 	mpStream->Seek(STREAM_SEEK_TO_END);
405 	sal_uInt32 nEndPos = mpStream->Tell();
406 	mpStream->Seek(nCurrentPos);
407 
408 	checkError();
409 
410 	return (sal_Int64)nEndPos;
411 }
412 
413 
414 // XStream
415 
416 ::css::uno::Reference< ::css::io::XInputStream > SAL_CALL OTempFileService::getInputStream()
417 throw ( ::css::uno::RuntimeException )
418 	{
419 	return ::css::uno::Reference< ::css::io::XInputStream >( *this, ::css::uno::UNO_QUERY );
420 }
421 
422 ::css::uno::Reference< ::css::io::XOutputStream > SAL_CALL OTempFileService::getOutputStream()
423 throw ( ::css::uno::RuntimeException )
424 	{
425 	return ::css::uno::Reference< ::css::io::XOutputStream >( *this, ::css::uno::UNO_QUERY );
426 	}
427 
428 // XTruncate
429 
430 void SAL_CALL OTempFileService::truncate()
431 throw ( ::css::io::IOException, ::css::uno::RuntimeException )
432 {
433 	::osl::MutexGuard aGuard( maMutex );
434 	checkConnected();
435 	// SetStreamSize() call does not change the position
436 	mpStream->Seek( 0 );
437 	mpStream->SetStreamSize( 0 );
438 	checkError();
439 }
440 
441 // XServiceInfo
442 
443 ::rtl::OUString SAL_CALL OTempFileService::getImplementationName()
444 throw ( ::css::uno::RuntimeException )
445 {
446 	return getImplementationName_Static();
447 }
448 
449 sal_Bool SAL_CALL OTempFileService::supportsService( ::rtl::OUString const & rServiceName )
450 throw ( ::css::uno::RuntimeException )
451 {
452 	::css::uno::Sequence< ::rtl::OUString > aServices(getSupportedServiceNames_Static());
453 	return rServiceName == aServices[0];
454 }
455 
456 ::css::uno::Sequence < ::rtl::OUString > SAL_CALL OTempFileService::getSupportedServiceNames()
457 throw ( ::css::uno::RuntimeException )
458 {
459 	return getSupportedServiceNames_Static();
460 }
461 
462 
463 
464 ::rtl::OUString OTempFileService::getImplementationName_Static ()
465 {
466 	return ::rtl::OUString ( RTL_CONSTASCII_USTRINGPARAM ( "com.sun.star.io.comp.TempFile" ) );
467 }
468 ::css::uno::Sequence < ::rtl::OUString > OTempFileService::getSupportedServiceNames_Static()
469 {
470 	::css::uno::Sequence < ::rtl::OUString > aNames ( 1 );
471 	aNames[0] = ::rtl::OUString ( RTL_CONSTASCII_USTRINGPARAM ( "com.sun.star.io.TempFile" ) );
472 	return aNames;
473 }
474 ::css::uno::Reference < ::css::uno::XInterface >SAL_CALL XTempFile_createInstance(
475 	css::uno::Reference< ::css::uno::XComponentContext > const & context)
476 	SAL_THROW( ( css::uno::Exception ) )
477 {
478 	return static_cast< ::cppu::OWeakObject * >( new OTempFileService(context) );
479 }
480 
481 ::css::uno::Reference < ::css::lang::XSingleComponentFactory > OTempFileService::createServiceFactory_Static( ::css::uno::Reference < ::css::lang::XMultiServiceFactory > const & )
482 {
483 	return ::cppu::createSingleComponentFactory( XTempFile_createInstance, getImplementationName_Static(), getSupportedServiceNames_Static() );
484 }
485 
486 // C functions to implement this as a component
487 
488 extern "C" SAL_DLLPUBLIC_EXPORT void SAL_CALL component_getImplementationEnvironment(
489     			const sal_Char ** ppEnvTypeName, uno_Environment ** /*ppEnv*/ )
490 {
491 	*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
492 }
493 
494 /**
495  * This function is called to get service factories for an implementation.
496  * @param pImplName name of implementation
497  * @param pServiceManager generic uno interface providing a service manager to instantiate components
498  * @param pRegistryKey registry data key to read and write component persistent data
499  * @return a component factory (generic uno interface)
500  */
501 extern "C" SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory(
502     const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ )
503 {
504     void * pRet = 0;
505 	::css::uno::Reference< ::css::lang::XMultiServiceFactory > xSMgr(
506 		reinterpret_cast< ::css::lang::XMultiServiceFactory * >( pServiceManager ) );
507 	::css::uno::Reference< ::css::lang::XSingleComponentFactory > xFactory;
508 
509 	if (OTempFileService::getImplementationName_Static().compareToAscii( pImplName ) == 0)
510 		xFactory = OTempFileService::createServiceFactory_Static ( xSMgr );
511 
512 	if ( xFactory.is() )
513 	{
514 		xFactory->acquire();
515 		pRet = xFactory.get();
516 	}
517     return pRet;
518 }
519