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 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_unotools.hxx"
30 
31 #include <unotools/tempfile.hxx>
32 #include <tools/tempfile.hxx>
33 #include <unotools/localfilehelper.hxx>
34 #include <unotools/ucbstreamhelper.hxx>
35 #include <ucbhelper/fileidentifierconverter.hxx>
36 #include <ucbhelper/contentbroker.hxx>
37 #include <rtl/ustring.hxx>
38 #include <rtl/instance.hxx>
39 #include <osl/file.hxx>
40 #include <tools/time.hxx>
41 #include <tools/debug.hxx>
42 #include <stdio.h>
43 
44 #ifdef UNX
45 #include <sys/stat.h>
46 #endif
47 
48 using namespace osl;
49 
50 namespace
51 {
52     struct TempNameBase_Impl
53         : public rtl::Static< ::rtl::OUString, TempNameBase_Impl > {};
54 }
55 
56 namespace utl
57 {
58 
59 struct TempFile_Impl
60 {
61     String      aName;
62     String      aURL;
63     SvStream*   pStream;
64     sal_Bool    bIsDirectory;
65 
66                 TempFile_Impl()
67                     : pStream(0)
68                     {}
69 };
70 
71 rtl::OUString getParentName( const rtl::OUString& aFileName )
72 {
73     sal_Int32 lastIndex = aFileName.lastIndexOf( sal_Unicode('/') );
74     rtl::OUString aParent = aFileName.copy( 0,lastIndex );
75 
76     if( aParent[ aParent.getLength()-1] == sal_Unicode(':') && aParent.getLength() == 6 )
77         aParent += rtl::OUString::createFromAscii( "/" );
78 
79     if( 0 == aParent.compareToAscii( "file://" ) )
80         aParent = rtl::OUString::createFromAscii( "file:///" );
81 
82     return aParent;
83 }
84 
85 sal_Bool ensuredir( const rtl::OUString& rUnqPath )
86 {
87 	rtl::OUString aPath;
88 	if ( rUnqPath.getLength() < 1 )
89 		return sal_False;
90 
91     // remove trailing slash
92     if ( rUnqPath[ rUnqPath.getLength() - 1 ] == sal_Unicode( '/' ) )
93 		aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
94 	else
95 		aPath = rUnqPath;
96 
97     // HACK: create directory on a mount point with nobrowse option
98     // returns ENOSYS in any case !!
99     osl::Directory aDirectory( aPath );
100 #ifdef UNX
101 /* RW permission for the user only! */
102  mode_t old_mode = umask(077);
103 #endif
104     osl::FileBase::RC nError = aDirectory.open();
105 #ifdef UNX
106 umask(old_mode);
107 #endif
108     aDirectory.close();
109     if( nError == osl::File::E_None )
110         return sal_True;
111 
112     // try to create the directory
113     nError = osl::Directory::create( aPath );
114 	sal_Bool  bSuccess = ( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
115     if( !bSuccess )
116 	{
117         // perhaps parent(s) don't exist
118 		rtl::OUString aParentDir = getParentName( aPath );
119 		if ( aParentDir != aPath )
120         {
121             bSuccess = ensuredir( getParentName( aPath ) );
122 
123 			// After parent directory structure exists try it one's more
124 			if ( bSuccess )
125             {
126                 // Parent directory exists, retry creation of directory
127 				nError = osl::Directory::create( aPath );
128 				bSuccess =( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
129 			}
130 		}
131 	}
132 
133 	return bSuccess;
134 }
135 
136 #define TMPNAME_SIZE  ( 1 + 5 + 5 + 4 + 1 )
137 String ConstructTempDir_Impl( const String* pParent )
138 {
139     String aName;
140     if ( pParent && pParent->Len() )
141     {
142         ::ucbhelper::ContentBroker* pBroker = ::ucbhelper::ContentBroker::get();
143         if ( pBroker )
144         {
145             ::com::sun::star::uno::Reference< ::com::sun::star::ucb::XContentProviderManager > xManager =
146                     pBroker->getContentProviderManagerInterface();
147 
148             // if parent given try to use it
149             rtl::OUString aTmp( *pParent );
150 
151             // test for valid filename
152 			rtl::OUString aRet;
153 			::osl::FileBase::getFileURLFromSystemPath(
154 				::ucbhelper::getSystemPathFromFileURL( xManager, aTmp ),
155 				aRet );
156             if ( aRet.getLength() )
157             {
158                 ::osl::DirectoryItem aItem;
159                 sal_Int32 i = aRet.getLength();
160                 if ( aRet[i-1] == '/' )
161                     i--;
162 
163                 if ( DirectoryItem::get( ::rtl::OUString( aRet, i ), aItem ) == FileBase::E_None )
164                     aName = aRet;
165             }
166         }
167         else
168         {
169             DBG_WARNING( "::unotools::TempFile : UCB not present or not initialized!" );
170         }
171     }
172 
173     if ( !aName.Len() )
174     {
175         ::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
176         if (rTempNameBase_Impl.getLength() == 0)
177         {
178             ::rtl::OUString ustrTempDirURL;
179             ::osl::FileBase::RC rc = ::osl::File::getTempDirURL(
180                 ustrTempDirURL );
181             if (rc == ::osl::FileBase::E_None)
182                 rTempNameBase_Impl = ustrTempDirURL;
183         }
184         // if no parent or invalid parent : use default directory
185         DBG_ASSERT( rTempNameBase_Impl.getLength(), "No TempDir!" );
186         aName = rTempNameBase_Impl;
187         ensuredir( aName );
188     }
189 
190     // Make sure that directory ends with a separator
191     xub_StrLen i = aName.Len();
192     if( i>0 && aName.GetChar(i-1) != '/' )
193         aName += '/';
194 
195     return aName;
196 }
197 
198 void CreateTempName_Impl( String& rName, sal_Bool bKeep, sal_Bool bDir = sal_True )
199 {
200     // add a suitable tempname
201     // 36 ** 6 == 2176782336
202     unsigned const nRadix = 36;
203     unsigned long const nMax = (nRadix*nRadix*nRadix*nRadix*nRadix*nRadix);
204     String aName( rName );
205     aName += String::CreateFromAscii( "sv" );
206 
207     rName.Erase();
208     unsigned long nSeed = Time::GetSystemTicks() % nMax;
209     for ( unsigned long u = nSeed; ++u != nSeed; )
210     {
211         u %= nMax;
212         String aTmp( aName );
213         aTmp += String::CreateFromInt64( static_cast<sal_Int64>(u), nRadix );
214         aTmp += String::CreateFromAscii( ".tmp" );
215 
216         if ( bDir )
217         {
218             FileBase::RC err = Directory::create( aTmp );
219             if (  err == FileBase::E_None )
220             {
221                 // !bKeep: only for creating a name, not a file or directory
222                 if ( bKeep || Directory::remove( aTmp ) == FileBase::E_None )
223                     rName = aTmp;
224                 break;
225             }
226             else if ( err != FileBase::E_EXIST )
227             {
228                 // if f.e. name contains invalid chars stop trying to create dirs
229                 break;
230             }
231         }
232         else
233         {
234             DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" );
235             File aFile( aTmp );
236 #ifdef UNX
237 /* RW permission for the user only! */
238  mode_t old_mode = umask(077);
239 #endif
240             FileBase::RC err = aFile.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_NoLock );
241 #ifdef UNX
242 umask(old_mode);
243 #endif
244             if (  err == FileBase::E_None )
245             {
246                 rName = aTmp;
247                 aFile.close();
248                 break;
249             }
250             else if ( err != FileBase::E_EXIST )
251             {
252                  // if f.e. name contains invalid chars stop trying to create files
253                  // but if there is a folder with such name proceed further
254 
255                  DirectoryItem aTmpItem;
256                  FileStatus aTmpStatus( FileStatusMask_Type );
257                  if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
258                    || aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
259                    || aTmpStatus.getFileType() != FileStatus::Directory )
260                      break;
261             }
262         }
263     }
264 }
265 
266 void lcl_createName(TempFile_Impl& _rImpl,const String& rLeadingChars,sal_Bool _bStartWithZero, const String* pExtension, const String* pParent, sal_Bool bDirectory)
267 {
268     _rImpl.bIsDirectory = bDirectory;
269 
270     // get correct directory
271     String aName = ConstructTempDir_Impl( pParent );
272 
273     sal_Bool bUseNumber = _bStartWithZero;
274     // now use special naming scheme ( name takes leading chars and an index counting up from zero
275     aName += rLeadingChars;
276     for ( sal_Int32 i=0;; i++ )
277     {
278         String aTmp( aName );
279         if ( bUseNumber )
280             aTmp += String::CreateFromInt32( i );
281         bUseNumber = sal_True;
282         if ( pExtension )
283             aTmp += *pExtension;
284         else
285             aTmp += String::CreateFromAscii( ".tmp" );
286         if ( bDirectory )
287         {
288             FileBase::RC err = Directory::create( aTmp );
289             if ( err == FileBase::E_None )
290             {
291                 _rImpl.aName = aTmp;
292                 break;
293             }
294             else if ( err != FileBase::E_EXIST )
295                 // if f.e. name contains invalid chars stop trying to create dirs
296                 break;
297         }
298         else
299         {
300             File aFile( aTmp );
301 #ifdef UNX
302 /* RW permission for the user only! */
303  mode_t old_mode = umask(077);
304 #endif
305             FileBase::RC err = aFile.open(osl_File_OpenFlag_Create);
306 #ifdef UNX
307 umask(old_mode);
308 #endif
309             if ( err == FileBase::E_None )
310             {
311                 _rImpl.aName = aTmp;
312                 aFile.close();
313                 break;
314             }
315             else if ( err != FileBase::E_EXIST )
316             {
317                 // if f.e. name contains invalid chars stop trying to create dirs
318                 // but if there is a folder with such name proceed further
319 
320                 DirectoryItem aTmpItem;
321                 FileStatus aTmpStatus( FileStatusMask_Type );
322                 if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
323                   || aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
324                   || aTmpStatus.getFileType() != FileStatus::Directory )
325                     break;
326             }
327         }
328         if ( !_bStartWithZero )
329             aTmp += String::CreateFromInt32( i );
330     }
331 }
332 
333 
334 String TempFile::CreateTempName( const String* pParent )
335 {
336     // get correct directory
337     String aName = ConstructTempDir_Impl( pParent );
338 
339     // get TempFile name with default naming scheme
340     CreateTempName_Impl( aName, sal_False );
341 
342     // convert to file URL
343     rtl::OUString aTmp;
344     if ( aName.Len() )
345         FileBase::getSystemPathFromFileURL( aName, aTmp );
346     return aTmp;
347 }
348 
349 TempFile::TempFile( const String* pParent, sal_Bool bDirectory )
350     : pImp( new TempFile_Impl )
351     , bKillingFileEnabled( sal_False )
352 {
353     pImp->bIsDirectory = bDirectory;
354 
355     // get correct directory
356     pImp->aName = ConstructTempDir_Impl( pParent );
357 
358     // get TempFile with default naming scheme
359     CreateTempName_Impl( pImp->aName, sal_True, bDirectory );
360 }
361 
362 TempFile::TempFile( const String& rLeadingChars, const String* pExtension, const String* pParent, sal_Bool bDirectory)
363     : pImp( new TempFile_Impl )
364     , bKillingFileEnabled( sal_False )
365 {
366     lcl_createName(*pImp,rLeadingChars,sal_True, pExtension, pParent, bDirectory);
367 }
368 TempFile::TempFile( const String& rLeadingChars,sal_Bool _bStartWithZero, const String* pExtension, const String* pParent, sal_Bool bDirectory)
369     : pImp( new TempFile_Impl )
370     , bKillingFileEnabled( sal_False )
371 {
372     lcl_createName(*pImp,rLeadingChars,_bStartWithZero, pExtension, pParent, bDirectory);
373 }
374 
375 TempFile::~TempFile()
376 {
377     delete pImp->pStream;
378     if ( bKillingFileEnabled )
379     {
380         if ( pImp->bIsDirectory )
381         {
382             // at the moment no recursiv algorithm present
383             Directory::remove( pImp->aName );
384         }
385         else
386         {
387             File::remove( pImp->aName );
388         }
389     }
390 
391     delete pImp;
392 }
393 
394 sal_Bool TempFile::IsValid() const
395 {
396     return pImp->aName.Len() != 0;
397 }
398 
399 String TempFile::GetFileName() const
400 {
401     rtl::OUString aTmp;
402     FileBase::getSystemPathFromFileURL( pImp->aName, aTmp );
403     return aTmp;
404 }
405 
406 String TempFile::GetURL() const
407 {
408     if ( !pImp->aURL.Len() )
409     {
410         String aTmp;
411         LocalFileHelper::ConvertPhysicalNameToURL( GetFileName(), aTmp );
412         pImp->aURL = aTmp;
413     }
414 
415     return pImp->aURL;
416 }
417 
418 SvStream* TempFile::GetStream( StreamMode eMode )
419 {
420     if ( !pImp->pStream )
421     {
422         if ( GetURL().Len() )
423             pImp->pStream = UcbStreamHelper::CreateStream( pImp->aURL, eMode, sal_True /* bFileExists */ );
424         else
425             pImp->pStream = new SvMemoryStream( eMode );
426     }
427 
428     return pImp->pStream;
429 }
430 
431 void TempFile::CloseStream()
432 {
433     if ( pImp->pStream )
434     {
435 		delete pImp->pStream;
436 		pImp->pStream = NULL;
437     }
438 }
439 
440 String TempFile::SetTempNameBaseDirectory( const String &rBaseName )
441 {
442 	if( !rBaseName.Len() )
443 		return String();
444 
445     rtl::OUString aUnqPath( rBaseName );
446 
447 	// remove trailing slash
448     if ( rBaseName.GetChar( rBaseName.Len() - 1 ) == sal_Unicode( '/' ) )
449         aUnqPath = rBaseName.Copy( 0, rBaseName.Len() - 1 );
450 
451     // try to create the directory
452     sal_Bool bRet = sal_False;
453     osl::FileBase::RC err = osl::Directory::create( aUnqPath );
454     if ( err != FileBase::E_None && err != FileBase::E_EXIST )
455         // perhaps parent(s) don't exist
456         bRet = ensuredir( aUnqPath );
457     else
458         bRet = sal_True;
459 
460     // failure to create base directory means returning an empty string
461     rtl::OUString aTmp;
462     if ( bRet )
463     {
464         // append own internal directory
465         bRet = sal_True;
466         ::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
467         rTempNameBase_Impl = rBaseName;
468         rTempNameBase_Impl += String( '/' );
469 
470         TempFile aBase( NULL, sal_True );
471         if ( aBase.IsValid() )
472             // use it in case of success
473             rTempNameBase_Impl = aBase.pImp->aName;
474 
475         // return system path of used directory
476         FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
477     }
478 
479     return aTmp;
480 }
481 
482 String TempFile::GetTempNameBaseDirectory()
483 {
484     const ::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
485     if ( !rTempNameBase_Impl.getLength() )
486         return String();
487 
488     rtl::OUString aTmp;
489     FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
490     return aTmp;
491 }
492 
493 }
494