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