1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
25 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
26 #include <unotools/tempfile.hxx>
27 #include <unotools/localfilehelper.hxx>
28 #include <rtl/ustring.hxx>
29 #include <rtl/instance.hxx>
30 #include <osl/detail/file.h>
31 #include <osl/file.hxx>
32 #include <tools/time.hxx>
33 #include <tools/debug.hxx>
38 #include <sys/types.h>
48 struct TempNameBase_Impl
49 : public rtl::Static
< OUString
, TempNameBase_Impl
> {};
55 static OUString
getParentName( const OUString
& aFileName
)
57 sal_Int32 lastIndex
= aFileName
.lastIndexOf( '/' );
62 aParent
= aFileName
.copy(0, lastIndex
);
64 if (aParent
.endsWith(":") && aParent
.getLength() == 6)
67 if (aParent
.equalsIgnoreAsciiCase("file://"))
74 static bool ensuredir( const OUString
& rUnqPath
)
77 if ( rUnqPath
.isEmpty() )
80 // remove trailing slash
81 if ( rUnqPath
.endsWith("/") )
82 aPath
= rUnqPath
.copy( 0, rUnqPath
.getLength() - 1 );
86 // HACK: create directory on a mount point with nobrowse option
87 // returns ENOSYS in any case !!
88 osl::Directory
aDirectory( aPath
);
89 osl::FileBase::RC nError
= aDirectory
.open();
91 if( nError
== osl::File::E_None
)
94 // try to create the directory
95 nError
= osl::Directory::create( aPath
);
96 bool bSuccess
= ( nError
== osl::File::E_None
|| nError
== osl::FileBase::E_EXIST
);
99 // perhaps parent(s) don't exist
100 OUString aParentDir
= getParentName( aPath
);
101 if ( aParentDir
!= aPath
)
103 bSuccess
= ensuredir( getParentName( aPath
) );
105 // After parent directory structure exists try it one's more
108 // Parent directory exists, retry creation of directory
109 nError
= osl::Directory::create( aPath
);
110 bSuccess
=( nError
== osl::File::E_None
|| nError
== osl::FileBase::E_EXIST
);
118 static OUString
ConstructTempDir_Impl( const OUString
* pParent
)
122 // Ignore pParent on iOS. We don't want to create any temp files
123 // in the same directory where the document being edited is.
125 if ( pParent
&& !pParent
->isEmpty() )
127 // test for valid filename
129 if ((osl::FileBase::getSystemPathFromFileURL(*pParent
, aRet
)
130 == osl::FileBase::E_None
)
131 && (osl::FileBase::getFileURLFromSystemPath(aRet
, aRet
)
132 == osl::FileBase::E_None
))
134 ::osl::DirectoryItem aItem
;
135 sal_Int32 i
= aRet
.getLength();
136 if ( aRet
[i
-1] == '/' )
139 if ( DirectoryItem::get( aRet
.copy(0, i
), aItem
) == FileBase::E_None
)
147 if ( aName
.isEmpty() )
149 OUString
&rTempNameBase_Impl
= TempNameBase_Impl::get();
150 if (rTempNameBase_Impl
.isEmpty())
152 OUString ustrTempDirURL
;
153 ::osl::FileBase::RC rc
= ::osl::File::getTempDirURL(
155 if (rc
== ::osl::FileBase::E_None
)
156 rTempNameBase_Impl
= ustrTempDirURL
;
158 // if no parent or invalid parent : use default directory
159 DBG_ASSERT( !rTempNameBase_Impl
.isEmpty(), "No TempDir!" );
160 aName
= rTempNameBase_Impl
;
164 // Make sure that directory ends with a separator
165 if( !aName
.isEmpty() && !aName
.endsWith("/") )
173 virtual bool next(OUString
*) = 0;
176 virtual ~Tokens() {} // avoid warnings
179 class SequentialTokens
: public Tokens
{
181 explicit SequentialTokens(bool showZero
): m_value(0), m_show(showZero
) {}
183 bool next(OUString
* token
) override
{
184 assert(token
!= nullptr);
185 if (m_value
== SAL_MAX_UINT32
) {
188 *token
= m_show
? OUString::number(m_value
) : OUString();
199 class UniqueTokens
: public Tokens
{
201 UniqueTokens(): m_count(0) {}
203 bool next(OUString
* token
) override
{
204 assert(token
!= nullptr);
205 // Because of the shared globalValue, no single instance of UniqueTokens
206 // is guaranteed to exhaustively test all 36^6 possible values, but stop
207 // after that many attempts anyway:
208 sal_uInt32 radix
= 36;
209 sal_uInt32 max
= radix
* radix
* radix
* radix
* radix
* radix
;
210 // 36^6 == 2'176'782'336 < SAL_MAX_UINT32 == 4'294'967'295
211 if (m_count
== max
) {
216 osl::MutexGuard
g(osl::Mutex::getGlobalMutex());
218 = ((globalValue
== SAL_MAX_UINT32
219 ? tools::Time::GetSystemTicks() : globalValue
+ 1)
223 *token
= OUString::number(v
, radix
);
229 static sal_uInt32 globalValue
;
234 sal_uInt32
UniqueTokens::globalValue
= SAL_MAX_UINT32
;
238 class TempDirCreatedObserver
: public DirectoryCreationObserver
241 virtual void DirectoryCreated(const OUString
& aDirectoryUrl
) override
243 File::setAttributes( aDirectoryUrl
, osl_File_Attribute_OwnRead
|
244 osl_File_Attribute_OwnWrite
| osl_File_Attribute_OwnExe
);
249 static OUString
lcl_createName(
250 const OUString
& rLeadingChars
, Tokens
& tokens
, const OUString
* pExtension
,
251 const OUString
* pParent
, bool bDirectory
, bool bKeep
, bool bLock
,
252 bool bCreateParentDirs
)
254 OUString aName
= ConstructTempDir_Impl( pParent
);
255 if ( bCreateParentDirs
)
257 sal_Int32 nOffset
= rLeadingChars
.lastIndexOf("/");
260 OUString aDirName
= aName
+ rLeadingChars
.copy( 0, nOffset
);
261 TempDirCreatedObserver observer
;
262 FileBase::RC err
= Directory::createPath( aDirName
, &observer
);
263 if ( err
!= FileBase::E_None
&& err
!= FileBase::E_EXIST
)
267 aName
+= rLeadingChars
;
270 while (tokens
.next(&token
))
272 OUString
aTmp( aName
+ token
);
279 FileBase::RC err
= Directory::create(
281 (osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
282 | osl_File_OpenFlag_Private
));
283 if ( err
== FileBase::E_None
)
285 // !bKeep: only for creating a name, not a file or directory
286 if ( bKeep
|| Directory::remove( aTmp
) == FileBase::E_None
)
291 else if ( err
!= FileBase::E_EXIST
)
292 // if f.e. name contains invalid chars stop trying to create dirs
297 DBG_ASSERT( bKeep
, "Too expensive, use directory for creating name!" );
299 FileBase::RC err
= aFile
.open(
300 osl_File_OpenFlag_Create
| osl_File_OpenFlag_Private
301 | (bLock
? 0 : osl_File_OpenFlag_NoLock
));
302 if ( err
== FileBase::E_None
|| (bLock
&& err
== FileBase::E_NOLCK
) )
307 else if ( err
!= FileBase::E_EXIST
)
309 // if f.e. name contains invalid chars stop trying to create dirs
310 // but if there is a folder with such name proceed further
312 DirectoryItem aTmpItem
;
313 FileStatus
aTmpStatus( osl_FileStatus_Mask_Type
);
314 if ( DirectoryItem::get( aTmp
, aTmpItem
) != FileBase::E_None
315 || aTmpItem
.getFileStatus( aTmpStatus
) != FileBase::E_None
316 || aTmpStatus
.getFileType() != FileStatus::Directory
)
324 static OUString
CreateTempName_Impl( const OUString
* pParent
, bool bKeep
, bool bDir
= true )
326 OUString aEyeCatcher
= "lu";
329 const char* eye
= getenv("LO_TESTNAME");
332 aEyeCatcher
= OUString(eye
, strlen(eye
), RTL_TEXTENCODING_ASCII_US
);
335 static const pid_t pid
= getpid();
336 static const OUString aPidString
= OUString::number(pid
);
337 aEyeCatcher
+= aPidString
;
340 static const int pid
= _getpid();
341 static const OUString aPidString
= OUString::number(pid
);
342 aEyeCatcher
+= aPidString
;
345 return lcl_createName( aEyeCatcher
, t
, nullptr, pParent
, bDir
, bKeep
,
349 OUString
TempFile::CreateTempName()
351 OUString
aName(CreateTempName_Impl( nullptr, false ));
353 // convert to file URL
355 if ( !aName
.isEmpty() )
356 FileBase::getSystemPathFromFileURL( aName
, aTmp
);
360 TempFile::TempFile( const OUString
* pParent
, bool bDirectory
)
361 : bIsDirectory( bDirectory
)
362 , bKillingFileEnabled( false )
364 aName
= CreateTempName_Impl( pParent
, true, bDirectory
);
367 TempFile::TempFile( const OUString
& rLeadingChars
, bool _bStartWithZero
,
368 const OUString
* pExtension
, const OUString
* pParent
,
369 bool bCreateParentDirs
)
370 : bIsDirectory( false )
371 , bKillingFileEnabled( false )
373 SequentialTokens
t(_bStartWithZero
);
374 aName
= lcl_createName( rLeadingChars
, t
, pExtension
, pParent
, false,
375 true, true, bCreateParentDirs
);
378 TempFile::TempFile(TempFile
&& other
):
379 aName(std::move(other
.aName
)), pStream(std::move(other
.pStream
)), bIsDirectory(other
.bIsDirectory
),
380 bKillingFileEnabled(other
.bKillingFileEnabled
)
382 other
.bKillingFileEnabled
= false;
385 TempFile::~TempFile()
388 if ( bKillingFileEnabled
)
392 // at the moment no recursiv algorithm present
393 Directory::remove( aName
);
397 File::remove( aName
);
402 bool TempFile::IsValid() const
404 return !aName
.isEmpty();
407 OUString
TempFile::GetFileName() const
410 FileBase::getSystemPathFromFileURL(aName
, aTmp
);
414 OUString
const & TempFile::GetURL() const
419 SvStream
* TempFile::GetStream( StreamMode eMode
)
423 if (!aName
.isEmpty())
424 pStream
.reset(new SvFileStream(aName
, eMode
));
426 pStream
.reset(new SvMemoryStream(nullptr, 0, eMode
));
429 return pStream
.get();
432 void TempFile::CloseStream()
437 OUString
TempFile::SetTempNameBaseDirectory( const OUString
&rBaseName
)
439 if( rBaseName
.isEmpty() )
442 OUString
aUnqPath( rBaseName
);
444 // remove trailing slash
445 if ( rBaseName
.endsWith("/") )
446 aUnqPath
= rBaseName
.copy( 0, rBaseName
.getLength() - 1 );
448 // try to create the directory
450 osl::FileBase::RC err
= osl::Directory::create( aUnqPath
);
451 if ( err
!= FileBase::E_None
&& err
!= FileBase::E_EXIST
)
452 // perhaps parent(s) don't exist
453 bRet
= ensuredir( aUnqPath
);
457 // failure to create base directory means returning an empty string
461 // append own internal directory
462 OUString
&rTempNameBase_Impl
= TempNameBase_Impl::get();
463 rTempNameBase_Impl
= rBaseName
+ "/";
465 TempFile
aBase( nullptr, true );
466 if ( aBase
.IsValid() )
467 // use it in case of success
468 rTempNameBase_Impl
= aBase
.aName
;
470 // return system path of used directory
471 FileBase::getSystemPathFromFileURL( rTempNameBase_Impl
, aTmp
);
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */