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 <stdio.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include "system.hxx"
26 #include <osl/file.h>
27 #include <osl/thread.h>
28 #include <rtl/ustrbuf.h>
29 #include <osl/diagnose.h>
30 #include <sal/macros.h>
31 
32 #include "file_url.hxx"
33 #include "file_impl.hxx"
34 
35 #include <cassert>
36 
osl_getTempDirURL(rtl_uString ** pustrTempDir)37 oslFileError SAL_CALL osl_getTempDirURL( rtl_uString** pustrTempDir )
38 {
39     oslFileError error;
40     /* described in environ(7) */
41     const char *pValue = getenv( "TMPDIR" );
42     rtl_uString *ustrTempPath = nullptr;
43 
44     if ( !pValue )
45         pValue = getenv( "TEMP" );
46 
47     if ( !pValue )
48         pValue = getenv( "TMP" );
49 
50     if ( !pValue )
51         pValue = "/tmp";
52 
53     auto nLen = strlen(pValue);
54     while (nLen > 1 && pValue[nLen - 1] == '/') // Allow path consisting of single "/"
55         --nLen;
56     rtl_string2UString( &ustrTempPath, pValue, nLen, osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
57     assert(ustrTempPath);
58     error = osl_getFileURLFromSystemPath( ustrTempPath, pustrTempDir );
59     rtl_uString_release( ustrTempPath );
60 
61     return error;
62 }
63 
64 /******************************************************************
65  * Generates a random unique file name. We're using the scheme
66  * from the standard c-lib function mkstemp to generate a more
67  * or less random unique file name
68  *
69  * @param rand_name
70  *        receives the random name
71  ******************************************************************/
72 
73 const char LETTERS[]        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
74 const int  COUNT_OF_LETTERS = SAL_N_ELEMENTS(LETTERS) - 1;
75 
76 #define RAND_NAME_LENGTH 6
77 
osl_gen_random_name_impl_(rtl_uString ** rand_name)78 static void osl_gen_random_name_impl_(rtl_uString** rand_name)
79 {
80     static uint64_t value;
81 
82     char     buffer[RAND_NAME_LENGTH];
83     struct   timeval tv;
84     uint64_t v;
85     int      i;
86 
87     gettimeofday(&tv, nullptr);
88 
89     value += (static_cast<uint64_t>(tv.tv_usec) << 16) ^ tv.tv_sec ^ getpid();
90 
91     v = value;
92 
93     for (i = 0; i < RAND_NAME_LENGTH; i++)
94     {
95         buffer[i] = LETTERS[v % COUNT_OF_LETTERS];
96         v        /= COUNT_OF_LETTERS;
97     }
98 
99     rtl_string2UString(
100             rand_name,
101             buffer,
102             RAND_NAME_LENGTH,
103             RTL_TEXTENCODING_ASCII_US,
104             OSTRING_TO_OUSTRING_CVTFLAGS);
105 
106     assert(*rand_name);
107 }
108 
109 /*****************************************************************
110  * Helper function
111  * Either use the directory provided or the result of
112  * osl_getTempDirUrl and return it as system path and file url
113  ****************************************************************/
114 
osl_setup_base_directory_impl_(rtl_uString * pustrDirectoryURL,rtl_uString ** ppustr_base_dir)115 static oslFileError osl_setup_base_directory_impl_(
116     rtl_uString*  pustrDirectoryURL,
117     rtl_uString** ppustr_base_dir)
118 {
119     rtl_uString* dir_url = nullptr;
120     rtl_uString* dir     = nullptr;
121     oslFileError error   = osl_File_E_None;
122 
123     if (pustrDirectoryURL)
124         rtl_uString_assign(&dir_url, pustrDirectoryURL);
125     else
126         error = osl_getTempDirURL(&dir_url);
127 
128     if (error == osl_File_E_None)
129     {
130         error = osl_getSystemPathFromFileURL_Ex(dir_url, &dir);
131         rtl_uString_release(dir_url);
132     }
133 
134     if (error == osl_File_E_None)
135     {
136         rtl_uString_assign(ppustr_base_dir, dir);
137         rtl_uString_release(dir);
138     }
139 
140     return error;
141 }
142 
143 /*****************************************************************
144  * osl_setup_createTempFile_impl
145  * validate input parameter, setup variables
146  ****************************************************************/
147 
osl_setup_createTempFile_impl_(rtl_uString * pustrDirectoryURL,oslFileHandle * pHandle,rtl_uString ** ppustrTempFileURL,rtl_uString ** ppustr_base_dir,bool * b_delete_on_close)148  static oslFileError osl_setup_createTempFile_impl_(
149      rtl_uString*   pustrDirectoryURL,
150     oslFileHandle* pHandle,
151     rtl_uString**  ppustrTempFileURL,
152     rtl_uString**  ppustr_base_dir,
153     bool*      b_delete_on_close)
154  {
155     oslFileError osl_error;
156 
157     OSL_PRECOND(((nullptr != pHandle) || (nullptr != ppustrTempFileURL)), "Invalid parameter!");
158 
159     if ((pHandle == nullptr) && (ppustrTempFileURL == nullptr))
160     {
161         osl_error = osl_File_E_INVAL;
162     }
163     else
164     {
165         osl_error = osl_setup_base_directory_impl_(
166             pustrDirectoryURL, ppustr_base_dir);
167 
168         *b_delete_on_close = (ppustrTempFileURL == nullptr);
169     }
170 
171     return osl_error;
172  }
173 
174 /*****************************************************************
175  * Create a unique file in the specified directory and return
176  * its name
177  ****************************************************************/
178 
osl_create_temp_file_impl_(const rtl_uString * pustr_base_directory,oslFileHandle * file_handle,rtl_uString ** ppustr_temp_file_name)179 static oslFileError osl_create_temp_file_impl_(
180     const rtl_uString* pustr_base_directory,
181     oslFileHandle* file_handle,
182     rtl_uString** ppustr_temp_file_name)
183 {
184     rtl_uString*        rand_name        = nullptr;
185     sal_uInt32          len_base_dir     = 0;
186     rtl_uString*        tmp_file_path    = nullptr;
187     rtl_uString*        tmp_file_url     = nullptr;
188     sal_Int32           capacity         = 0;
189     oslFileError        osl_error        = osl_File_E_None;
190     sal_Int32           offset_file_name;
191     const sal_Unicode*  puchr;
192 
193     OSL_PRECOND(pustr_base_directory, "Invalid Parameter");
194     OSL_PRECOND(file_handle, "Invalid Parameter");
195     OSL_PRECOND(ppustr_temp_file_name, "Invalid Parameter");
196 
197     len_base_dir = rtl_uString_getLength(pustr_base_directory);
198 
199     rtl_uString_new_WithLength(
200         &tmp_file_path,
201         (len_base_dir + 1 + RAND_NAME_LENGTH));
202     capacity = len_base_dir + 1 + RAND_NAME_LENGTH;
203 
204     rtl_uStringbuffer_insert(
205         &tmp_file_path,
206         &capacity,
207         0,
208         rtl_uString_getStr(const_cast<rtl_uString*>(pustr_base_directory)),
209         len_base_dir);
210 
211     offset_file_name = len_base_dir;
212 
213     puchr = rtl_uString_getStr(tmp_file_path);
214 
215     /* ensure that the last character is a '/' */
216 
217     if (puchr[len_base_dir - 1] != '/')
218     {
219         rtl_uStringbuffer_insert_ascii(
220             &tmp_file_path,
221             &capacity,
222             len_base_dir,
223             "/",
224             1);
225 
226         offset_file_name++;
227     }
228 
229     while(true) /* try until success */
230     {
231         osl_gen_random_name_impl_(&rand_name);
232 
233         rtl_uStringbuffer_insert(
234             &tmp_file_path,
235             &capacity,
236             offset_file_name,
237             rtl_uString_getStr(rand_name),
238             rtl_uString_getLength(rand_name));
239 
240         osl_error = osl_getFileURLFromSystemPath(
241             tmp_file_path, &tmp_file_url);
242 
243         if (osl_error == osl_File_E_None)
244         {
245             osl_error = openFile(
246                 tmp_file_url,
247                 file_handle,
248                 osl_File_OpenFlag_Read |
249                 osl_File_OpenFlag_Write |
250                 osl_File_OpenFlag_Create,
251                 S_IRUSR | S_IWUSR);
252         }
253 
254         /* in case of error osl_File_E_EXIST we simply try again else we give up */
255 
256         if (osl_error != osl_File_E_EXIST)
257         {
258             rtl_uString_release(rand_name);
259 
260             if (tmp_file_url)
261                 rtl_uString_release(tmp_file_url);
262 
263             break;
264         }
265     } /* while(1) */
266 
267     if (osl_error == osl_File_E_None)
268         rtl_uString_assign(ppustr_temp_file_name, tmp_file_path);
269 
270     rtl_uString_release(tmp_file_path);
271 
272     return osl_error;
273 }
274 
osl_createTempFile(rtl_uString * pustrDirectoryURL,oslFileHandle * pHandle,rtl_uString ** ppustrTempFileURL)275 oslFileError SAL_CALL osl_createTempFile(
276     rtl_uString*   pustrDirectoryURL,
277     oslFileHandle* pHandle,
278     rtl_uString**  ppustrTempFileURL)
279 {
280     rtl_uString*  base_directory     = nullptr;
281     oslFileHandle temp_file_handle;
282     bool      b_delete_on_close;
283     oslFileError  osl_error;
284 
285     osl_error = osl_setup_createTempFile_impl_(
286         pustrDirectoryURL,
287         pHandle,
288         ppustrTempFileURL,
289         &base_directory,
290         &b_delete_on_close);
291 
292     if (osl_error != osl_File_E_None)
293         return osl_error;
294 
295     rtl_uString*  temp_file_name = nullptr;
296     osl_error = osl_create_temp_file_impl_(
297         base_directory, &temp_file_handle, &temp_file_name);
298 
299     rtl_uString* temp_file_url = nullptr;
300     if (osl_error == osl_File_E_None)
301     {
302         osl_error = osl_getFileURLFromSystemPath(temp_file_name, &temp_file_url);
303         rtl_uString_release(temp_file_name);
304     }
305 
306     if (osl_error == osl_File_E_None)
307     {
308         if (b_delete_on_close)
309         {
310             osl_error = osl_removeFile(temp_file_url);
311 
312             if (osl_error == osl_File_E_None)
313             {
314                 *pHandle = temp_file_handle;
315                 temp_file_handle = nullptr;
316             }
317         }
318         else
319         {
320             if (pHandle)
321             {
322                 *pHandle = temp_file_handle;
323                 temp_file_handle = nullptr;
324             }
325 
326             rtl_uString_assign(ppustrTempFileURL, temp_file_url);
327         }
328 
329         rtl_uString_release(temp_file_url);
330     }
331 
332     if (temp_file_handle)
333         osl_closeFile(temp_file_handle);
334 
335     rtl_uString_release(base_directory);
336 
337     return osl_error;
338 }
339 
340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
341