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
TempFile_Implutl::TempFile_Impl62 TempFile_Impl()
63 : pStream(0)
64 {}
65 };
66
getParentName(const rtl::OUString & aFileName)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
ensuredir(const rtl::OUString & rUnqPath)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 )
ConstructTempDir_Impl(const String * pParent)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.getStr(), 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
CreateTempName_Impl(String & rName,sal_Bool bKeep,sal_Bool bDir=sal_True)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
lcl_createName(TempFile_Impl & _rImpl,const String & rLeadingChars,sal_Bool _bStartWithZero,const String * pExtension,const String * pParent,sal_Bool bDirectory)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
CreateTempName(const String * pParent)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
TempFile(const String * pParent,sal_Bool bDirectory)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
TempFile(const String & rLeadingChars,const String * pExtension,const String * pParent,sal_Bool bDirectory)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 }
TempFile(const String & rLeadingChars,sal_Bool _bStartWithZero,const String * pExtension,const String * pParent,sal_Bool bDirectory)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
~TempFile()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
IsValid() const390 sal_Bool TempFile::IsValid() const
391 {
392 return pImp->aName.Len() != 0;
393 }
394
GetFileName() const395 String TempFile::GetFileName() const
396 {
397 rtl::OUString aTmp;
398 FileBase::getSystemPathFromFileURL( pImp->aName, aTmp );
399 return aTmp;
400 }
401
GetURL() const402 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
GetStream(StreamMode eMode)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
CloseStream()427 void TempFile::CloseStream()
428 {
429 if ( pImp->pStream )
430 {
431 delete pImp->pStream;
432 pImp->pStream = NULL;
433 }
434 }
435
SetTempNameBaseDirectory(const String & rBaseName)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
GetTempNameBaseDirectory()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