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 :
21 : #include "file_url.h"
22 :
23 : #include "system.h"
24 :
25 : #include <limits.h>
26 : #include <errno.h>
27 : #include <strings.h>
28 : #include <unistd.h>
29 :
30 : #include "osl/file.hxx"
31 : #include <osl/security.h>
32 : #include <osl/diagnose.h>
33 : #include <osl/thread.h>
34 : #include <osl/process.h>
35 :
36 : #include <rtl/uri.h>
37 : #include <rtl/ustring.hxx>
38 : #include <rtl/ustrbuf.h>
39 : #include "rtl/textcvt.h"
40 :
41 : #include "file_error_transl.h"
42 : #include "file_path_helper.hxx"
43 :
44 : #include "uunxapi.hxx"
45 :
46 : /***************************************************
47 :
48 : General note
49 :
50 : This file contains the part that handles File URLs.
51 :
52 : File URLs as scheme specific notion of URIs
53 : (RFC2396) may be handled platform independend, but
54 : will not in osl which is considered wrong.
55 : Future version of osl should handle File URLs this
56 : way. In rtl/uri there is already an URI parser etc.
57 : so this code should be consolidated.
58 :
59 : **************************************************/
60 : /************************************************************************
61 : * ToDo
62 : *
63 : * Fix osl_getCanonicalName
64 : *
65 : ***********************************************************************/
66 :
67 :
68 : /***************************************************
69 : * namespace directives
70 : **************************************************/
71 :
72 : using namespace osl;
73 :
74 : /***************************************************
75 : * constants
76 : **************************************************/
77 :
78 : const sal_Unicode UNICHAR_SLASH = ((sal_Unicode)'/');
79 : const sal_Unicode UNICHAR_COLON = ((sal_Unicode)':');
80 : const sal_Unicode UNICHAR_DOT = ((sal_Unicode)'.');
81 :
82 : /******************************************************************************
83 : *
84 : * Exported Module Functions
85 : *
86 : *****************************************************************************/
87 :
88 : /* a slightly modified version of Pchar in rtl/source/uri.c */
89 : const sal_Bool uriCharClass[128] =
90 : {
91 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Pchar but without encoding slashes */
92 : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
93 : 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* !"#$%&'()*+,-./ */
94 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, /* 0123456789:;<=>? */
95 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ABCDEFGHIJKLMNO */
96 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* PQRSTUVWXYZ[\]^_ */
97 : 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* `abcdefghijklmno */
98 : 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 /* pqrstuvwxyz{|}~ */
99 : };
100 :
101 :
102 : /* check for top wrong usage strings */
103 : /*
104 : static sal_Bool findWrongUsage( const sal_Unicode *path, sal_Int32 len )
105 : {
106 : rtl_uString *pTmp = NULL;
107 : sal_Bool bRet;
108 :
109 : rtl_uString_newFromStr_WithLength( &pTmp, path, len );
110 :
111 : rtl_ustr_toAsciiLowerCase_WithLength( pTmp->buffer, pTmp->length );
112 :
113 : bRet = ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "ftp://", 6 ) ) ||
114 : ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "http://", 7 ) ) ||
115 : ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "vnd.sun.star", 12 ) ) ||
116 : ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "private:", 8 ) ) ||
117 : ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "slot:", 5) );
118 :
119 : rtl_uString_release( pTmp );
120 : return bRet;
121 : }
122 : */
123 :
124 : /****************************************************************************/
125 : /* osl_getCanonicalName */
126 : /****************************************************************************/
127 :
128 0 : oslFileError SAL_CALL osl_getCanonicalName( rtl_uString* ustrFileURL, rtl_uString** pustrValidURL )
129 : {
130 : OSL_FAIL("osl_getCanonicalName not implemented");
131 :
132 0 : rtl_uString_newFromString(pustrValidURL, ustrFileURL);
133 0 : return osl_File_E_None;
134 : }
135 :
136 : /****************************************************************************/
137 : /* osl_getSystemPathFromFileURL */
138 : /****************************************************************************/
139 :
140 825837 : oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath )
141 : {
142 : sal_Int32 nIndex;
143 825837 : rtl_uString * pTmp = NULL;
144 :
145 825837 : sal_Unicode encodedSlash[3] = { '%', '2', 'F' };
146 825837 : sal_Unicode protocolDelimiter[3] = { ':', '/', '/' };
147 :
148 : /* temporary hack: if already system path, return ustrFileURL */
149 : /*
150 : if( (sal_Unicode) '/' == ustrFileURL->buffer[0] )
151 : {
152 : OSL_FAIL( "osl_getSystemPathFromFileURL: input is already system path" );
153 : rtl_uString_assign( pustrSystemPath, ustrFileURL );
154 : return osl_File_E_None;
155 : }
156 : */
157 :
158 : /* a valid file url may not start with '/' */
159 825837 : if( ( 0 == ustrFileURL->length ) || ( (sal_Unicode) '/' == ustrFileURL->buffer[0] ) )
160 : {
161 1310 : return osl_File_E_INVAL;
162 : }
163 :
164 : /* Check for non file:// protocols */
165 :
166 824527 : nIndex = rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, protocolDelimiter, 3 );
167 824527 : if ( -1 != nIndex && (4 != nIndex || 0 != rtl_ustr_ascii_shortenedCompare_WithLength( ustrFileURL->buffer, ustrFileURL->length,"file", 4 ) ) )
168 : {
169 0 : return osl_File_E_INVAL;
170 : }
171 :
172 : /* search for encoded slashes (%2F) and decode every single token if we find one */
173 :
174 824527 : nIndex = 0;
175 :
176 824527 : if( -1 != rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, encodedSlash, 3 ) )
177 : {
178 0 : rtl_uString * ustrPathToken = NULL;
179 0 : sal_Int32 nOffset = 7;
180 :
181 0 : do
182 : {
183 0 : nOffset += nIndex;
184 :
185 : /* break url down in '/' divided tokens tokens */
186 0 : nIndex = rtl_ustr_indexOfChar_WithLength( ustrFileURL->buffer + nOffset, ustrFileURL->length - nOffset, (sal_Unicode) '/' );
187 :
188 : /* copy token to new string */
189 0 : rtl_uString_newFromStr_WithLength( &ustrPathToken, ustrFileURL->buffer + nOffset,
190 0 : -1 == nIndex ? ustrFileURL->length - nOffset : nIndex++ );
191 :
192 : /* decode token */
193 0 : rtl_uriDecode( ustrPathToken, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp );
194 :
195 : /* the result should not contain any '/' */
196 0 : if( -1 != rtl_ustr_indexOfChar_WithLength( pTmp->buffer, pTmp->length, (sal_Unicode) '/' ) )
197 : {
198 0 : rtl_uString_release( pTmp );
199 0 : rtl_uString_release( ustrPathToken );
200 :
201 0 : return osl_File_E_INVAL;
202 : }
203 :
204 : } while( -1 != nIndex );
205 :
206 : /* release temporary string and restore index variable */
207 0 : rtl_uString_release( ustrPathToken );
208 0 : nIndex = 0;
209 : }
210 :
211 : /* protocol and server should not be encoded, so decode the whole string */
212 824527 : rtl_uriDecode( ustrFileURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp );
213 :
214 : /* check if file protocol specified */
215 : /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */
216 824527 : if( 7 <= pTmp->length )
217 : {
218 824377 : rtl_uString * pProtocol = NULL;
219 824377 : rtl_uString_newFromStr_WithLength( &pProtocol, pTmp->buffer, 7 );
220 :
221 : /* protocol is case insensitive */
222 824377 : rtl_ustr_toAsciiLowerCase_WithLength( pProtocol->buffer, pProtocol->length );
223 :
224 824377 : if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pProtocol->buffer, pProtocol->length,"file://", 7 ) )
225 823823 : nIndex = 7;
226 :
227 824377 : rtl_uString_release( pProtocol );
228 : }
229 :
230 : /* skip "localhost" or "127.0.0.1" if "file://" is specified */
231 : /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */
232 824527 : if( nIndex && ( 10 <= pTmp->length - nIndex ) )
233 : {
234 803254 : rtl_uString * pServer = NULL;
235 803254 : rtl_uString_newFromStr_WithLength( &pServer, pTmp->buffer + nIndex, 10 );
236 :
237 : /* server is case insensitive */
238 803254 : rtl_ustr_toAsciiLowerCase_WithLength( pServer->buffer, pServer->length );
239 :
240 1606508 : if( ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"localhost/", 10 ) ) ||
241 803254 : ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"127.0.0.1/", 10 ) ) )
242 : {
243 : /* don't exclude the '/' */
244 0 : nIndex += 9;
245 : }
246 :
247 803254 : rtl_uString_release( pServer );
248 : }
249 :
250 824527 : if( nIndex )
251 823823 : rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + nIndex, pTmp->length - nIndex );
252 :
253 : /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
254 824527 : if( (sal_Unicode) '~' == pTmp->buffer[0] )
255 : {
256 : /* check if another user is specified */
257 0 : if( ( 1 == pTmp->length ) || ( (sal_Unicode)'/' == pTmp->buffer[1] ) )
258 : {
259 0 : rtl_uString *pTmp2 = NULL;
260 :
261 : /* osl_getHomeDir returns file URL */
262 0 : osl_getHomeDir( osl_getCurrentSecurity(), &pTmp2 );
263 :
264 : /* remove "file://" prefix */
265 0 : rtl_uString_newFromStr_WithLength( &pTmp2, pTmp2->buffer + 7, pTmp2->length - 7 );
266 :
267 : /* replace '~' in original string */
268 0 : rtl_uString_newReplaceStrAt( &pTmp, pTmp, 0, 1, pTmp2 );
269 0 : rtl_uString_release( pTmp2 );
270 : }
271 :
272 : else
273 : {
274 : /* FIXME: replace ~user with users home directory */
275 0 : return osl_File_E_INVAL;
276 : }
277 : }
278 :
279 : /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */
280 : /*
281 : OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) );
282 : */
283 :
284 824527 : *pustrSystemPath = pTmp;
285 824527 : return osl_File_E_None;
286 : }
287 :
288 : /****************************************************************************/
289 : /* osl_getFileURLFromSystemPath */
290 : /****************************************************************************/
291 :
292 341807 : oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL )
293 : {
294 : static const sal_Unicode pDoubleSlash[2] = { '/', '/' };
295 :
296 341807 : rtl_uString *pTmp = NULL;
297 : sal_Int32 nIndex;
298 :
299 341807 : if( 0 == ustrSystemPath->length )
300 122 : return osl_File_E_INVAL;
301 :
302 : /* temporary hack: if already file url, return ustrSystemPath */
303 :
304 341685 : if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file:", 5 ) )
305 : {
306 : /*
307 : if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file://", 7 ) )
308 : {
309 : OSL_FAIL( "osl_getFileURLFromSystemPath: input is already file URL" );
310 : rtl_uString_assign( pustrFileURL, ustrSystemPath );
311 : }
312 : else
313 : {
314 : rtl_uString *pTmp2 = NULL;
315 :
316 : OSL_FAIL( "osl_getFileURLFromSystemPath: input is wrong file URL" );
317 : rtl_uString_newFromStr_WithLength( pustrFileURL, ustrSystemPath->buffer + 5, ustrSystemPath->length - 5 );
318 : rtl_uString_newFromAscii( &pTmp2, "file://" );
319 : rtl_uString_newConcat( pustrFileURL, *pustrFileURL, pTmp2 );
320 : rtl_uString_release( pTmp2 );
321 : }
322 : return osl_File_E_None;
323 : */
324 10751 : return osl_File_E_INVAL;
325 : }
326 :
327 :
328 : /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
329 330934 : if( (sal_Unicode) '~' == ustrSystemPath->buffer[0] )
330 : {
331 : /* check if another user is specified */
332 0 : if( ( 1 == ustrSystemPath->length ) || ( (sal_Unicode)'/' == ustrSystemPath->buffer[1] ) )
333 : {
334 : /* osl_getHomeDir returns file URL */
335 0 : oslSecurity pSecurity = osl_getCurrentSecurity();
336 0 : osl_getHomeDir( pSecurity , &pTmp );
337 0 : osl_freeSecurityHandle( pSecurity );
338 :
339 : /* remove "file://" prefix */
340 0 : rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 );
341 :
342 : /* replace '~' in original string */
343 0 : rtl_uString_newReplaceStrAt( &pTmp, ustrSystemPath, 0, 1, pTmp );
344 : }
345 :
346 : else
347 : {
348 : /* FIXME: replace ~user with users home directory */
349 0 : return osl_File_E_INVAL;
350 : }
351 : }
352 :
353 : /* check if initial string contains double instances of '/' */
354 330934 : nIndex = rtl_ustr_indexOfStr_WithLength( ustrSystemPath->buffer, ustrSystemPath->length, pDoubleSlash, 2 );
355 330934 : if( -1 != nIndex )
356 : {
357 : sal_Int32 nSrcIndex;
358 157 : sal_Int32 nDeleted = 0;
359 :
360 : /* if pTmp is not already allocated, copy ustrSystemPath for modification */
361 157 : if( NULL == pTmp )
362 157 : rtl_uString_newFromString( &pTmp, ustrSystemPath );
363 :
364 : /* adapt index to pTmp */
365 157 : nIndex += pTmp->length - ustrSystemPath->length;
366 :
367 : /* remove all occurrences of '//' */
368 2527 : for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ )
369 : {
370 2370 : if( ((sal_Unicode) '/' == pTmp->buffer[nSrcIndex]) && ((sal_Unicode) '/' == pTmp->buffer[nIndex]) )
371 157 : nDeleted++;
372 : else
373 2213 : pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex];
374 : }
375 :
376 : /* adjust length member */
377 157 : pTmp->length -= nDeleted;
378 : }
379 :
380 330934 : if( NULL == pTmp )
381 330777 : rtl_uString_assign( &pTmp, ustrSystemPath );
382 :
383 : /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */
384 : /*
385 : OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) );
386 : */
387 :
388 : /* file URLs must be URI encoded */
389 330934 : rtl_uriEncode( pTmp, uriCharClass, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL );
390 :
391 330934 : rtl_uString_release( pTmp );
392 :
393 : /* absolute urls should start with 'file://' */
394 330934 : if( (sal_Unicode)'/' == (*pustrFileURL)->buffer[0] )
395 : {
396 330508 : rtl_uString *pProtocol = NULL;
397 :
398 330508 : rtl_uString_newFromAscii( &pProtocol, "file://" );
399 330508 : rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL );
400 330508 : rtl_uString_release( pProtocol );
401 : }
402 :
403 330934 : return osl_File_E_None;
404 : }
405 :
406 : /****************************************************************************
407 : * osl_getSystemPathFromFileURL_Ex - helper function
408 : * clients may specify if they want to accept relative
409 : * URLs or not
410 : ****************************************************************************/
411 :
412 363526 : oslFileError osl_getSystemPathFromFileURL_Ex(
413 : rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath, sal_Bool bAllowRelative)
414 : {
415 363526 : rtl_uString* temp = 0;
416 363526 : oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp);
417 :
418 363526 : if (osl_File_E_None == osl_error)
419 : {
420 363323 : if (bAllowRelative || (UNICHAR_SLASH == temp->buffer[0]))
421 : {
422 363322 : *pustrSystemPath = temp;
423 : }
424 : else
425 : {
426 1 : rtl_uString_release(temp);
427 1 : osl_error = osl_File_E_INVAL;
428 : }
429 : }
430 :
431 363526 : return osl_error;
432 : }
433 :
434 : namespace /* private */
435 : {
436 :
437 : /******************************************************
438 : * Helper function, return a pinter to the final '\0'
439 : * of a string
440 : ******************************************************/
441 :
442 7472854 : sal_Unicode* ustrtoend(sal_Unicode* pStr)
443 : {
444 7472854 : return (pStr + rtl_ustr_getLength(pStr));
445 : }
446 :
447 : /*********************************************
448 :
449 : ********************************************/
450 :
451 3529597 : sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d)
452 : {
453 3529597 : sal_Unicode* p = ustrtoend(d);
454 3529597 : *p++ = chr;
455 3529597 : *p = 0;
456 3529597 : return d;
457 : }
458 :
459 : /******************************************************
460 : *
461 : ******************************************************/
462 :
463 404578 : bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr)
464 : {
465 404578 : sal_Unicode* p = ustrtoend(pStr);
466 404578 : if (p > pStr)
467 404578 : p--;
468 404578 : return (*p == Chr);
469 : }
470 :
471 : /******************************************************
472 : * Remove the last part of a path, a path that has
473 : * only a '/' or no '/' at all will be returned
474 : * unmodified
475 : ******************************************************/
476 :
477 4541 : sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath)
478 : {
479 : /* we always may skip -2 because we
480 : may at least stand on a '/' but
481 : either there is no other character
482 : before this '/' or it's another
483 : character than the '/'
484 : */
485 4541 : sal_Unicode* p = ustrtoend(aPath) - 2;
486 :
487 : // move back to the next path separator
488 : // or to the start of the string
489 35431 : while ((p > aPath) && (*p != UNICHAR_SLASH))
490 26349 : p--;
491 :
492 4541 : if (p >= aPath)
493 : {
494 4541 : if (UNICHAR_SLASH == *p)
495 : {
496 4541 : p++;
497 4541 : *p = '\0';
498 : }
499 : else
500 : {
501 0 : *p = '\0';
502 : }
503 : }
504 :
505 4541 : return aPath;
506 : }
507 :
508 : /******************************************************
509 : *
510 : ******************************************************/
511 :
512 438795 : oslFileError _osl_resolvepath(
513 : /*inout*/ sal_Unicode* path,
514 : /*inout*/ bool* failed)
515 : {
516 438795 : oslFileError ferr = osl_File_E_None;
517 :
518 438795 : if (!*failed)
519 : {
520 : char unresolved_path[PATH_MAX];
521 438795 : if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path)))
522 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
523 :
524 : char resolved_path[PATH_MAX];
525 438795 : if (realpath(unresolved_path, resolved_path))
526 : {
527 438586 : if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX))
528 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
529 :
530 : }
531 : else
532 : {
533 209 : if (EACCES == errno || ENOTDIR == errno || ENOENT == errno)
534 209 : *failed = true;
535 : else
536 0 : ferr = oslTranslateFileError(OSL_FET_ERROR, errno);
537 : }
538 : }
539 :
540 438795 : return ferr;
541 : }
542 :
543 : /******************************************************
544 : * Works even with non existing paths. The resulting
545 : * path must not exceed PATH_MAX else
546 : * osl_File_E_NAMETOOLONG is the result
547 : ******************************************************/
548 :
549 39755 : oslFileError osl_getAbsoluteFileURL_impl_(const rtl::OUString& unresolved_path, rtl::OUString& resolved_path)
550 : {
551 : // the given unresolved path must not exceed PATH_MAX
552 39755 : if (unresolved_path.getLength() >= (PATH_MAX - 2))
553 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
554 :
555 : sal_Unicode path_resolved_so_far[PATH_MAX];
556 39755 : const sal_Unicode* punresolved = unresolved_path.getStr();
557 39755 : sal_Unicode* presolvedsf = path_resolved_so_far;
558 :
559 : // reserve space for leading '/' and trailing '\0'
560 : // do not exceed this limit
561 39755 : sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2;
562 :
563 : // if realpath fails with error ENOTDIR, EACCES or ENOENT
564 : // we will not call it again, because _osl_realpath should also
565 : // work with non existing directories etc.
566 39755 : bool realpath_failed = false;
567 : oslFileError ferr;
568 :
569 39755 : path_resolved_so_far[0] = '\0';
570 :
571 3248848 : while (*punresolved != '\0')
572 : {
573 : // ignore '/.' , skip one part back when '/..'
574 :
575 3169338 : if ((UNICHAR_DOT == *punresolved) && (UNICHAR_SLASH == *presolvedsf))
576 : {
577 4543 : if ('\0' == *(punresolved + 1))
578 : {
579 0 : punresolved++;
580 0 : continue;
581 : }
582 4543 : else if (UNICHAR_SLASH == *(punresolved + 1))
583 : {
584 2 : punresolved += 2;
585 2 : continue;
586 : }
587 4541 : else if ((UNICHAR_DOT == *(punresolved + 1)) && ('\0' == *(punresolved + 2) || (UNICHAR_SLASH == *(punresolved + 2))))
588 : {
589 4541 : _rmlastpathtoken(path_resolved_so_far);
590 :
591 4541 : presolvedsf = ustrtoend(path_resolved_so_far) - 1;
592 :
593 4541 : if (UNICHAR_SLASH == *(punresolved + 2))
594 4368 : punresolved += 3;
595 : else
596 173 : punresolved += 2;
597 :
598 4541 : continue;
599 : }
600 : else // a file or directory name may start with '.'
601 : {
602 0 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
603 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
604 :
605 0 : ustrchrcat(*punresolved++, path_resolved_so_far);
606 :
607 0 : if ('\0' == *punresolved && !realpath_failed)
608 : {
609 : ferr = _osl_resolvepath(
610 : path_resolved_so_far,
611 0 : &realpath_failed);
612 :
613 0 : if (osl_File_E_None != ferr)
614 0 : return ferr;
615 : }
616 : }
617 : }
618 3164795 : else if (UNICHAR_SLASH == *punresolved)
619 : {
620 404603 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
621 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
622 :
623 404603 : ustrchrcat(*punresolved++, path_resolved_so_far);
624 :
625 404603 : if (!realpath_failed)
626 : {
627 : ferr = _osl_resolvepath(
628 : path_resolved_so_far,
629 404578 : &realpath_failed);
630 :
631 404578 : if (osl_File_E_None != ferr)
632 0 : return ferr;
633 :
634 404578 : if (!_islastchr(path_resolved_so_far, UNICHAR_SLASH))
635 : {
636 364802 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
637 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
638 :
639 364802 : ustrchrcat(UNICHAR_SLASH, path_resolved_so_far);
640 : }
641 : }
642 : }
643 : else // any other character
644 : {
645 2760192 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
646 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
647 :
648 2760192 : ustrchrcat(*punresolved++, path_resolved_so_far);
649 :
650 2760192 : if ('\0' == *punresolved && !realpath_failed)
651 : {
652 : ferr = _osl_resolvepath(
653 : path_resolved_so_far,
654 34217 : &realpath_failed);
655 :
656 34217 : if (osl_File_E_None != ferr)
657 0 : return ferr;
658 : }
659 : }
660 : }
661 :
662 39755 : sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far);
663 :
664 : OSL_ASSERT(len < PATH_MAX);
665 :
666 39755 : resolved_path = rtl::OUString(path_resolved_so_far, len);
667 :
668 39755 : return osl_File_E_None;
669 : }
670 :
671 : } // end namespace private
672 :
673 :
674 : /******************************************************
675 : * osl_getAbsoluteFileURL
676 : ******************************************************/
677 :
678 39808 : oslFileError osl_getAbsoluteFileURL(rtl_uString* ustrBaseDirURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL)
679 : {
680 : // Work around the below call to getSystemPathFromFileURL rejecting input
681 : // that starts with "/" (for whatever reason it behaves that way; but
682 : // changing that would start to break lots of tests at least):
683 39808 : rtl::OUString relUrl(ustrRelativeURL);
684 39808 : if (relUrl.startsWith("//")) {
685 0 : relUrl = "file:" + relUrl;
686 39808 : } else if (relUrl.startsWith("/")) {
687 0 : relUrl = "file://" + relUrl;
688 : }
689 :
690 : FileBase::RC rc;
691 79616 : rtl::OUString unresolved_path;
692 :
693 39808 : rc = FileBase::getSystemPathFromFileURL(relUrl, unresolved_path);
694 :
695 39808 : if(FileBase::E_None != rc)
696 51 : return oslFileError(rc);
697 :
698 39757 : if (systemPathIsRelativePath(unresolved_path))
699 : {
700 90 : rtl::OUString base_path;
701 90 : rc = (FileBase::RC) osl_getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData, sal_False);
702 :
703 90 : if (FileBase::E_None != rc)
704 2 : return oslFileError(rc);
705 :
706 176 : rtl::OUString abs_path;
707 88 : systemPathMakeAbsolutePath(base_path, unresolved_path, abs_path);
708 :
709 176 : unresolved_path = abs_path;
710 : }
711 :
712 79510 : rtl::OUString resolved_path;
713 :
714 39755 : static bool allow_symlinks = getenv("SAL_ALLOW_LINKOO_SYMLINKS") != 0;
715 : // getenv is not thread safe, so minimize use of result
716 39755 : if (!allow_symlinks)
717 : {
718 29767 : rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
719 : }
720 : else
721 : {
722 : // SAL_ALLOW_LINKOO_SYMLINKS environment variable:
723 : // for linkoo to work, we need to let the symlinks to the libraries untouched
724 9988 : rtl::OUString base;
725 9988 : sal_Int32 last_slash = unresolved_path.lastIndexOf( UNICHAR_SLASH );
726 :
727 19976 : if (last_slash >= 0 && last_slash + 1 < unresolved_path.getLength()
728 9988 : && ! ( last_slash + 2 == unresolved_path.getLength() && unresolved_path.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("."), last_slash + 1) )
729 19976 : && ! ( last_slash + 3 == unresolved_path.getLength() && unresolved_path.matchAsciiL(RTL_CONSTASCII_STRINGPARAM(".."), last_slash + 1) ))
730 : {
731 9820 : base = unresolved_path.copy(last_slash+1);
732 9820 : unresolved_path = unresolved_path.copy(0, last_slash);
733 : }
734 :
735 9988 : rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
736 :
737 9988 : if (!base.isEmpty())
738 : {
739 9820 : resolved_path += rtl::OUString( UNICHAR_SLASH );
740 9820 : resolved_path += base;
741 9988 : }
742 : }
743 :
744 39755 : if (FileBase::E_None == rc)
745 : {
746 39755 : rc = (FileBase::RC) osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL);
747 : OSL_ASSERT(FileBase::E_None == rc);
748 : }
749 :
750 79563 : return oslFileError(rc);
751 : }
752 :
753 :
754 : namespace /* private */
755 : {
756 :
757 : /*********************************************
758 : No separate error code if unicode to text
759 : conversion or getenv fails because for the
760 : caller there is no difference why a file
761 : could not be found in $PATH
762 : ********************************************/
763 :
764 0 : bool find_in_PATH(const rtl::OUString& file_path, rtl::OUString& result)
765 : {
766 0 : bool bfound = false;
767 0 : rtl::OUString path("PATH");
768 0 : rtl::OUString env_path;
769 :
770 0 : if (osl_Process_E_None == osl_getEnvironment(path.pData, &env_path.pData))
771 0 : bfound = osl::searchPath(file_path, env_path, result);
772 :
773 0 : return bfound;
774 : }
775 :
776 : /*********************************************
777 : No separate error code if unicode to text
778 : conversion or getcwd fails because for the
779 : caller there is no difference why a file
780 : could not be found in CDW
781 : ********************************************/
782 :
783 0 : bool find_in_CWD(const rtl::OUString& file_path, rtl::OUString& result)
784 : {
785 0 : bool bfound = false;
786 0 : rtl::OUString cwd_url;
787 :
788 0 : if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData))
789 : {
790 0 : rtl::OUString cwd;
791 0 : FileBase::getSystemPathFromFileURL(cwd_url, cwd);
792 0 : bfound = osl::searchPath(file_path, cwd, result);
793 : }
794 0 : return bfound;
795 : }
796 :
797 : /*********************************************
798 :
799 : ********************************************/
800 :
801 330 : bool find_in_searchPath(const rtl::OUString& file_path, rtl_uString* search_path, rtl::OUString& result)
802 : {
803 330 : return (search_path && osl::searchPath(file_path, rtl::OUString(search_path), result));
804 : }
805 :
806 : } // end namespace private
807 :
808 :
809 : /****************************************************************************
810 : * osl_searchFileURL
811 : ***************************************************************************/
812 :
813 330 : oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL)
814 : {
815 : OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter");
816 :
817 : FileBase::RC rc;
818 330 : rtl::OUString file_path;
819 :
820 : // try to interpret search path as file url else assume it's a system path list
821 330 : rc = FileBase::getSystemPathFromFileURL(rtl::OUString(ustrFilePath), file_path);
822 330 : if (FileBase::E_INVAL == rc)
823 0 : file_path = ustrFilePath;
824 330 : else if (FileBase::E_None != rc)
825 0 : return oslFileError(rc);
826 :
827 330 : bool bfound = false;
828 660 : rtl::OUString result;
829 :
830 660 : if (find_in_searchPath(file_path, ustrSearchPath, result) ||
831 330 : find_in_PATH(file_path, result) ||
832 0 : find_in_CWD(file_path, result))
833 : {
834 330 : rtl::OUString resolved;
835 :
836 330 : if (osl::realpath(result, resolved))
837 : {
838 : #if OSL_DEBUG_LEVEL > 0
839 : oslFileError osl_error =
840 : #endif
841 330 : osl_getFileURLFromSystemPath(resolved.pData, pustrURL);
842 : OSL_ASSERT(osl_File_E_None == osl_error);
843 330 : bfound = true;
844 330 : }
845 : }
846 660 : return bfound ? osl_File_E_None : osl_File_E_NOENT;
847 : }
848 :
849 :
850 : /****************************************************************************
851 : * FileURLToPath
852 : ***************************************************************************/
853 :
854 191522 : oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL)
855 : {
856 191522 : rtl_uString* ustrSystemPath = NULL;
857 191522 : oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &ustrSystemPath);
858 :
859 191522 : if(osl_File_E_None != osl_error)
860 5 : return osl_error;
861 :
862 191517 : osl_systemPathRemoveSeparator(ustrSystemPath);
863 :
864 : /* convert unicode path to text */
865 191517 : if(!UnicodeToText( buffer, bufLen, ustrSystemPath->buffer, ustrSystemPath->length))
866 0 : osl_error = oslTranslateFileError(OSL_FET_ERROR, errno);
867 :
868 191517 : rtl_uString_release(ustrSystemPath);
869 :
870 191517 : return osl_error;
871 : }
872 :
873 : /*****************************************************************************
874 : * UnicodeToText
875 : ****************************************************************************/
876 :
877 : namespace /* private */
878 : {
879 : class UnicodeToTextConverter_Impl
880 : {
881 : rtl_UnicodeToTextConverter m_converter;
882 :
883 1663 : UnicodeToTextConverter_Impl()
884 1663 : : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding()))
885 1663 : {}
886 :
887 1663 : ~UnicodeToTextConverter_Impl()
888 : {
889 1663 : rtl_destroyUnicodeToTextConverter (m_converter);
890 1663 : }
891 : public:
892 694498 : static UnicodeToTextConverter_Impl & getInstance()
893 : {
894 694498 : static UnicodeToTextConverter_Impl g_theConverter;
895 694498 : return g_theConverter;
896 : }
897 :
898 694498 : sal_Size convert(
899 : sal_Unicode const * pSrcBuf, sal_Size nSrcChars, sal_Char * pDstBuf, sal_Size nDstBytes,
900 : sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtChars)
901 : {
902 : OSL_ASSERT(m_converter != 0);
903 : return rtl_convertUnicodeToText (
904 694498 : m_converter, 0, pSrcBuf, nSrcChars, pDstBuf, nDstBytes, nFlags, pInfo, pSrcCvtChars);
905 : }
906 : };
907 : } // end namespace private
908 :
909 694498 : int UnicodeToText( char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen )
910 : {
911 694498 : sal_uInt32 nInfo = 0;
912 694498 : sal_Size nSrcChars = 0;
913 :
914 694498 : sal_Size nDestBytes = UnicodeToTextConverter_Impl::getInstance().convert (
915 : uniText, uniTextLen, buffer, bufLen,
916 1388996 : OUSTRING_TO_OSTRING_CVTFLAGS | RTL_UNICODETOTEXT_FLAGS_FLUSH, &nInfo, &nSrcChars);
917 :
918 694498 : if( nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL )
919 : {
920 0 : errno = EOVERFLOW;
921 0 : return 0;
922 : }
923 :
924 : /* ensure trailing '\0' */
925 694498 : buffer[nDestBytes] = '\0';
926 694498 : return nDestBytes;
927 : }
928 :
929 : /*****************************************************************************
930 : * TextToUnicode
931 : ****************************************************************************/
932 :
933 : namespace /* private */
934 : {
935 : class TextToUnicodeConverter_Impl
936 : {
937 : rtl_TextToUnicodeConverter m_converter;
938 :
939 1060 : TextToUnicodeConverter_Impl()
940 1060 : : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding()))
941 1060 : {}
942 :
943 1060 : ~TextToUnicodeConverter_Impl()
944 : {
945 1060 : rtl_destroyTextToUnicodeConverter (m_converter);
946 1060 : }
947 :
948 : public:
949 438586 : static TextToUnicodeConverter_Impl & getInstance()
950 : {
951 438586 : static TextToUnicodeConverter_Impl g_theConverter;
952 438586 : return g_theConverter;
953 : }
954 :
955 438586 : sal_Size convert(
956 : sal_Char const * pSrcBuf, sal_Size nSrcBytes, sal_Unicode * pDstBuf, sal_Size nDstChars,
957 : sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtBytes)
958 : {
959 : OSL_ASSERT(m_converter != 0);
960 : return rtl_convertTextToUnicode (
961 438586 : m_converter, 0, pSrcBuf, nSrcBytes, pDstBuf, nDstChars, nFlags, pInfo, pSrcCvtBytes);
962 : }
963 : };
964 : } // end namespace private
965 :
966 438586 : int TextToUnicode(
967 : const char* text,
968 : size_t text_buffer_size,
969 : sal_Unicode* unic_text,
970 : sal_Int32 unic_text_buffer_size)
971 : {
972 438586 : sal_uInt32 nInfo = 0;
973 438586 : sal_Size nSrcChars = 0;
974 :
975 438586 : sal_Size nDestBytes = TextToUnicodeConverter_Impl::getInstance().convert(
976 : text, text_buffer_size, unic_text, unic_text_buffer_size,
977 877172 : OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_FLUSH, &nInfo, &nSrcChars);
978 :
979 438586 : if (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL)
980 : {
981 0 : errno = EOVERFLOW;
982 0 : return 0;
983 : }
984 :
985 : /* ensure trailing '\0' */
986 438586 : unic_text[nDestBytes] = '\0';
987 438586 : return nDestBytes;
988 : }
989 :
990 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|