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