blob: 918037a1d3e598ca1d9c1d4a6074f9419f555c04 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_unotools.hxx"
#include <unotools/tempfile.hxx>
#include <tools/tempfile.hxx>
#include <unotools/localfilehelper.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <ucbhelper/fileidentifierconverter.hxx>
#include <ucbhelper/contentbroker.hxx>
#include <rtl/ustring.hxx>
#include <rtl/instance.hxx>
#include <osl/file.hxx>
#include <tools/time.hxx>
#include <tools/debug.hxx>
#include <stdio.h>
#ifdef UNX
#include <sys/stat.h>
#endif
using namespace osl;
namespace
{
struct TempNameBase_Impl
: public rtl::Static< ::rtl::OUString, TempNameBase_Impl > {};
}
namespace utl
{
struct TempFile_Impl
{
String aName;
String aURL;
SvStream* pStream;
sal_Bool bIsDirectory;
TempFile_Impl()
: pStream(0)
{}
};
rtl::OUString getParentName( const rtl::OUString& aFileName )
{
sal_Int32 lastIndex = aFileName.lastIndexOf( sal_Unicode('/') );
rtl::OUString aParent = aFileName.copy( 0,lastIndex );
if( aParent[ aParent.getLength()-1] == sal_Unicode(':') && aParent.getLength() == 6 )
aParent += rtl::OUString::createFromAscii( "/" );
if( 0 == aParent.compareToAscii( "file://" ) )
aParent = rtl::OUString::createFromAscii( "file:///" );
return aParent;
}
sal_Bool ensuredir( const rtl::OUString& rUnqPath )
{
rtl::OUString aPath;
if ( rUnqPath.getLength() < 1 )
return sal_False;
// remove trailing slash
if ( rUnqPath[ rUnqPath.getLength() - 1 ] == sal_Unicode( '/' ) )
aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
else
aPath = rUnqPath;
// HACK: create directory on a mount point with nobrowse option
// returns ENOSYS in any case !!
osl::Directory aDirectory( aPath );
#ifdef UNX
/* RW permission for the user only! */
mode_t old_mode = umask(077);
#endif
osl::FileBase::RC nError = aDirectory.open();
#ifdef UNX
umask(old_mode);
#endif
aDirectory.close();
if( nError == osl::File::E_None )
return sal_True;
// try to create the directory
nError = osl::Directory::create( aPath );
sal_Bool bSuccess = ( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
if( !bSuccess )
{
// perhaps parent(s) don't exist
rtl::OUString aParentDir = getParentName( aPath );
if ( aParentDir != aPath )
{
bSuccess = ensuredir( getParentName( aPath ) );
// After parent directory structure exists try it one's more
if ( bSuccess )
{
// Parent directory exists, retry creation of directory
nError = osl::Directory::create( aPath );
bSuccess =( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
}
}
}
return bSuccess;
}
#define TMPNAME_SIZE ( 1 + 5 + 5 + 4 + 1 )
String ConstructTempDir_Impl( const String* pParent )
{
String aName;
if ( pParent && pParent->Len() )
{
::ucbhelper::ContentBroker* pBroker = ::ucbhelper::ContentBroker::get();
if ( pBroker )
{
::com::sun::star::uno::Reference< ::com::sun::star::ucb::XContentProviderManager > xManager =
pBroker->getContentProviderManagerInterface();
// if parent given try to use it
rtl::OUString aTmp( *pParent );
// test for valid filename
rtl::OUString aRet;
::osl::FileBase::getFileURLFromSystemPath(
::ucbhelper::getSystemPathFromFileURL( xManager, aTmp ),
aRet );
if ( aRet.getLength() )
{
::osl::DirectoryItem aItem;
sal_Int32 i = aRet.getLength();
if ( aRet[i-1] == '/' )
i--;
if ( DirectoryItem::get( ::rtl::OUString( aRet.getStr(), i ), aItem ) == FileBase::E_None )
aName = aRet;
}
}
else
{
DBG_WARNING( "::unotools::TempFile : UCB not present or not initialized!" );
}
}
if ( !aName.Len() )
{
::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
if (rTempNameBase_Impl.getLength() == 0)
{
::rtl::OUString ustrTempDirURL;
::osl::FileBase::RC rc = ::osl::File::getTempDirURL(
ustrTempDirURL );
if (rc == ::osl::FileBase::E_None)
rTempNameBase_Impl = ustrTempDirURL;
}
// if no parent or invalid parent : use default directory
DBG_ASSERT( rTempNameBase_Impl.getLength(), "No TempDir!" );
aName = rTempNameBase_Impl;
ensuredir( aName );
}
// Make sure that directory ends with a separator
xub_StrLen i = aName.Len();
if( i>0 && aName.GetChar(i-1) != '/' )
aName += '/';
return aName;
}
void CreateTempName_Impl( String& rName, sal_Bool bKeep, sal_Bool bDir = sal_True )
{
// add a suitable tempname
// 36 ** 6 == 2176782336
unsigned const nRadix = 36;
unsigned long const nMax = (nRadix*nRadix*nRadix*nRadix*nRadix*nRadix);
String aName( rName );
aName += String::CreateFromAscii( "sv" );
rName.Erase();
unsigned long nSeed = Time::GetSystemTicks() % nMax;
for ( unsigned long u = nSeed; ++u != nSeed; )
{
u %= nMax;
String aTmp( aName );
aTmp += String::CreateFromInt64( static_cast<sal_Int64>(u), nRadix );
aTmp += String::CreateFromAscii( ".tmp" );
if ( bDir )
{
FileBase::RC err = Directory::create( aTmp );
if ( err == FileBase::E_None )
{
// !bKeep: only for creating a name, not a file or directory
if ( bKeep || Directory::remove( aTmp ) == FileBase::E_None )
rName = aTmp;
break;
}
else if ( err != FileBase::E_EXIST )
{
// if f.e. name contains invalid chars stop trying to create dirs
break;
}
}
else
{
DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" );
File aFile( aTmp );
#ifdef UNX
/* RW permission for the user only! */
mode_t old_mode = umask(077);
#endif
FileBase::RC err = aFile.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_NoLock );
#ifdef UNX
umask(old_mode);
#endif
if ( err == FileBase::E_None )
{
rName = aTmp;
aFile.close();
break;
}
else if ( err != FileBase::E_EXIST )
{
// if f.e. name contains invalid chars stop trying to create files
// but if there is a folder with such name proceed further
DirectoryItem aTmpItem;
FileStatus aTmpStatus( FileStatusMask_Type );
if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
|| aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
|| aTmpStatus.getFileType() != FileStatus::Directory )
break;
}
}
}
}
void lcl_createName(TempFile_Impl& _rImpl,const String& rLeadingChars,sal_Bool _bStartWithZero, const String* pExtension, const String* pParent, sal_Bool bDirectory)
{
_rImpl.bIsDirectory = bDirectory;
// get correct directory
String aName = ConstructTempDir_Impl( pParent );
sal_Bool bUseNumber = _bStartWithZero;
// now use special naming scheme ( name takes leading chars and an index counting up from zero
aName += rLeadingChars;
for ( sal_Int32 i=0;; i++ )
{
String aTmp( aName );
if ( bUseNumber )
aTmp += String::CreateFromInt32( i );
bUseNumber = sal_True;
if ( pExtension )
aTmp += *pExtension;
else
aTmp += String::CreateFromAscii( ".tmp" );
if ( bDirectory )
{
FileBase::RC err = Directory::create( aTmp );
if ( err == FileBase::E_None )
{
_rImpl.aName = aTmp;
break;
}
else if ( err != FileBase::E_EXIST )
// if f.e. name contains invalid chars stop trying to create dirs
break;
}
else
{
File aFile( aTmp );
#ifdef UNX
/* RW permission for the user only! */
mode_t old_mode = umask(077);
#endif
FileBase::RC err = aFile.open(osl_File_OpenFlag_Create);
#ifdef UNX
umask(old_mode);
#endif
if ( err == FileBase::E_None )
{
_rImpl.aName = aTmp;
aFile.close();
break;
}
else if ( err != FileBase::E_EXIST )
{
// if f.e. name contains invalid chars stop trying to create dirs
// but if there is a folder with such name proceed further
DirectoryItem aTmpItem;
FileStatus aTmpStatus( FileStatusMask_Type );
if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
|| aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
|| aTmpStatus.getFileType() != FileStatus::Directory )
break;
}
}
if ( !_bStartWithZero )
aTmp += String::CreateFromInt32( i );
}
}
String TempFile::CreateTempName( const String* pParent )
{
// get correct directory
String aName = ConstructTempDir_Impl( pParent );
// get TempFile name with default naming scheme
CreateTempName_Impl( aName, sal_False );
// convert to file URL
rtl::OUString aTmp;
if ( aName.Len() )
FileBase::getSystemPathFromFileURL( aName, aTmp );
return aTmp;
}
TempFile::TempFile( const String* pParent, sal_Bool bDirectory )
: pImp( new TempFile_Impl )
, bKillingFileEnabled( sal_False )
{
pImp->bIsDirectory = bDirectory;
// get correct directory
pImp->aName = ConstructTempDir_Impl( pParent );
// get TempFile with default naming scheme
CreateTempName_Impl( pImp->aName, sal_True, bDirectory );
}
TempFile::TempFile( const String& rLeadingChars, const String* pExtension, const String* pParent, sal_Bool bDirectory)
: pImp( new TempFile_Impl )
, bKillingFileEnabled( sal_False )
{
lcl_createName(*pImp,rLeadingChars,sal_True, pExtension, pParent, bDirectory);
}
TempFile::TempFile( const String& rLeadingChars,sal_Bool _bStartWithZero, const String* pExtension, const String* pParent, sal_Bool bDirectory)
: pImp( new TempFile_Impl )
, bKillingFileEnabled( sal_False )
{
lcl_createName(*pImp,rLeadingChars,_bStartWithZero, pExtension, pParent, bDirectory);
}
TempFile::~TempFile()
{
delete pImp->pStream;
if ( bKillingFileEnabled )
{
if ( pImp->bIsDirectory )
{
// at the moment no recursiv algorithm present
Directory::remove( pImp->aName );
}
else
{
File::remove( pImp->aName );
}
}
delete pImp;
}
sal_Bool TempFile::IsValid() const
{
return pImp->aName.Len() != 0;
}
String TempFile::GetFileName() const
{
rtl::OUString aTmp;
FileBase::getSystemPathFromFileURL( pImp->aName, aTmp );
return aTmp;
}
String TempFile::GetURL() const
{
if ( !pImp->aURL.Len() )
{
String aTmp;
LocalFileHelper::ConvertPhysicalNameToURL( GetFileName(), aTmp );
pImp->aURL = aTmp;
}
return pImp->aURL;
}
SvStream* TempFile::GetStream( StreamMode eMode )
{
if ( !pImp->pStream )
{
if ( GetURL().Len() )
pImp->pStream = UcbStreamHelper::CreateStream( pImp->aURL, eMode, sal_True /* bFileExists */ );
else
pImp->pStream = new SvMemoryStream( eMode );
}
return pImp->pStream;
}
void TempFile::CloseStream()
{
if ( pImp->pStream )
{
delete pImp->pStream;
pImp->pStream = NULL;
}
}
String TempFile::SetTempNameBaseDirectory( const String &rBaseName )
{
if( !rBaseName.Len() )
return String();
rtl::OUString aUnqPath( rBaseName );
// remove trailing slash
if ( rBaseName.GetChar( rBaseName.Len() - 1 ) == sal_Unicode( '/' ) )
aUnqPath = rBaseName.Copy( 0, rBaseName.Len() - 1 );
// try to create the directory
sal_Bool bRet = sal_False;
osl::FileBase::RC err = osl::Directory::create( aUnqPath );
if ( err != FileBase::E_None && err != FileBase::E_EXIST )
// perhaps parent(s) don't exist
bRet = ensuredir( aUnqPath );
else
bRet = sal_True;
// failure to create base directory means returning an empty string
rtl::OUString aTmp;
if ( bRet )
{
// append own internal directory
bRet = sal_True;
::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
rTempNameBase_Impl = rBaseName;
rTempNameBase_Impl += String( '/' );
TempFile aBase( NULL, sal_True );
if ( aBase.IsValid() )
// use it in case of success
rTempNameBase_Impl = aBase.pImp->aName;
// return system path of used directory
FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
}
return aTmp;
}
String TempFile::GetTempNameBaseDirectory()
{
const ::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
if ( !rTempNameBase_Impl.getLength() )
return String();
rtl::OUString aTmp;
FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
return aTmp;
}
}