Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <sal/config.h>
21 :
22 : #include <cassert>
23 :
24 : #include <com/sun/star/ucb/UniversalContentBroker.hpp>
25 : #include <comphelper/processfactory.hxx>
26 : #include <unotools/tempfile.hxx>
27 : #include <unotools/localfilehelper.hxx>
28 : #include <unotools/ucbstreamhelper.hxx>
29 : #include <ucbhelper/fileidentifierconverter.hxx>
30 : #include <rtl/ustring.hxx>
31 : #include <rtl/instance.hxx>
32 : #include <osl/detail/file.h>
33 : #include <osl/file.hxx>
34 : #include <tools/time.hxx>
35 : #include <tools/debug.hxx>
36 : #include <stdio.h>
37 :
38 : #ifdef UNX
39 : #include <sys/stat.h>
40 : #include <sys/types.h>
41 : #include <unistd.h>
42 : #endif
43 :
44 : using namespace osl;
45 :
46 : namespace
47 : {
48 : struct TempNameBase_Impl
49 : : public rtl::Static< OUString, TempNameBase_Impl > {};
50 : }
51 :
52 : namespace utl
53 : {
54 :
55 0 : OUString getParentName( const OUString& aFileName )
56 : {
57 0 : sal_Int32 lastIndex = aFileName.lastIndexOf( '/' );
58 0 : OUString aParent = aFileName.copy( 0, lastIndex );
59 :
60 0 : if( aParent.endsWith(":") && aParent.getLength() == 6 )
61 0 : aParent += "/";
62 :
63 0 : if( aParent.equalsIgnoreAsciiCase( "file://" ) )
64 0 : aParent = "file:///";
65 :
66 0 : return aParent;
67 : }
68 :
69 25454 : bool ensuredir( const OUString& rUnqPath )
70 : {
71 25454 : OUString aPath;
72 25454 : if ( rUnqPath.isEmpty() )
73 0 : return false;
74 :
75 : // remove trailing slash
76 25454 : if ( rUnqPath.endsWith("/") )
77 116 : aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
78 : else
79 25338 : aPath = rUnqPath;
80 :
81 : // HACK: create directory on a mount point with nobrowse option
82 : // returns ENOSYS in any case !!
83 50908 : osl::Directory aDirectory( aPath );
84 25454 : osl::FileBase::RC nError = aDirectory.open();
85 25454 : aDirectory.close();
86 25454 : if( nError == osl::File::E_None )
87 25454 : return true;
88 :
89 : // try to create the directory
90 0 : nError = osl::Directory::create( aPath );
91 0 : bool bSuccess = ( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
92 0 : if( !bSuccess )
93 : {
94 : // perhaps parent(s) don't exist
95 0 : OUString aParentDir = getParentName( aPath );
96 0 : if ( aParentDir != aPath )
97 : {
98 0 : bSuccess = ensuredir( getParentName( aPath ) );
99 :
100 : // After parent directory structure exists try it one's more
101 0 : if ( bSuccess )
102 : {
103 : // Parent directory exists, retry creation of directory
104 0 : nError = osl::Directory::create( aPath );
105 0 : bSuccess =( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
106 : }
107 0 : }
108 : }
109 :
110 25454 : return bSuccess;
111 : }
112 :
113 26301 : OUString ConstructTempDir_Impl( const OUString* pParent )
114 : {
115 26301 : OUString aName;
116 26301 : if ( pParent && !pParent->isEmpty() )
117 : {
118 : com::sun::star::uno::Reference<
119 : com::sun::star::ucb::XUniversalContentBroker > pBroker(
120 : com::sun::star::ucb::UniversalContentBroker::create(
121 847 : comphelper::getProcessComponentContext() ) );
122 :
123 : // if parent given try to use it
124 1694 : OUString aTmp( *pParent );
125 :
126 : // test for valid filename
127 1694 : OUString aRet;
128 : ::osl::FileBase::getFileURLFromSystemPath(
129 : ::ucbhelper::getSystemPathFromFileURL( pBroker, aTmp ),
130 847 : aRet );
131 847 : if ( !aRet.isEmpty() )
132 : {
133 847 : ::osl::DirectoryItem aItem;
134 847 : sal_Int32 i = aRet.getLength();
135 847 : if ( aRet[i-1] == '/' )
136 21 : i--;
137 :
138 847 : if ( DirectoryItem::get( aRet.copy(0, i), aItem ) == FileBase::E_None )
139 847 : aName = aRet;
140 847 : }
141 : }
142 :
143 26301 : if ( aName.isEmpty() )
144 : {
145 25454 : OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
146 25454 : if (rTempNameBase_Impl.isEmpty())
147 : {
148 90 : OUString ustrTempDirURL;
149 : ::osl::FileBase::RC rc = ::osl::File::getTempDirURL(
150 90 : ustrTempDirURL );
151 90 : if (rc == ::osl::FileBase::E_None)
152 90 : rTempNameBase_Impl = ustrTempDirURL;
153 : }
154 : // if no parent or invalid parent : use default directory
155 : DBG_ASSERT( !rTempNameBase_Impl.isEmpty(), "No TempDir!" );
156 25454 : aName = rTempNameBase_Impl;
157 25454 : ensuredir( aName );
158 : }
159 :
160 : // Make sure that directory ends with a separator
161 26301 : if( !aName.isEmpty() && !aName.endsWith("/") )
162 26164 : aName += "/";
163 :
164 26301 : return aName;
165 : }
166 :
167 26301 : class Tokens {
168 : public:
169 : virtual bool next(OUString *) = 0;
170 :
171 : protected:
172 26301 : virtual ~Tokens() {} // avoid warnings
173 : };
174 :
175 853 : class SequentialTokens: public Tokens {
176 : public:
177 853 : explicit SequentialTokens(bool showZero): m_value(0), m_show(showZero) {}
178 :
179 943 : bool next(OUString * token) SAL_OVERRIDE {
180 : assert(token != 0);
181 943 : if (m_value == SAL_MAX_UINT32) {
182 0 : return false;
183 : }
184 943 : *token = m_show ? OUString::number(m_value) : OUString();
185 943 : ++m_value;
186 943 : m_show = true;
187 943 : return true;
188 : }
189 :
190 : private:
191 : sal_uInt32 m_value;
192 : bool m_show;
193 : };
194 :
195 25448 : class UniqueTokens: public Tokens {
196 : public:
197 25448 : UniqueTokens(): m_count(0) {}
198 :
199 25448 : bool next(OUString * token) SAL_OVERRIDE {
200 : assert(token != 0);
201 : // Because of the shared globalValue, no single instance of UniqueTokens
202 : // is guaranteed to exhaustively test all 36^6 possible values, but stop
203 : // after that many attempts anyway:
204 25448 : sal_uInt32 radix = 36;
205 25448 : sal_uInt32 max = radix * radix * radix * radix * radix * radix;
206 : // 36^6 == 2'176'782'336 < SAL_MAX_UINT32 == 4'294'967'295
207 25448 : if (m_count == max) {
208 0 : return false;
209 : }
210 : sal_uInt32 v;
211 : {
212 25448 : osl::MutexGuard g(osl::Mutex::getGlobalMutex());
213 : globalValue
214 25448 : = ((globalValue == SAL_MAX_UINT32
215 25242 : ? tools::Time::GetSystemTicks() : globalValue + 1)
216 50690 : % max);
217 25448 : v = globalValue;
218 : }
219 25448 : *token = OUString::number(v, radix);
220 25448 : ++m_count;
221 25448 : return true;
222 : }
223 :
224 : private:
225 : static sal_uInt32 globalValue;
226 :
227 : sal_uInt32 m_count;
228 : };
229 :
230 : sal_uInt32 UniqueTokens::globalValue = SAL_MAX_UINT32;
231 :
232 26301 : OUString lcl_createName(
233 : const OUString& rLeadingChars, Tokens & tokens, const OUString* pExtension,
234 : const OUString* pParent, bool bDirectory, bool bKeep, bool bLock)
235 : {
236 26301 : OUString aName = ConstructTempDir_Impl( pParent ) + rLeadingChars;;
237 52602 : OUString token;
238 26301 : while (tokens.next(&token))
239 : {
240 26391 : OUString aTmp( aName + token );
241 26391 : if ( pExtension )
242 942 : aTmp += *pExtension;
243 : else
244 25449 : aTmp += ".tmp";
245 26391 : if ( bDirectory )
246 : {
247 : FileBase::RC err = Directory::create(
248 : aTmp,
249 : (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write
250 127 : | osl_File_OpenFlag_Private));
251 127 : if ( err == FileBase::E_None )
252 : {
253 : // !bKeep: only for creating a name, not a file or directory
254 127 : if ( bKeep || Directory::remove( aTmp ) == FileBase::E_None )
255 127 : return aTmp;
256 : else
257 0 : return OUString();
258 : }
259 0 : else if ( err != FileBase::E_EXIST )
260 : // if f.e. name contains invalid chars stop trying to create dirs
261 0 : return OUString();
262 : }
263 : else
264 : {
265 : DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" );
266 26264 : File aFile( aTmp );
267 : FileBase::RC err = aFile.open(
268 : osl_File_OpenFlag_Create | osl_File_OpenFlag_Private
269 26264 : | (bLock ? 0 : osl_File_OpenFlag_NoLock));
270 26264 : if ( err == FileBase::E_None || (bLock && err == FileBase::E_NOLCK) )
271 : {
272 26174 : aFile.close();
273 26174 : return aTmp;
274 : }
275 90 : else if ( err != FileBase::E_EXIST )
276 : {
277 : // if f.e. name contains invalid chars stop trying to create dirs
278 : // but if there is a folder with such name proceed further
279 :
280 0 : DirectoryItem aTmpItem;
281 0 : FileStatus aTmpStatus( osl_FileStatus_Mask_Type );
282 0 : if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
283 0 : || aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
284 0 : || aTmpStatus.getFileType() != FileStatus::Directory )
285 0 : return OUString();
286 90 : }
287 : }
288 90 : }
289 26301 : return OUString();
290 : }
291 :
292 25448 : OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = true )
293 : {
294 25448 : OUString aEyeCatcher = "lu";
295 : #ifdef UNX
296 : #ifdef DBG_UTIL
297 : const char* eye = getenv("LO_TESTNAME");
298 : if(eye)
299 : {
300 : aEyeCatcher = OUString(eye, strlen(eye), RTL_TEXTENCODING_ASCII_US);
301 : }
302 : #else
303 25448 : static const pid_t pid = getpid();
304 25448 : static const OUString aPidString = OUString::number(pid);
305 25448 : aEyeCatcher += aPidString;
306 : #endif
307 : #endif
308 50896 : UniqueTokens t;
309 50896 : return lcl_createName(aEyeCatcher, t, 0, pParent, bDir, bKeep, false);
310 : }
311 :
312 0 : OUString TempFile::CreateTempName()
313 : {
314 0 : OUString aName(CreateTempName_Impl( 0, false ));
315 :
316 : // convert to file URL
317 0 : OUString aTmp;
318 0 : if ( !aName.isEmpty() )
319 0 : FileBase::getSystemPathFromFileURL( aName, aTmp );
320 0 : return aTmp;
321 : }
322 :
323 25448 : TempFile::TempFile( const OUString* pParent, bool bDirectory )
324 : : pStream( 0 )
325 : , bIsDirectory( bDirectory )
326 25448 : , bKillingFileEnabled( false )
327 : {
328 25448 : aName = CreateTempName_Impl( pParent, true, bDirectory );
329 25448 : }
330 :
331 853 : TempFile::TempFile( const OUString& rLeadingChars, bool _bStartWithZero, const OUString* pExtension, const OUString* pParent, bool bDirectory)
332 : : pStream( 0 )
333 : , bIsDirectory( bDirectory )
334 853 : , bKillingFileEnabled( false )
335 : {
336 853 : SequentialTokens t(_bStartWithZero);
337 853 : aName = lcl_createName(rLeadingChars, t, pExtension, pParent, bDirectory, true, true);
338 853 : }
339 :
340 52548 : TempFile::~TempFile()
341 : {
342 26274 : delete pStream;
343 26274 : if ( bKillingFileEnabled )
344 : {
345 22105 : if ( bIsDirectory )
346 : {
347 : // at the moment no recursiv algorithm present
348 0 : Directory::remove( aName );
349 : }
350 : else
351 : {
352 22105 : File::remove( aName );
353 : }
354 : }
355 26274 : }
356 :
357 235 : bool TempFile::IsValid() const
358 : {
359 235 : return !aName.isEmpty();
360 : }
361 :
362 33760 : OUString TempFile::GetFileName() const
363 : {
364 33760 : OUString aTmp;
365 33760 : FileBase::getSystemPathFromFileURL( aName, aTmp );
366 33760 : return aTmp;
367 : }
368 :
369 31516 : OUString TempFile::GetURL()
370 : {
371 31516 : if ( aURL.isEmpty() )
372 : {
373 24977 : OUString const name(GetFileName());
374 24977 : LocalFileHelper::ConvertPhysicalNameToURL(name, aURL);
375 24977 : assert((name.isEmpty() || !aURL.isEmpty()) && "TempFile::GetURL failed: unit test is leaking temp files, add the ucpfile1 component!");
376 : }
377 :
378 31516 : return aURL;
379 : }
380 :
381 13531 : SvStream* TempFile::GetStream( StreamMode eMode )
382 : {
383 13531 : if ( !pStream )
384 : {
385 13531 : if ( !GetURL().isEmpty() )
386 13531 : pStream = UcbStreamHelper::CreateStream( aURL, eMode, true /* bFileExists */ );
387 : else
388 0 : pStream = new SvMemoryStream( NULL, 0, eMode );
389 : }
390 :
391 13531 : return pStream;
392 : }
393 :
394 6587 : void TempFile::CloseStream()
395 : {
396 6587 : if ( pStream )
397 : {
398 6587 : delete pStream;
399 6587 : pStream = NULL;
400 : }
401 6587 : }
402 :
403 116 : OUString TempFile::SetTempNameBaseDirectory( const OUString &rBaseName )
404 : {
405 116 : if( rBaseName.isEmpty() )
406 0 : return OUString();
407 :
408 116 : OUString aUnqPath( rBaseName );
409 :
410 : // remove trailing slash
411 116 : if ( rBaseName.endsWith("/") )
412 0 : aUnqPath = rBaseName.copy( 0, rBaseName.getLength() - 1 );
413 :
414 : // try to create the directory
415 116 : bool bRet = false;
416 116 : osl::FileBase::RC err = osl::Directory::create( aUnqPath );
417 116 : if ( err != FileBase::E_None && err != FileBase::E_EXIST )
418 : // perhaps parent(s) don't exist
419 0 : bRet = ensuredir( aUnqPath );
420 : else
421 116 : bRet = true;
422 :
423 : // failure to create base directory means returning an empty string
424 232 : OUString aTmp;
425 116 : if ( bRet )
426 : {
427 : // append own internal directory
428 116 : bRet = true;
429 116 : OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
430 116 : rTempNameBase_Impl = rBaseName;
431 116 : rTempNameBase_Impl += OUString('/');
432 :
433 116 : TempFile aBase( NULL, true );
434 116 : if ( aBase.IsValid() )
435 : // use it in case of success
436 116 : rTempNameBase_Impl = aBase.aName;
437 :
438 : // return system path of used directory
439 116 : FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
440 : }
441 :
442 232 : return aTmp;
443 : }
444 : }
445 :
446 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|