lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / unotools / source / ucbhelper / tempfile.cxx
bloba659428f862307aec7e8f7224441551eb952f36c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <cassert>
23 #include <utility>
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>
34 #include <stdio.h>
36 #ifdef UNX
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #elif defined( WNT )
41 #include <process.h>
42 #endif
44 using namespace osl;
46 namespace
48 struct TempNameBase_Impl
49 : public rtl::Static< OUString, TempNameBase_Impl > {};
52 namespace utl
55 static OUString getParentName( const OUString& aFileName )
57 sal_Int32 lastIndex = aFileName.lastIndexOf( '/' );
58 OUString aParent;
60 if (lastIndex > -1)
62 aParent = aFileName.copy(0, lastIndex);
64 if (aParent.endsWith(":") && aParent.getLength() == 6)
65 aParent += "/";
67 if (aParent.equalsIgnoreAsciiCase("file://"))
68 aParent = "file:///";
71 return aParent;
74 static bool ensuredir( const OUString& rUnqPath )
76 OUString aPath;
77 if ( rUnqPath.isEmpty() )
78 return false;
80 // remove trailing slash
81 if ( rUnqPath.endsWith("/") )
82 aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
83 else
84 aPath = rUnqPath;
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();
90 aDirectory.close();
91 if( nError == osl::File::E_None )
92 return true;
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 );
97 if( !bSuccess )
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
106 if ( bSuccess )
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 );
115 return bSuccess;
118 static OUString ConstructTempDir_Impl( const OUString* pParent )
120 OUString aName;
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.
124 #ifndef IOS
125 if ( pParent && !pParent->isEmpty() )
127 // test for valid filename
128 OUString aRet;
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] == '/' )
137 i--;
139 if ( DirectoryItem::get( aRet.copy(0, i), aItem ) == FileBase::E_None )
140 aName = aRet;
143 #else
144 (void) pParent;
145 #endif
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(
154 ustrTempDirURL );
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;
161 ensuredir( aName );
164 // Make sure that directory ends with a separator
165 if( !aName.isEmpty() && !aName.endsWith("/") )
166 aName += "/";
168 return aName;
171 class Tokens {
172 public:
173 virtual bool next(OUString *) = 0;
175 protected:
176 virtual ~Tokens() {} // avoid warnings
179 class SequentialTokens: public Tokens {
180 public:
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) {
186 return false;
188 *token = m_show ? OUString::number(m_value) : OUString();
189 ++m_value;
190 m_show = true;
191 return true;
194 private:
195 sal_uInt32 m_value;
196 bool m_show;
199 class UniqueTokens: public Tokens {
200 public:
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) {
212 return false;
214 sal_uInt32 v;
216 osl::MutexGuard g(osl::Mutex::getGlobalMutex());
217 globalValue
218 = ((globalValue == SAL_MAX_UINT32
219 ? tools::Time::GetSystemTicks() : globalValue + 1)
220 % max);
221 v = globalValue;
223 *token = OUString::number(v, radix);
224 ++m_count;
225 return true;
228 private:
229 static sal_uInt32 globalValue;
231 sal_uInt32 m_count;
234 sal_uInt32 UniqueTokens::globalValue = SAL_MAX_UINT32;
236 namespace
238 class TempDirCreatedObserver : public DirectoryCreationObserver
240 public:
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("/");
258 if (-1 != nOffset)
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 )
264 return OUString();
267 aName += rLeadingChars;
269 OUString token;
270 while (tokens.next(&token))
272 OUString aTmp( aName + token );
273 if ( pExtension )
274 aTmp += *pExtension;
275 else
276 aTmp += ".tmp";
277 if ( bDirectory )
279 FileBase::RC err = Directory::create(
280 aTmp,
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 )
287 return aTmp;
288 else
289 return OUString();
291 else if ( err != FileBase::E_EXIST )
292 // if f.e. name contains invalid chars stop trying to create dirs
293 return OUString();
295 else
297 DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" );
298 File aFile( aTmp );
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) )
304 aFile.close();
305 return aTmp;
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 )
317 return OUString();
321 return OUString();
324 static OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = true )
326 OUString aEyeCatcher = "lu";
327 #ifdef UNX
328 #ifdef DBG_UTIL
329 const char* eye = getenv("LO_TESTNAME");
330 if(eye)
332 aEyeCatcher = OUString(eye, strlen(eye), RTL_TEXTENCODING_ASCII_US);
334 #else
335 static const pid_t pid = getpid();
336 static const OUString aPidString = OUString::number(pid);
337 aEyeCatcher += aPidString;
338 #endif
339 #elif defined(WNT)
340 static const int pid = _getpid();
341 static const OUString aPidString = OUString::number(pid);
342 aEyeCatcher += aPidString;
343 #endif
344 UniqueTokens t;
345 return lcl_createName( aEyeCatcher, t, nullptr, pParent, bDir, bKeep,
346 false, false);
349 OUString TempFile::CreateTempName()
351 OUString aName(CreateTempName_Impl( nullptr, false ));
353 // convert to file URL
354 OUString aTmp;
355 if ( !aName.isEmpty() )
356 FileBase::getSystemPathFromFileURL( aName, aTmp );
357 return 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()
387 pStream.reset();
388 if ( bKillingFileEnabled )
390 if ( bIsDirectory )
392 // at the moment no recursiv algorithm present
393 Directory::remove( aName );
395 else
397 File::remove( aName );
402 bool TempFile::IsValid() const
404 return !aName.isEmpty();
407 OUString TempFile::GetFileName() const
409 OUString aTmp;
410 FileBase::getSystemPathFromFileURL(aName, aTmp);
411 return aTmp;
414 OUString const & TempFile::GetURL() const
416 return aName;
419 SvStream* TempFile::GetStream( StreamMode eMode )
421 if (!pStream)
423 if (!aName.isEmpty())
424 pStream.reset(new SvFileStream(aName, eMode));
425 else
426 pStream.reset(new SvMemoryStream(nullptr, 0, eMode));
429 return pStream.get();
432 void TempFile::CloseStream()
434 pStream.reset();
437 OUString TempFile::SetTempNameBaseDirectory( const OUString &rBaseName )
439 if( rBaseName.isEmpty() )
440 return OUString();
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
449 bool bRet = false;
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 );
454 else
455 bRet = true;
457 // failure to create base directory means returning an empty string
458 OUString aTmp;
459 if ( bRet )
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 );
474 return aTmp;
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */