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