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 <com/sun/star/ucb/UniversalContentBroker.hpp>
23 : #include <comphelper/processfactory.hxx>
24 : #include <unotools/tempfile.hxx>
25 : #include <tools/tempfile.hxx>
26 : #include <unotools/localfilehelper.hxx>
27 : #include <unotools/ucbstreamhelper.hxx>
28 : #include <ucbhelper/fileidentifierconverter.hxx>
29 : #include <rtl/ustring.hxx>
30 : #include <rtl/instance.hxx>
31 : #include <osl/file.hxx>
32 : #include <tools/time.hxx>
33 : #include <tools/debug.hxx>
34 : #include <stdio.h>
35 :
36 : #ifdef UNX
37 : #include <sys/stat.h>
38 : #endif
39 :
40 : using namespace osl;
41 :
42 : namespace
43 : {
44 : struct TempNameBase_Impl
45 : : public rtl::Static< ::rtl::OUString, TempNameBase_Impl > {};
46 : }
47 :
48 : namespace utl
49 : {
50 :
51 1424 : struct TempFile_Impl
52 : {
53 : String aName;
54 : String aURL;
55 : SvStream* pStream;
56 : sal_Bool bIsDirectory;
57 :
58 1774 : TempFile_Impl()
59 1774 : : pStream(0)
60 1774 : {}
61 : };
62 :
63 0 : rtl::OUString getParentName( const rtl::OUString& aFileName )
64 : {
65 0 : sal_Int32 lastIndex = aFileName.lastIndexOf( sal_Unicode('/') );
66 0 : rtl::OUString aParent = aFileName.copy( 0,lastIndex );
67 :
68 0 : if( aParent[ aParent.getLength()-1] == sal_Unicode(':') && aParent.getLength() == 6 )
69 0 : aParent += rtl::OUString("/");
70 :
71 0 : if( 0 == aParent.compareToAscii( "file://" ) )
72 0 : aParent = rtl::OUString("file:///");
73 :
74 0 : return aParent;
75 : }
76 :
77 1715 : sal_Bool ensuredir( const rtl::OUString& rUnqPath )
78 : {
79 1715 : rtl::OUString aPath;
80 1715 : if ( rUnqPath.isEmpty() )
81 0 : return sal_False;
82 :
83 : // remove trailing slash
84 1715 : if ( rUnqPath[ rUnqPath.getLength() - 1 ] == sal_Unicode( '/' ) )
85 0 : aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
86 : else
87 1715 : aPath = rUnqPath;
88 :
89 : // HACK: create directory on a mount point with nobrowse option
90 : // returns ENOSYS in any case !!
91 1715 : osl::Directory aDirectory( aPath );
92 : #ifdef UNX
93 : /* RW permission for the user only! */
94 1715 : mode_t old_mode = umask(077);
95 : #endif
96 1715 : osl::FileBase::RC nError = aDirectory.open();
97 : #ifdef UNX
98 1715 : umask(old_mode);
99 : #endif
100 1715 : aDirectory.close();
101 1715 : if( nError == osl::File::E_None )
102 1715 : return sal_True;
103 :
104 : // try to create the directory
105 0 : nError = osl::Directory::create( aPath );
106 0 : sal_Bool bSuccess = ( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
107 0 : if( !bSuccess )
108 : {
109 : // perhaps parent(s) don't exist
110 0 : rtl::OUString aParentDir = getParentName( aPath );
111 0 : if ( aParentDir != aPath )
112 : {
113 0 : bSuccess = ensuredir( getParentName( aPath ) );
114 :
115 : // After parent directory structure exists try it one's more
116 0 : if ( bSuccess )
117 : {
118 : // Parent directory exists, retry creation of directory
119 0 : nError = osl::Directory::create( aPath );
120 0 : bSuccess =( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
121 : }
122 0 : }
123 : }
124 :
125 0 : return bSuccess;
126 : }
127 :
128 1774 : String ConstructTempDir_Impl( const String* pParent )
129 : {
130 1774 : String aName;
131 1774 : if ( pParent && pParent->Len() )
132 : {
133 : com::sun::star::uno::Reference<
134 : com::sun::star::ucb::XUniversalContentBroker > pBroker(
135 : com::sun::star::ucb::UniversalContentBroker::create(
136 118 : comphelper::getProcessComponentContext() ) );
137 :
138 : // if parent given try to use it
139 118 : rtl::OUString aTmp( *pParent );
140 :
141 : // test for valid filename
142 118 : rtl::OUString aRet;
143 : ::osl::FileBase::getFileURLFromSystemPath(
144 : ::ucbhelper::getSystemPathFromFileURL( pBroker, aTmp ),
145 118 : aRet );
146 118 : if ( !aRet.isEmpty() )
147 : {
148 118 : ::osl::DirectoryItem aItem;
149 118 : sal_Int32 i = aRet.getLength();
150 118 : if ( aRet[i-1] == '/' )
151 59 : i--;
152 :
153 118 : if ( DirectoryItem::get( aRet.copy(0, i), aItem ) == FileBase::E_None )
154 59 : aName = aRet;
155 118 : }
156 : }
157 :
158 1774 : if ( !aName.Len() )
159 : {
160 1715 : ::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
161 1715 : if (rTempNameBase_Impl.isEmpty())
162 : {
163 17 : ::rtl::OUString ustrTempDirURL;
164 : ::osl::FileBase::RC rc = ::osl::File::getTempDirURL(
165 17 : ustrTempDirURL );
166 17 : if (rc == ::osl::FileBase::E_None)
167 17 : rTempNameBase_Impl = ustrTempDirURL;
168 : }
169 : // if no parent or invalid parent : use default directory
170 : DBG_ASSERT( !rTempNameBase_Impl.isEmpty(), "No TempDir!" );
171 1715 : aName = rTempNameBase_Impl;
172 1715 : ensuredir( aName );
173 : }
174 :
175 : // Make sure that directory ends with a separator
176 1774 : xub_StrLen i = aName.Len();
177 1774 : if( i>0 && aName.GetChar(i-1) != '/' )
178 1715 : aName += '/';
179 :
180 1774 : return aName;
181 : }
182 :
183 1655 : void CreateTempName_Impl( String& rName, sal_Bool bKeep, sal_Bool bDir = sal_True )
184 : {
185 : // add a suitable tempname
186 : // 36 ** 6 == 2176782336
187 1655 : unsigned const nRadix = 36;
188 1655 : unsigned long const nMax = (nRadix*nRadix*nRadix*nRadix*nRadix*nRadix);
189 1655 : String aName( rName );
190 1655 : aName += rtl::OUString( "lu" );
191 :
192 1655 : rName.Erase();
193 1655 : static unsigned long u = Time::GetSystemTicks() % nMax;
194 3310 : for ( unsigned long nSeed = u; ++u != nSeed; )
195 : {
196 1655 : u %= nMax;
197 1655 : String aTmp( aName );
198 1655 : aTmp += rtl::OUString::valueOf(static_cast<sal_Int64>(u), nRadix);
199 1655 : aTmp += rtl::OUString( ".tmp" );
200 :
201 1655 : if ( bDir )
202 : {
203 : #ifdef UNX /* RW permission for the user only! */
204 0 : mode_t old_mode = umask(077);
205 : #endif
206 0 : FileBase::RC err = Directory::create( aTmp );
207 : #ifdef UNX
208 0 : umask(old_mode);
209 : #endif
210 0 : if ( err == FileBase::E_None )
211 : {
212 : // !bKeep: only for creating a name, not a file or directory
213 0 : if ( bKeep || Directory::remove( aTmp ) == FileBase::E_None )
214 0 : rName = aTmp;
215 : break;
216 : }
217 0 : else if ( err != FileBase::E_EXIST )
218 : {
219 : // if f.e. name contains invalid chars stop trying to create dirs
220 : break;
221 : }
222 : }
223 : else
224 : {
225 : DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" );
226 1655 : File aFile( aTmp );
227 : #ifdef UNX /* RW permission for the user only! */
228 1655 : mode_t old_mode = umask(077);
229 : #endif
230 1655 : FileBase::RC err = aFile.open( osl_File_OpenFlag_Create | osl_File_OpenFlag_NoLock );
231 : #ifdef UNX
232 1655 : umask(old_mode);
233 : #endif
234 1655 : if ( err == FileBase::E_None )
235 : {
236 1655 : rName = aTmp;
237 1655 : aFile.close();
238 : break;
239 : }
240 0 : else if ( err != FileBase::E_EXIST )
241 : {
242 : // if f.e. name contains invalid chars stop trying to create files
243 : // but if there is a folder with such name proceed further
244 :
245 0 : DirectoryItem aTmpItem;
246 0 : FileStatus aTmpStatus( osl_FileStatus_Mask_Type );
247 0 : if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
248 0 : || aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
249 0 : || aTmpStatus.getFileType() != FileStatus::Directory )
250 0 : break;
251 1655 : }
252 : }
253 3310 : }
254 1655 : }
255 :
256 119 : void lcl_createName(TempFile_Impl& _rImpl,const String& rLeadingChars,sal_Bool _bStartWithZero, const String* pExtension, const String* pParent, sal_Bool bDirectory)
257 : {
258 119 : _rImpl.bIsDirectory = bDirectory;
259 :
260 : // get correct directory
261 119 : String aName = ConstructTempDir_Impl( pParent );
262 :
263 119 : sal_Bool bUseNumber = _bStartWithZero;
264 : // now use special naming scheme ( name takes leading chars and an index counting up from zero
265 119 : aName += rLeadingChars;
266 119 : for ( sal_Int32 i=0;; i++ )
267 : {
268 119 : String aTmp( aName );
269 119 : if ( bUseNumber )
270 119 : aTmp += String::CreateFromInt32( i );
271 119 : bUseNumber = sal_True;
272 119 : if ( pExtension )
273 118 : aTmp += *pExtension;
274 : else
275 1 : aTmp += rtl::OUString( ".tmp" );
276 119 : if ( bDirectory )
277 : {
278 0 : FileBase::RC err = Directory::create( aTmp );
279 0 : if ( err == FileBase::E_None )
280 : {
281 0 : _rImpl.aName = aTmp;
282 : break;
283 : }
284 0 : else if ( err != FileBase::E_EXIST )
285 : // if f.e. name contains invalid chars stop trying to create dirs
286 : break;
287 : }
288 : else
289 : {
290 119 : File aFile( aTmp );
291 : #ifdef UNX
292 : /* RW permission for the user only! */
293 119 : mode_t old_mode = umask(077);
294 : #endif
295 119 : FileBase::RC err = aFile.open(osl_File_OpenFlag_Create);
296 : #ifdef UNX
297 119 : umask(old_mode);
298 : #endif
299 119 : if ( err == FileBase::E_None || err == FileBase::E_NOLCK )
300 : {
301 119 : _rImpl.aName = aTmp;
302 119 : aFile.close();
303 : break;
304 : }
305 0 : else if ( err != FileBase::E_EXIST )
306 : {
307 : // if f.e. name contains invalid chars stop trying to create dirs
308 : // but if there is a folder with such name proceed further
309 :
310 0 : DirectoryItem aTmpItem;
311 0 : FileStatus aTmpStatus( osl_FileStatus_Mask_Type );
312 0 : if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
313 0 : || aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
314 0 : || aTmpStatus.getFileType() != FileStatus::Directory )
315 0 : break;
316 119 : }
317 : }
318 0 : if ( !_bStartWithZero )
319 0 : aTmp += String::CreateFromInt32( i );
320 238 : }
321 119 : }
322 :
323 :
324 0 : String TempFile::CreateTempName( const String* pParent )
325 : {
326 : // get correct directory
327 0 : String aName = ConstructTempDir_Impl( pParent );
328 :
329 : // get TempFile name with default naming scheme
330 0 : CreateTempName_Impl( aName, sal_False );
331 :
332 : // convert to file URL
333 0 : rtl::OUString aTmp;
334 0 : if ( aName.Len() )
335 0 : FileBase::getSystemPathFromFileURL( aName, aTmp );
336 0 : return aTmp;
337 : }
338 :
339 1655 : TempFile::TempFile( const String* pParent, sal_Bool bDirectory )
340 1655 : : pImp( new TempFile_Impl )
341 3310 : , bKillingFileEnabled( sal_False )
342 : {
343 1655 : pImp->bIsDirectory = bDirectory;
344 :
345 : // get correct directory
346 1655 : pImp->aName = ConstructTempDir_Impl( pParent );
347 :
348 : // get TempFile with default naming scheme
349 1655 : CreateTempName_Impl( pImp->aName, sal_True, bDirectory );
350 1655 : }
351 :
352 119 : TempFile::TempFile( const String& rLeadingChars, const String* pExtension, const String* pParent, sal_Bool bDirectory)
353 119 : : pImp( new TempFile_Impl )
354 238 : , bKillingFileEnabled( sal_False )
355 : {
356 119 : lcl_createName(*pImp,rLeadingChars,sal_True, pExtension, pParent, bDirectory);
357 119 : }
358 0 : TempFile::TempFile( const String& rLeadingChars,sal_Bool _bStartWithZero, const String* pExtension, const String* pParent, sal_Bool bDirectory)
359 0 : : pImp( new TempFile_Impl )
360 0 : , bKillingFileEnabled( sal_False )
361 : {
362 0 : lcl_createName(*pImp,rLeadingChars,_bStartWithZero, pExtension, pParent, bDirectory);
363 0 : }
364 :
365 1424 : TempFile::~TempFile()
366 : {
367 1424 : delete pImp->pStream;
368 1424 : if ( bKillingFileEnabled )
369 : {
370 1300 : if ( pImp->bIsDirectory )
371 : {
372 : // at the moment no recursiv algorithm present
373 0 : Directory::remove( pImp->aName );
374 : }
375 : else
376 : {
377 1300 : File::remove( pImp->aName );
378 : }
379 : }
380 :
381 1424 : delete pImp;
382 1424 : }
383 :
384 4 : sal_Bool TempFile::IsValid() const
385 : {
386 4 : return pImp->aName.Len() != 0;
387 : }
388 :
389 2118 : String TempFile::GetFileName() const
390 : {
391 2118 : rtl::OUString aTmp;
392 2118 : FileBase::getSystemPathFromFileURL( pImp->aName, aTmp );
393 2118 : return aTmp;
394 : }
395 :
396 2193 : String TempFile::GetURL() const
397 : {
398 2193 : if ( !pImp->aURL.Len() )
399 : {
400 1771 : rtl::OUString aTmp;
401 1771 : LocalFileHelper::ConvertPhysicalNameToURL( GetFileName(), aTmp );
402 1771 : pImp->aURL = aTmp;
403 : }
404 :
405 2193 : return pImp->aURL;
406 : }
407 :
408 1246 : SvStream* TempFile::GetStream( StreamMode eMode )
409 : {
410 1246 : if ( !pImp->pStream )
411 : {
412 1246 : if ( GetURL().Len() )
413 1246 : pImp->pStream = UcbStreamHelper::CreateStream( pImp->aURL, eMode, sal_True /* bFileExists */ );
414 : else
415 0 : pImp->pStream = new SvMemoryStream( eMode );
416 : }
417 :
418 1246 : return pImp->pStream;
419 : }
420 :
421 604 : void TempFile::CloseStream()
422 : {
423 604 : if ( pImp->pStream )
424 : {
425 604 : delete pImp->pStream;
426 604 : pImp->pStream = NULL;
427 : }
428 604 : }
429 :
430 0 : String TempFile::SetTempNameBaseDirectory( const String &rBaseName )
431 : {
432 0 : if( !rBaseName.Len() )
433 0 : return String();
434 :
435 0 : rtl::OUString aUnqPath( rBaseName );
436 :
437 : // remove trailing slash
438 0 : if ( rBaseName.GetChar( rBaseName.Len() - 1 ) == sal_Unicode( '/' ) )
439 0 : aUnqPath = rBaseName.Copy( 0, rBaseName.Len() - 1 );
440 :
441 : // try to create the directory
442 0 : sal_Bool bRet = sal_False;
443 0 : osl::FileBase::RC err = osl::Directory::create( aUnqPath );
444 0 : if ( err != FileBase::E_None && err != FileBase::E_EXIST )
445 : // perhaps parent(s) don't exist
446 0 : bRet = ensuredir( aUnqPath );
447 : else
448 0 : bRet = sal_True;
449 :
450 : // failure to create base directory means returning an empty string
451 0 : rtl::OUString aTmp;
452 0 : if ( bRet )
453 : {
454 : // append own internal directory
455 0 : bRet = sal_True;
456 0 : ::rtl::OUString &rTempNameBase_Impl = TempNameBase_Impl::get();
457 0 : rTempNameBase_Impl = rBaseName;
458 0 : rTempNameBase_Impl += rtl::OUString('/');
459 :
460 0 : TempFile aBase( NULL, sal_True );
461 0 : if ( aBase.IsValid() )
462 : // use it in case of success
463 0 : rTempNameBase_Impl = aBase.pImp->aName;
464 :
465 : // return system path of used directory
466 0 : FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
467 : }
468 :
469 0 : return aTmp;
470 : }
471 : }
472 :
473 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|