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