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 390005 : oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath )
141 : {
142 : sal_Int32 nIndex;
143 390005 : rtl_uString * pTmp = NULL;
144 :
145 390005 : sal_Unicode encodedSlash[3] = { '%', '2', 'F' };
146 390005 : 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 390005 : if( ( 0 == ustrFileURL->length ) || ( (sal_Unicode) '/' == ustrFileURL->buffer[0] ) )
160 : {
161 1224 : return osl_File_E_INVAL;
162 : }
163 :
164 : /* Check for non file:// protocols */
165 :
166 388781 : nIndex = rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, protocolDelimiter, 3 );
167 388781 : 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 388781 : nIndex = 0;
175 :
176 388781 : 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 '/' devided 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 388781 : 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 388781 : if( 7 <= pTmp->length )
217 : {
218 388749 : rtl_uString * pProtocol = NULL;
219 388749 : rtl_uString_newFromStr_WithLength( &pProtocol, pTmp->buffer, 7 );
220 :
221 : /* protocol is case insensitive */
222 388749 : rtl_ustr_toAsciiLowerCase_WithLength( pProtocol->buffer, pProtocol->length );
223 :
224 388749 : if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pProtocol->buffer, pProtocol->length,"file://", 7 ) )
225 379626 : nIndex = 7;
226 :
227 388749 : 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 388781 : if( nIndex && ( 10 <= pTmp->length - nIndex ) )
233 : {
234 374584 : rtl_uString * pServer = NULL;
235 374584 : rtl_uString_newFromStr_WithLength( &pServer, pTmp->buffer + nIndex, 10 );
236 :
237 : /* server is case insensitive */
238 374584 : rtl_ustr_toAsciiLowerCase_WithLength( pServer->buffer, pServer->length );
239 :
240 749168 : if( ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"localhost/", 10 ) ) ||
241 374584 : ( 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 374584 : rtl_uString_release( pServer );
248 : }
249 :
250 388781 : if( nIndex )
251 379626 : 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 388781 : 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 388781 : *pustrSystemPath = pTmp;
285 388781 : return osl_File_E_None;
286 : }
287 :
288 : /****************************************************************************/
289 : /* osl_getFileURLFromSystemPath */
290 : /****************************************************************************/
291 :
292 272477 : oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL )
293 : {
294 : static const sal_Unicode pDoubleSlash[2] = { '/', '/' };
295 :
296 272477 : rtl_uString *pTmp = NULL;
297 : sal_Int32 nIndex;
298 :
299 272477 : if( 0 == ustrSystemPath->length )
300 8 : return osl_File_E_INVAL;
301 :
302 : /* temporary hack: if already file url, return ustrSystemPath */
303 :
304 272469 : 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 10737 : 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 261732 : 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 : osl_getHomeDir( osl_getCurrentSecurity(), &pTmp );
336 :
337 : /* remove "file://" prefix */
338 0 : rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 );
339 :
340 : /* replace '~' in original string */
341 0 : rtl_uString_newReplaceStrAt( &pTmp, ustrSystemPath, 0, 1, pTmp );
342 : }
343 :
344 : else
345 : {
346 : /* FIXME: replace ~user with users home directory */
347 0 : return osl_File_E_INVAL;
348 : }
349 : }
350 :
351 : /* check if initial string contains double instances of '/' */
352 261732 : nIndex = rtl_ustr_indexOfStr_WithLength( ustrSystemPath->buffer, ustrSystemPath->length, pDoubleSlash, 2 );
353 261732 : if( -1 != nIndex )
354 : {
355 : sal_Int32 nSrcIndex;
356 27 : sal_Int32 nDeleted = 0;
357 :
358 : /* if pTmp is not already allocated, copy ustrSystemPath for modification */
359 27 : if( NULL == pTmp )
360 27 : rtl_uString_newFromString( &pTmp, ustrSystemPath );
361 :
362 : /* adapt index to pTmp */
363 27 : nIndex += pTmp->length - ustrSystemPath->length;
364 :
365 : /* remove all occurrences of '//' */
366 405 : for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ )
367 : {
368 378 : if( ((sal_Unicode) '/' == pTmp->buffer[nSrcIndex]) && ((sal_Unicode) '/' == pTmp->buffer[nIndex]) )
369 27 : nDeleted++;
370 : else
371 351 : pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex];
372 : }
373 :
374 : /* adjust length member */
375 27 : pTmp->length -= nDeleted;
376 : }
377 :
378 261732 : if( NULL == pTmp )
379 261705 : rtl_uString_assign( &pTmp, ustrSystemPath );
380 :
381 : /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */
382 : /*
383 : OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) );
384 : */
385 :
386 : /* file URLs must be URI encoded */
387 261732 : rtl_uriEncode( pTmp, uriCharClass, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL );
388 :
389 261732 : rtl_uString_release( pTmp );
390 :
391 : /* absolute urls should start with 'file://' */
392 261732 : if( (sal_Unicode)'/' == (*pustrFileURL)->buffer[0] )
393 : {
394 232327 : rtl_uString *pProtocol = NULL;
395 :
396 232327 : rtl_uString_newFromAscii( &pProtocol, "file://" );
397 232327 : rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL );
398 232327 : rtl_uString_release( pProtocol );
399 : }
400 :
401 261732 : return osl_File_E_None;
402 : }
403 :
404 : /****************************************************************************
405 : * osl_getSystemPathFromFileURL_Ex - helper function
406 : * clients may specify if they want to accept relative
407 : * URLs or not
408 : ****************************************************************************/
409 :
410 53258 : oslFileError osl_getSystemPathFromFileURL_Ex(
411 : rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath, sal_Bool bAllowRelative)
412 : {
413 53258 : rtl_uString* temp = 0;
414 53258 : oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp);
415 :
416 53258 : if (osl_File_E_None == osl_error)
417 : {
418 52599 : if (bAllowRelative || (UNICHAR_SLASH == temp->buffer[0]))
419 : {
420 52598 : *pustrSystemPath = temp;
421 : }
422 : else
423 : {
424 1 : rtl_uString_release(temp);
425 1 : osl_error = osl_File_E_INVAL;
426 : }
427 : }
428 :
429 53258 : return osl_error;
430 : }
431 :
432 : namespace /* private */
433 : {
434 :
435 : /******************************************************
436 : * Helper function, return a pinter to the final '\0'
437 : * of a string
438 : ******************************************************/
439 :
440 7152622 : sal_Unicode* ustrtoend(sal_Unicode* pStr)
441 : {
442 7152622 : return (pStr + rtl_ustr_getLength(pStr));
443 : }
444 :
445 : /*********************************************
446 :
447 : ********************************************/
448 :
449 3359707 : sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d)
450 : {
451 3359707 : sal_Unicode* p = ustrtoend(d);
452 3359707 : *p++ = chr;
453 3359707 : *p = 0;
454 3359707 : return d;
455 : }
456 :
457 : /******************************************************
458 : *
459 : ******************************************************/
460 :
461 389312 : bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr)
462 : {
463 389312 : sal_Unicode* p = ustrtoend(pStr);
464 389312 : if (p > pStr)
465 389312 : p--;
466 389312 : return (*p == Chr);
467 : }
468 :
469 : /******************************************************
470 : * Remove the last part of a path, a path that has
471 : * only a '/' or no '/' at all will be returned
472 : * unmodified
473 : ******************************************************/
474 :
475 21948 : sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath)
476 : {
477 : /* we always may skip -2 because we
478 : may at least stand on a '/' but
479 : either there is no other character
480 : before this '/' or it's another
481 : character than the '/'
482 : */
483 21948 : sal_Unicode* p = ustrtoend(aPath) - 2;
484 :
485 : // move back to the next path separator
486 : // or to the start of the string
487 148615 : while ((p > aPath) && (*p != UNICHAR_SLASH))
488 104719 : p--;
489 :
490 21948 : if (p >= aPath)
491 : {
492 21948 : if (UNICHAR_SLASH == *p)
493 : {
494 21948 : p++;
495 21948 : *p = '\0';
496 : }
497 : else
498 : {
499 0 : *p = '\0';
500 : }
501 : }
502 :
503 21948 : return aPath;
504 : }
505 :
506 : /******************************************************
507 : *
508 : ******************************************************/
509 :
510 417175 : oslFileError _osl_resolvepath(
511 : /*inout*/ sal_Unicode* path,
512 : /*inout*/ bool* failed)
513 : {
514 417175 : oslFileError ferr = osl_File_E_None;
515 :
516 417175 : if (!*failed)
517 : {
518 : char unresolved_path[PATH_MAX];
519 417175 : if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path)))
520 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
521 :
522 : char resolved_path[PATH_MAX];
523 417175 : if (realpath(unresolved_path, resolved_path))
524 : {
525 416995 : if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX))
526 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
527 :
528 : }
529 : else
530 : {
531 180 : if (EACCES == errno || ENOTDIR == errno || ENOENT == errno)
532 180 : *failed = true;
533 : else
534 0 : ferr = oslTranslateFileError(OSL_FET_ERROR, errno);
535 : }
536 : }
537 :
538 417175 : return ferr;
539 : }
540 :
541 : /******************************************************
542 : * Works even with non existing paths. The resulting
543 : * path must not exceed PATH_MAX else
544 : * osl_File_E_NAMETOOLONG is the result
545 : ******************************************************/
546 :
547 33130 : oslFileError osl_getAbsoluteFileURL_impl_(const rtl::OUString& unresolved_path, rtl::OUString& resolved_path)
548 : {
549 : // the given unresolved path must not exceed PATH_MAX
550 33130 : if (unresolved_path.getLength() >= (PATH_MAX - 2))
551 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
552 :
553 : sal_Unicode path_resolved_so_far[PATH_MAX];
554 33130 : const sal_Unicode* punresolved = unresolved_path.getStr();
555 33130 : sal_Unicode* presolvedsf = path_resolved_so_far;
556 :
557 : // reserve space for leading '/' and trailing '\0'
558 : // do not exceed this limit
559 33130 : sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2;
560 :
561 : // if realpath fails with error ENOTDIR, EACCES or ENOENT
562 : // we will not call it again, because _osl_realpath should also
563 : // work with non existing directories etc.
564 33130 : bool realpath_failed = false;
565 : oslFileError ferr;
566 :
567 33130 : path_resolved_so_far[0] = '\0';
568 :
569 3091756 : while (*punresolved != '\0')
570 : {
571 : // ignore '/.' , skip one part back when '/..'
572 :
573 3025496 : if ((UNICHAR_DOT == *punresolved) && (UNICHAR_SLASH == *presolvedsf))
574 : {
575 21950 : if ('\0' == *(punresolved + 1))
576 : {
577 0 : punresolved++;
578 0 : continue;
579 : }
580 21950 : else if (UNICHAR_SLASH == *(punresolved + 1))
581 : {
582 2 : punresolved += 2;
583 2 : continue;
584 : }
585 21948 : else if ((UNICHAR_DOT == *(punresolved + 1)) && ('\0' == *(punresolved + 2) || (UNICHAR_SLASH == *(punresolved + 2))))
586 : {
587 21948 : _rmlastpathtoken(path_resolved_so_far);
588 :
589 21948 : presolvedsf = ustrtoend(path_resolved_so_far) - 1;
590 :
591 21948 : if (UNICHAR_SLASH == *(punresolved + 2))
592 21944 : punresolved += 3;
593 : else
594 4 : punresolved += 2;
595 :
596 21948 : continue;
597 : }
598 : else // a file or directory name may start with '.'
599 : {
600 0 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
601 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
602 :
603 0 : ustrchrcat(*punresolved++, path_resolved_so_far);
604 :
605 0 : if ('\0' == *punresolved && !realpath_failed)
606 : {
607 : ferr = _osl_resolvepath(
608 : path_resolved_so_far,
609 0 : &realpath_failed);
610 :
611 0 : if (osl_File_E_None != ferr)
612 0 : return ferr;
613 : }
614 : }
615 : }
616 3003546 : else if (UNICHAR_SLASH == *punresolved)
617 : {
618 389337 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
619 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
620 :
621 389337 : ustrchrcat(*punresolved++, path_resolved_so_far);
622 :
623 389337 : if (!realpath_failed)
624 : {
625 : ferr = _osl_resolvepath(
626 : path_resolved_so_far,
627 389312 : &realpath_failed);
628 :
629 389312 : if (osl_File_E_None != ferr)
630 0 : return ferr;
631 :
632 389312 : if (!_islastchr(path_resolved_so_far, UNICHAR_SLASH))
633 : {
634 356161 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
635 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
636 :
637 356161 : ustrchrcat(UNICHAR_SLASH, path_resolved_so_far);
638 : }
639 : }
640 : }
641 : else // any other character
642 : {
643 2614209 : if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
644 0 : return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG);
645 :
646 2614209 : ustrchrcat(*punresolved++, path_resolved_so_far);
647 :
648 2614209 : if ('\0' == *punresolved && !realpath_failed)
649 : {
650 : ferr = _osl_resolvepath(
651 : path_resolved_so_far,
652 27863 : &realpath_failed);
653 :
654 27863 : if (osl_File_E_None != ferr)
655 0 : return ferr;
656 : }
657 : }
658 : }
659 :
660 33130 : sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far);
661 :
662 : OSL_ASSERT(len < PATH_MAX);
663 :
664 33130 : resolved_path = rtl::OUString(path_resolved_so_far, len);
665 :
666 33130 : return osl_File_E_None;
667 : }
668 :
669 : } // end namespace private
670 :
671 :
672 : /******************************************************
673 : * osl_getAbsoluteFileURL
674 : ******************************************************/
675 :
676 33130 : oslFileError osl_getAbsoluteFileURL(rtl_uString* ustrBaseDirURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL)
677 : {
678 : // Work around the below call to getSystemPathFromFileURL rejecting input
679 : // that starts with "/" (for whatever reason it behaves that way; but
680 : // changing that would start to break lots of tests at least):
681 33130 : rtl::OUString relUrl(ustrRelativeURL);
682 33130 : if (relUrl.startsWith("//")) {
683 0 : relUrl = "file:" + relUrl;
684 33130 : } else if (relUrl.startsWith("/")) {
685 0 : relUrl = "file://" + relUrl;
686 : }
687 :
688 : FileBase::RC rc;
689 33130 : rtl::OUString unresolved_path;
690 :
691 33130 : rc = FileBase::getSystemPathFromFileURL(relUrl, unresolved_path);
692 :
693 33130 : if(FileBase::E_None != rc)
694 0 : return oslFileError(rc);
695 :
696 33130 : if (systemPathIsRelativePath(unresolved_path))
697 : {
698 9062 : rtl::OUString base_path;
699 9062 : rc = (FileBase::RC) osl_getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData, sal_False);
700 :
701 9062 : if (FileBase::E_None != rc)
702 0 : return oslFileError(rc);
703 :
704 9062 : rtl::OUString abs_path;
705 9062 : systemPathMakeAbsolutePath(base_path, unresolved_path, abs_path);
706 :
707 9062 : unresolved_path = abs_path;
708 : }
709 :
710 33130 : rtl::OUString resolved_path;
711 :
712 33130 : static bool allow_symlinks = getenv("SAL_ALLOW_LINKOO_SYMLINKS") != 0;
713 : // getenv is not thread safe, so minimize use of result
714 33130 : if (!allow_symlinks)
715 : {
716 33130 : rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
717 : }
718 : else
719 : {
720 : // SAL_ALLOW_LINKOO_SYMLINKS environment variable:
721 : // for linkoo to work, we need to let the symlinks to the libraries untouched
722 0 : rtl::OUString base;
723 0 : sal_Int32 last_slash = unresolved_path.lastIndexOf( UNICHAR_SLASH );
724 :
725 0 : if (last_slash >= 0 && last_slash + 1 < unresolved_path.getLength()
726 0 : && ! ( last_slash + 2 == unresolved_path.getLength() && unresolved_path.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("."), last_slash + 1) )
727 0 : && ! ( last_slash + 3 == unresolved_path.getLength() && unresolved_path.matchAsciiL(RTL_CONSTASCII_STRINGPARAM(".."), last_slash + 1) ))
728 : {
729 0 : base = unresolved_path.copy(last_slash+1);
730 0 : unresolved_path = unresolved_path.copy(0, last_slash);
731 : }
732 :
733 0 : rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
734 :
735 0 : if (!base.isEmpty())
736 : {
737 0 : resolved_path += rtl::OUString( UNICHAR_SLASH );
738 0 : resolved_path += base;
739 0 : }
740 : }
741 :
742 33130 : if (FileBase::E_None == rc)
743 : {
744 33130 : rc = (FileBase::RC) osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL);
745 : OSL_ASSERT(FileBase::E_None == rc);
746 : }
747 :
748 33130 : return oslFileError(rc);
749 : }
750 :
751 :
752 : namespace /* private */
753 : {
754 :
755 : /*********************************************
756 : No separate error code if unicode to text
757 : conversion or getenv fails because for the
758 : caller there is no difference why a file
759 : could not be found in $PATH
760 : ********************************************/
761 :
762 0 : bool find_in_PATH(const rtl::OUString& file_path, rtl::OUString& result)
763 : {
764 0 : bool bfound = false;
765 0 : rtl::OUString path(RTL_CONSTASCII_USTRINGPARAM("PATH"));
766 0 : rtl::OUString env_path;
767 :
768 0 : if (osl_Process_E_None == osl_getEnvironment(path.pData, &env_path.pData))
769 0 : bfound = osl::searchPath(file_path, env_path, result);
770 :
771 0 : return bfound;
772 : }
773 :
774 : /*********************************************
775 : No separate error code if unicode to text
776 : conversion or getcwd fails because for the
777 : caller there is no difference why a file
778 : could not be found in CDW
779 : ********************************************/
780 :
781 0 : bool find_in_CWD(const rtl::OUString& file_path, rtl::OUString& result)
782 : {
783 0 : bool bfound = false;
784 0 : rtl::OUString cwd_url;
785 :
786 0 : if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData))
787 : {
788 0 : rtl::OUString cwd;
789 0 : FileBase::getSystemPathFromFileURL(cwd_url, cwd);
790 0 : bfound = osl::searchPath(file_path, cwd, result);
791 : }
792 0 : return bfound;
793 : }
794 :
795 : /*********************************************
796 :
797 : ********************************************/
798 :
799 0 : bool find_in_searchPath(const rtl::OUString& file_path, rtl_uString* search_path, rtl::OUString& result)
800 : {
801 0 : return (search_path && osl::searchPath(file_path, rtl::OUString(search_path), result));
802 : }
803 :
804 : } // end namespace private
805 :
806 :
807 : /****************************************************************************
808 : * osl_searchFileURL
809 : ***************************************************************************/
810 :
811 0 : oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL)
812 : {
813 : OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter");
814 :
815 : FileBase::RC rc;
816 0 : rtl::OUString file_path;
817 :
818 : // try to interpret search path as file url else assume it's a system path list
819 0 : rc = FileBase::getSystemPathFromFileURL(rtl::OUString(ustrFilePath), file_path);
820 0 : if (FileBase::E_INVAL == rc)
821 0 : file_path = ustrFilePath;
822 0 : else if (FileBase::E_None != rc)
823 0 : return oslFileError(rc);
824 :
825 0 : bool bfound = false;
826 0 : rtl::OUString result;
827 :
828 0 : if (find_in_searchPath(file_path, ustrSearchPath, result) ||
829 0 : find_in_PATH(file_path, result) ||
830 0 : find_in_CWD(file_path, result))
831 : {
832 0 : rtl::OUString resolved;
833 :
834 0 : if (osl::realpath(result, resolved))
835 : {
836 : #if OSL_DEBUG_LEVEL > 0
837 : oslFileError osl_error =
838 : #endif
839 0 : osl_getFileURLFromSystemPath(resolved.pData, pustrURL);
840 : OSL_ASSERT(osl_File_E_None == osl_error);
841 0 : bfound = true;
842 0 : }
843 : }
844 0 : return bfound ? osl_File_E_None : osl_File_E_NOENT;
845 : }
846 :
847 :
848 : /****************************************************************************
849 : * FileURLToPath
850 : ***************************************************************************/
851 :
852 146566 : oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL)
853 : {
854 146566 : rtl_uString* ustrSystemPath = NULL;
855 146566 : oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &ustrSystemPath);
856 :
857 146566 : if(osl_File_E_None != osl_error)
858 5 : return osl_error;
859 :
860 146561 : osl_systemPathRemoveSeparator(ustrSystemPath);
861 :
862 : /* convert unicode path to text */
863 146561 : if(!UnicodeToText( buffer, bufLen, ustrSystemPath->buffer, ustrSystemPath->length))
864 0 : osl_error = oslTranslateFileError(OSL_FET_ERROR, errno);
865 :
866 146561 : rtl_uString_release(ustrSystemPath);
867 :
868 146561 : return osl_error;
869 : }
870 :
871 : /*****************************************************************************
872 : * UnicodeToText
873 : ****************************************************************************/
874 :
875 : namespace /* private */
876 : {
877 : class UnicodeToTextConverter_Impl
878 : {
879 : rtl_UnicodeToTextConverter m_converter;
880 :
881 1146 : UnicodeToTextConverter_Impl()
882 1146 : : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding()))
883 1146 : {}
884 :
885 1146 : ~UnicodeToTextConverter_Impl()
886 : {
887 1146 : rtl_destroyUnicodeToTextConverter (m_converter);
888 1146 : }
889 : public:
890 582455 : static UnicodeToTextConverter_Impl & getInstance()
891 : {
892 582455 : static UnicodeToTextConverter_Impl g_theConverter;
893 582455 : return g_theConverter;
894 : }
895 :
896 582455 : sal_Size convert(
897 : sal_Unicode const * pSrcBuf, sal_Size nSrcChars, sal_Char * pDstBuf, sal_Size nDstBytes,
898 : sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtChars)
899 : {
900 : OSL_ASSERT(m_converter != 0);
901 : return rtl_convertUnicodeToText (
902 582455 : m_converter, 0, pSrcBuf, nSrcChars, pDstBuf, nDstBytes, nFlags, pInfo, pSrcCvtChars);
903 : }
904 : };
905 : } // end namespace private
906 :
907 582455 : int UnicodeToText( char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen )
908 : {
909 582455 : sal_uInt32 nInfo = 0;
910 582455 : sal_Size nSrcChars = 0;
911 :
912 582455 : sal_Size nDestBytes = UnicodeToTextConverter_Impl::getInstance().convert (
913 : uniText, uniTextLen, buffer, bufLen,
914 1164910 : OUSTRING_TO_OSTRING_CVTFLAGS | RTL_UNICODETOTEXT_FLAGS_FLUSH, &nInfo, &nSrcChars);
915 :
916 582455 : if( nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL )
917 : {
918 0 : errno = EOVERFLOW;
919 0 : return 0;
920 : }
921 :
922 : /* ensure trailing '\0' */
923 582455 : buffer[nDestBytes] = '\0';
924 582455 : return nDestBytes;
925 : }
926 :
927 : /*****************************************************************************
928 : * TextToUnicode
929 : ****************************************************************************/
930 :
931 : namespace /* private */
932 : {
933 : class TextToUnicodeConverter_Impl
934 : {
935 : rtl_TextToUnicodeConverter m_converter;
936 :
937 477 : TextToUnicodeConverter_Impl()
938 477 : : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding()))
939 477 : {}
940 :
941 477 : ~TextToUnicodeConverter_Impl()
942 : {
943 477 : rtl_destroyTextToUnicodeConverter (m_converter);
944 477 : }
945 :
946 : public:
947 416995 : static TextToUnicodeConverter_Impl & getInstance()
948 : {
949 416995 : static TextToUnicodeConverter_Impl g_theConverter;
950 416995 : return g_theConverter;
951 : }
952 :
953 416995 : sal_Size convert(
954 : sal_Char const * pSrcBuf, sal_Size nSrcBytes, sal_Unicode * pDstBuf, sal_Size nDstChars,
955 : sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtBytes)
956 : {
957 : OSL_ASSERT(m_converter != 0);
958 : return rtl_convertTextToUnicode (
959 416995 : m_converter, 0, pSrcBuf, nSrcBytes, pDstBuf, nDstChars, nFlags, pInfo, pSrcCvtBytes);
960 : }
961 : };
962 : } // end namespace private
963 :
964 416995 : int TextToUnicode(
965 : const char* text,
966 : size_t text_buffer_size,
967 : sal_Unicode* unic_text,
968 : sal_Int32 unic_text_buffer_size)
969 : {
970 416995 : sal_uInt32 nInfo = 0;
971 416995 : sal_Size nSrcChars = 0;
972 :
973 416995 : sal_Size nDestBytes = TextToUnicodeConverter_Impl::getInstance().convert(
974 : text, text_buffer_size, unic_text, unic_text_buffer_size,
975 833990 : OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_FLUSH, &nInfo, &nSrcChars);
976 :
977 416995 : if (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL)
978 : {
979 0 : errno = EOVERFLOW;
980 0 : return 0;
981 : }
982 :
983 : /* ensure trailing '\0' */
984 416995 : unic_text[nDestBytes] = '\0';
985 416995 : return nDestBytes;
986 : }
987 :
988 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|