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