/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include #include "system.h" #ifndef _LIMITS_H #include #endif #ifndef _ERRNO_H #include #endif #ifndef _STDLIB_H_ #include #endif #ifndef _STRINGS_H #include #endif #ifndef _UNISTD_H #include #endif #include #include #include #include #include #include #ifndef _OSL_TREAD_H_ #include #endif #include #include #include #include "file_error_transl.h" #ifndef _FILE_URL_H_ #include "file_url.h" #endif #include "file_path_helper.hxx" #ifndef _OSL_UUNXAPI_HXX_ #include "uunxapi.hxx" #endif #include #include /*************************************************** General note This file contains the part that handles File URLs. File URLs as scheme specific notion of URIs (RFC2396) may be handled platform independend, but will not in osl which is considered wrong. Future version of osl should handle File URLs this way. In rtl/uri there is already an URI parser etc. so this code should be consolidated. **************************************************/ oslMutex g_CurrentDirectoryMutex; /*************************************************** * forward **************************************************/ void _osl_warnFile(const char*, rtl_uString*); rtl_uString* oslMakeUStrFromPsz(const sal_Char* pszStr,rtl_uString** uStr); extern "C" int UnicodeToText(char *, size_t, const sal_Unicode *, sal_Int32); extern "C" int TextToUnicode(const char* text, size_t text_buffer_size, sal_Unicode* unic_text, sal_Int32 unic_text_buffer_size); #define CHAR_POINTER(THE_OUSTRING) ::rtl::OUStringToOString (THE_OUSTRING, RTL_TEXTENCODING_UTF8).pData->buffer /*************************************************** * namespace directives **************************************************/ using namespace osl; /****************************************************************************** * * Exported Module Functions * *****************************************************************************/ /* a slightly modified version of Pchar in rtl/source/uri.c */ const sal_Bool uriCharClass[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* Pchar but without encoding slashes */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* !"#$%&'()*+,-./ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, /* 0123456789:;<=>? */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* PQRSTUVWXYZ[\]^_ */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 /* pqrstuvwxyz{|}~ */ }; /* check for top wrong usage strings */ /* static sal_Bool findWrongUsage( const sal_Unicode *path, sal_Int32 len ) { rtl_uString *pTmp = NULL; sal_Bool bRet; rtl_uString_newFromStr_WithLength( &pTmp, path, len ); rtl_ustr_toAsciiLowerCase_WithLength( pTmp->buffer, pTmp->length ); bRet = ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "ftp://", 6 ) ) || ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "http://", 7 ) ) || ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "vnd.sun.star", 12 ) ) || ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "private:", 8 ) ) || ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pTmp->buffer, pTmp->length, "slot:", 5) ); rtl_uString_release( pTmp ); return bRet; } */ /****************************************************************************/ /* osl_getFileURLFromSystemPath */ /****************************************************************************/ BOOL WINAPI IsValidFilePathComponent( LPCTSTR lpComponent, LPCTSTR *lppComponentEnd, DWORD dwFlags) { LPCTSTR lpComponentEnd = NULL; LPCTSTR lpCurrent = lpComponent; BOOL fValid = TRUE; /* Assume success */ TCHAR cLast = 0; /* Path component length must not exceed MAX_PATH */ while ( !lpComponentEnd && lpCurrent && lpCurrent - lpComponent < _MAX_PATH ) { switch ( *lpCurrent ) { /* Both backslash and slash determine the end of a path component */ case '\0': case '/': case '\\': switch ( cLast ) { /* Component must not end with '.' or blank and can't be empty */ case '.': if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE ) { if ( 1 == lpCurrent - lpComponent ) { /* Current directory is O.K. */ lpComponentEnd = lpCurrent; break; } else if ( 2 == lpCurrent - lpComponent && '.' == *lpComponent ) { /* Parent directory is O.K. */ lpComponentEnd = lpCurrent; break; } } case 0: case ' ': lpComponentEnd = lpCurrent - 1; fValid = FALSE; break; default: lpComponentEnd = lpCurrent; break; } break; /* '?' and '*' are valid wildcards but not valid file name characters */ case '?': case '*': if ( dwFlags & VALIDATEPATH_ALLOW_WILDCARDS ) break; /* The following characters are reserved */ case '<': case '>': case '\"': case '|': case ':': lpComponentEnd = lpCurrent; fValid = FALSE; break; default: /* Characters below ASCII 32 are not allowed */ if ( *lpCurrent < ' ' ) { lpComponentEnd = lpCurrent; fValid = FALSE; } break; } cLast = *lpCurrent++; } /* If we don't reached the end of the component the length of the component was to long ( See condition of while loop ) */ if ( !lpComponentEnd ) { fValid = FALSE; lpComponentEnd = lpCurrent; } /* Test wether the component specifies a device name what is not allowed */ // MT: PERFORMANCE: // This is very expensive. A lot of calls to _tcsicmp. // in SRC6870m71 67.000 calls of this method while empty office start result into more than 1.500.00 calls of _tcsicmp! // Possible optimizations // - Array should be const static // - Sorted array, use binary search // - More intelligent check for com1-9, lpt1-9 // Maybe make szComponent upper case, don't search case intensitive // Talked to HRO: Could be removed. Shouldn't be used in OOo, and if used for something like a filename, it will lead to an error anyway. /* if ( fValid ) { LPCTSTR alpDeviceNames[] = { TEXT("CON"), TEXT("PRN"), TEXT("AUX"), TEXT("CLOCK$"), TEXT("NUL"), TEXT("LPT1"), TEXT("LPT2"), TEXT("LPT3"), TEXT("LPT4"), TEXT("LPT5"), TEXT("LPT6"), TEXT("LPT7"), TEXT("LPT8"), TEXT("LPT9"), TEXT("COM1"), TEXT("COM2"), TEXT("COM3"), TEXT("COM4"), TEXT("COM5"), TEXT("COM6"), TEXT("COM7"), TEXT("COM8"), TEXT("COM9") }; TCHAR szComponent[MAX_PATH]; int nComponentLength; LPCTSTR lpDot; int i; // A device name with an extension is also invalid lpDot = _tcschr( lpComponent, '.' ); if ( !lpDot || lpDot > lpComponentEnd ) nComponentLength = lpComponentEnd - lpComponent; else nComponentLength = lpDot - lpComponent; _tcsncpy( szComponent, lpComponent, nComponentLength ); szComponent[nComponentLength] = 0; for ( i = 0; i < sizeof( alpDeviceNames ) / sizeof(LPCTSTR); i++ ) { if ( 0 == _tcsicmp( szComponent, alpDeviceNames[i] ) ) { lpComponentEnd = lpComponent; fValid = FALSE; break; } } } */ if ( fValid ) { // Empty components are not allowed if ( lpComponentEnd - lpComponent < 1 ) fValid = FALSE; // If we reached the end of the string NULL is returned else if ( !*lpComponentEnd ) lpComponentEnd = NULL; } if ( lppComponentEnd ) *lppComponentEnd = lpComponentEnd; return fValid; } //##################################################### DWORD WINAPI IsValidFilePath(LPCTSTR lpszPath, LPCTSTR *lppError, DWORD dwFlags) { LPCTSTR lpComponent; BOOL fValid = TRUE; DWORD dwPathType = PATHTYPE_ERROR; if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE ) dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE; if ( !lpszPath ) { fValid = FALSE; lpComponent = lpszPath; } /* Test for UNC path notation */ if ( 2 == _tcsspn( lpszPath, CHARSET_SEPARATOR ) ) { /* Place the pointer behind the leading to backslashes */ lpComponent = lpszPath + 2; fValid = IsValidFilePathComponent( lpComponent, &lpComponent, VALIDATEPATH_ALLOW_ELLIPSE ); /* So far we have a valid servername. Now let's see if we also have a network resource */ dwPathType = PATHTYPE_ABSOLUTE_UNC; if ( fValid ) { if ( lpComponent && !*++lpComponent ) lpComponent = NULL; if ( !lpComponent ) { #if 0 /* We only have a Server specification what is invalid */ lpComponent = lpszPath; fValid = FALSE; #else dwPathType |= PATHTYPE_IS_SERVER; #endif } else { /* Now test the network resource */ fValid = IsValidFilePathComponent( lpComponent, &lpComponent, 0 ); /* If we now reached the end of the path, everything is O.K. */ if ( fValid && (!lpComponent || lpComponent && !*++lpComponent ) ) { lpComponent = NULL; dwPathType |= PATHTYPE_IS_VOLUME; } } } } /* Local path verification. Must start with : */ else if ( _istalpha( lpszPath[0] ) && ':' == lpszPath[1] ) { /* Place pointer behind correct drive specification */ lpComponent = lpszPath + 2; if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) ) lpComponent++; else if ( *lpComponent ) fValid = FALSE; dwPathType = PATHTYPE_ABSOLUTE_LOCAL; /* Now we are behind the backslash or it was a simple drive without backslash */ if ( fValid && !*lpComponent ) { lpComponent = NULL; dwPathType |= PATHTYPE_IS_VOLUME; } } /* Can be a relative path */ else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE ) { lpComponent = lpszPath; /* Relative path can start with a backslash */ if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) ) { lpComponent++; if ( !*lpComponent ) lpComponent = NULL; } dwPathType = PATHTYPE_RELATIVE; } /* Anything else is an error */ else { fValid = FALSE; lpComponent = lpszPath; } /* Now validate each component of the path */ while ( fValid && lpComponent ) { fValid = IsValidFilePathComponent( lpComponent, &lpComponent, dwFlags ); if ( fValid && lpComponent ) { lpComponent++; /* If the string behind the backslash is empty, we've done */ if ( !*lpComponent ) lpComponent = NULL; } } if ( fValid && _tcslen( lpszPath ) >= _MAX_PATH ) { fValid = FALSE; lpComponent = lpszPath + _MAX_PATH; } if ( lppError ) *lppError = lpComponent; return fValid ? dwPathType : PATHTYPE_ERROR; } sal_Bool _osl_decodeURL( rtl_String* strUTF8, rtl_uString** pstrDecodedURL ) { sal_Char *pBuffer; const sal_Char *pSrcEnd; const sal_Char *pSrc; sal_Char *pDest; sal_Int32 nSrcLen; sal_Bool bValidEncoded = sal_True; /* Assume success */ /* The resulting decoded string length is shorter or equal to the source length */ nSrcLen = rtl_string_getLength(strUTF8); pBuffer = reinterpret_cast(rtl_allocateMemory(nSrcLen + 1)); pDest = pBuffer; pSrc = rtl_string_getStr(strUTF8); pSrcEnd = pSrc + nSrcLen; /* Now decode the URL what should result in an UTF8 string */ while ( bValidEncoded && pSrc < pSrcEnd ) { switch ( *pSrc ) { case '%': { sal_Char aToken[3]; sal_Char aChar; pSrc++; aToken[0] = *pSrc++; aToken[1] = *pSrc++; aToken[2] = 0; aChar = (sal_Char)strtoul( aToken, NULL, 16 ); /* The chars are path delimiters and must not be encoded */ if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar ) bValidEncoded = sal_False; else *pDest++ = aChar; } break; default: *pDest++ = *pSrc++; break; } } *pDest++ = 0; if ( bValidEncoded ) { rtl_string2UString( pstrDecodedURL, pBuffer, rtl_str_getLength(pBuffer), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS ); OSL_ASSERT(*pstrDecodedURL != 0); } rtl_freeMemory( pBuffer ); return bValidEncoded; } //############################################# void _osl_encodeURL( rtl_uString *strURL, rtl_String **pstrEncodedURL ) { /* Encode non ascii characters within the URL */ rtl_String *strUTF8 = NULL; sal_Char *pszEncodedURL; const sal_Char *pURLScan; sal_Char *pURLDest; sal_Int32 nURLScanLen; sal_Int32 nURLScanCount; rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS ); pszEncodedURL = (sal_Char*) rtl_allocateMemory( (rtl_string_getLength( strUTF8 ) * 3 + 1) * sizeof(sal_Char) ); pURLDest = pszEncodedURL; pURLScan = rtl_string_getStr( strUTF8 ); nURLScanLen = rtl_string_getLength( strUTF8 ); nURLScanCount = 0; while ( nURLScanCount < nURLScanLen ) { sal_Char cCurrent = *pURLScan; switch ( cCurrent ) { default: if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) ) { sprintf( pURLDest, "%%%02X", (unsigned char)cCurrent ); pURLDest += 3; break; } case '!': case '\'': case '(': case ')': case '*': case '-': case '.': case '_': case '~': case '$': case '&': case '+': case ',': case '=': case '@': case ':': case '/': case '\\': case '|': *pURLDest++ = cCurrent; break; case 0: break; } pURLScan++; nURLScanCount++; } *pURLDest = 0; rtl_string_release( strUTF8 ); rtl_string_newFromStr( pstrEncodedURL, pszEncodedURL ); rtl_freeMemory( pszEncodedURL ); } //############################################# oslFileError SAL_CALL _osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL ) { oslFileError nError = osl_File_E_INVAL; /* Assume failure */ rtl_uString *strTempURL = NULL; DWORD dwPathType = PATHTYPE_ERROR; if (strPath) dwPathType = IsValidFilePath(strPath->buffer, NULL, VALIDATEPATH_ALLOW_RELATIVE); if (dwPathType) { rtl_uString *strTempPath = NULL; /* Replace backslashes */ rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' ); switch ( dwPathType & PATHTYPE_MASK_TYPE ) { case PATHTYPE_RELATIVE: rtl_uString_assign( &strTempURL, strTempPath ); nError = osl_File_E_None; break; case PATHTYPE_ABSOLUTE_UNC: rtl_uString_newFromAscii( &strTempURL, "file:" ); rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath ); nError = osl_File_E_None; break; case PATHTYPE_ABSOLUTE_LOCAL: rtl_uString_newFromAscii( &strTempURL, "file:///" ); rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath ); nError = osl_File_E_None; break; default: break; } /* Release temp path */ rtl_uString_release( strTempPath ); } if ( osl_File_E_None == nError ) { rtl_String *strEncodedURL = NULL; /* Encode the URL */ _osl_encodeURL( strTempURL, &strEncodedURL ); /* Provide URL via unicode string */ rtl_string2UString( pstrURL, rtl_string_getStr(strEncodedURL), rtl_string_getLength(strEncodedURL), RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS ); OSL_ASSERT(*pstrURL != 0); rtl_string_release( strEncodedURL ); } /* Release temp URL */ if ( strTempURL ) rtl_uString_release( strTempURL ); /* OSL_ENSURE_FILE( !nError, "osl_getFileURLFromSystemPath: \"%s\" is not a systemPath !!!", strPath ); */ return nError; } oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL ) { return _osl_getFileURLFromSystemPath( ustrSystemPath, pustrFileURL ); #if 0 static const sal_Unicode pDoubleSlash[2] = { '/', '/' }; rtl_uString *pTmp = NULL; sal_Int32 nIndex; if( 0 == ustrSystemPath->length ) return osl_File_E_INVAL; /* YD convert '\' to '/' */ rtl_ustr_replaceChar( ustrSystemPath->buffer, '\\', '/' ); /* temporary hack: if already file url, return ustrSystemPath */ if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file:", 5 ) ) { /* if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file://", 7 ) ) { OSL_ENSURE( 0, "osl_getFileURLFromSystemPath: input is already file URL" ); rtl_uString_assign( pustrFileURL, ustrSystemPath ); } else { rtl_uString *pTmp2 = NULL; OSL_ENSURE( 0, "osl_getFileURLFromSystemPath: input is wrong file URL" ); rtl_uString_newFromStr_WithLength( pustrFileURL, ustrSystemPath->buffer + 5, ustrSystemPath->length - 5 ); rtl_uString_newFromAscii( &pTmp2, "file://" ); rtl_uString_newConcat( pustrFileURL, *pustrFileURL, pTmp2 ); rtl_uString_release( pTmp2 ); } return osl_File_E_None; */ return osl_File_E_INVAL; } /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */ if( (sal_Unicode) '~' == ustrSystemPath->buffer[0] ) { /* check if another user is specified */ if( ( 1 == ustrSystemPath->length ) || ( (sal_Unicode)'/' == ustrSystemPath->buffer[1] ) ) { /* osl_getHomeDir returns file URL */ osl_getHomeDir( osl_getCurrentSecurity(), &pTmp ); /* remove "file://" prefix */ rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 ); /* replace '~' in original string */ rtl_uString_newReplaceStrAt( &pTmp, ustrSystemPath, 0, 1, pTmp ); } else { /* FIXME: replace ~user with users home directory */ return osl_File_E_INVAL; } } /* check if initial string contains double instances of '/' */ nIndex = rtl_ustr_indexOfStr_WithLength( ustrSystemPath->buffer, ustrSystemPath->length, pDoubleSlash, 2 ); if( -1 != nIndex ) { sal_Int32 nSrcIndex; sal_Int32 nDeleted = 0; /* if pTmp is not already allocated, copy ustrSystemPath for modification */ if( NULL == pTmp ) rtl_uString_newFromString( &pTmp, ustrSystemPath ); /* adapt index to pTmp */ nIndex += pTmp->length - ustrSystemPath->length; /* remove all occurances of '//' */ for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ ) { if( ((sal_Unicode) '/' == pTmp->buffer[nSrcIndex]) && ((sal_Unicode) '/' == pTmp->buffer[nIndex]) ) nDeleted++; else pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex]; } /* adjust length member */ pTmp->length -= nDeleted; } if( NULL == pTmp ) rtl_uString_assign( &pTmp, ustrSystemPath ); /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */ /* OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) ); */ /* file URLs must be URI encoded */ rtl_uriEncode( pTmp, uriCharClass, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL ); rtl_uString_release( pTmp ); /* absolute urls should start with 'file://' */ if( (sal_Unicode)'/' == (*pustrFileURL)->buffer[0] ) { rtl_uString *pProtocol = NULL; rtl_uString_newFromAscii( &pProtocol, "file://" ); rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL ); rtl_uString_release( pProtocol ); } return osl_File_E_None; #endif } //############################################# oslFileError SAL_CALL _osl_getSystemPathFromFileURL( rtl_uString *strURL, rtl_uString **pustrPath, sal_Bool bAllowRelative ) { rtl_String *strUTF8 = NULL; rtl_uString *strDecodedURL = NULL; rtl_uString *strTempPath = NULL; const sal_Unicode *pDecodedURL; sal_uInt32 nDecodedLen; sal_Bool bValidEncoded; oslFileError nError = osl_File_E_INVAL; /* Assume failure */ /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from having a mixed encoded URL later */ rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS ); /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */ OSL_ENSURE_FILE( strUTF8->length == strURL->length || 0 != rtl_ustr_ascii_shortenedCompare_WithLength( strURL->buffer, strURL->length, "file:\\\\", 7 ) ,"osl_getSystemPathFromFileURL: \"%s\" is not encoded !!!", strURL ); bValidEncoded = _osl_decodeURL( strUTF8, &strDecodedURL ); /* Release the encoded UTF8 string */ rtl_string_release( strUTF8 ); if ( bValidEncoded ) { /* Replace backslashes and pipes */ rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '/', '\\' ); rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '|', ':' ); pDecodedURL = rtl_uString_getStr( strDecodedURL ); nDecodedLen = rtl_uString_getLength( strDecodedURL ); /* Must start with "file://" */ if ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pDecodedURL, nDecodedLen, "file:\\\\", 7 ) ) { sal_uInt32 nSkip; if ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pDecodedURL, nDecodedLen, "file:\\\\\\", 8 ) ) nSkip = 8; else if ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pDecodedURL, nDecodedLen, "file:\\\\localhost\\", 17 ) || 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pDecodedURL, nDecodedLen, "file:\\\\127.0.0.1\\", 17 ) ) nSkip = 17; else nSkip = 7; /* Indicates local root */ if ( nDecodedLen == nSkip ) rtl_uString_newFromStr_WithLength( &strTempPath, (const sal_Unicode*)WSTR_SYSTEM_ROOT_PATH, ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1 ); else rtl_uString_newFromStr_WithLength( &strTempPath, pDecodedURL + nSkip, nDecodedLen - nSkip ); if ( IsValidFilePath( strTempPath->buffer, NULL, VALIDATEPATH_ALLOW_ELLIPSE ) ) nError = osl_File_E_None; } else if ( bAllowRelative ) /* This maybe a relative file URL */ { rtl_uString_assign( &strTempPath, strDecodedURL ); if ( IsValidFilePath( strTempPath->buffer, NULL, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE ) ) nError = osl_File_E_None; } /* else OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not an absolute FileURL !!!", strURL ); */ } if ( strDecodedURL ) rtl_uString_release( strDecodedURL ); if ( osl_File_E_None == nError ) rtl_uString_assign( pustrPath, strTempPath ); if ( strTempPath ) rtl_uString_release( strTempPath ); /* OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not a FileURL !!!", strURL ); */ return nError; } /****************************************************************************/ /* osl_getSystemPathFromFileURL */ /****************************************************************************/ oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath ) { return _osl_getSystemPathFromFileURL( ustrFileURL, pustrSystemPath, sal_True ); #if 0 sal_Int32 nIndex = 0; rtl_uString * pTmp = NULL; sal_Unicode encodedSlash[3] = { '%', '2', 'F' }; /* temporary hack: if already system path, return ustrFileURL */ /* if( (sal_Unicode) '/' == ustrFileURL->buffer[0] ) { OSL_ENSURE( 0, "osl_getSystemPathFromFileURL: input is already system path" ); rtl_uString_assign( pustrSystemPath, ustrFileURL ); return osl_File_E_None; } */ /* a valid file url may not start with '/' */ if( ( 0 == ustrFileURL->length ) || ( (sal_Unicode) '/' == ustrFileURL->buffer[0] ) ) { return osl_File_E_INVAL; } /* search for encoded slashes (%2F) and decode every single token if we find one */ if( -1 != rtl_ustr_indexOfStr_WithLength( ustrFileURL->buffer, ustrFileURL->length, encodedSlash, 3 ) ) { rtl_uString * ustrPathToken = NULL; sal_Int32 nOffset = 7; do { nOffset += nIndex; /* break url down in '/' devided tokens tokens */ nIndex = rtl_ustr_indexOfChar_WithLength( ustrFileURL->buffer + nOffset, ustrFileURL->length - nOffset, (sal_Unicode) '/' ); /* copy token to new string */ rtl_uString_newFromStr_WithLength( &ustrPathToken, ustrFileURL->buffer + nOffset, -1 == nIndex ? ustrFileURL->length - nOffset : nIndex++ ); /* decode token */ rtl_uriDecode( ustrPathToken, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp ); /* the result should not contain any '/' */ if( -1 != rtl_ustr_indexOfChar_WithLength( pTmp->buffer, pTmp->length, (sal_Unicode) '/' ) ) { rtl_uString_release( pTmp ); rtl_uString_release( ustrPathToken ); return osl_File_E_INVAL; } } while( -1 != nIndex ); /* release temporary string and restore index variable */ rtl_uString_release( ustrPathToken ); nIndex = 0; } /* protocol and server should not be encoded, so decode the whole string */ rtl_uriDecode( ustrFileURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8, &pTmp ); /* check if file protocol specified */ /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */ if( 7 <= pTmp->length ) { rtl_uString * pProtocol = NULL; rtl_uString_newFromStr_WithLength( &pProtocol, pTmp->buffer, 7 ); /* protocol is case insensitive */ rtl_ustr_toAsciiLowerCase_WithLength( pProtocol->buffer, pProtocol->length ); if( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pProtocol->buffer, pProtocol->length,"file://", 7 ) ) nIndex = 7; rtl_uString_release( pProtocol ); } /* skip "localhost" or "127.0.0.1" if "file://" is specified */ /* FIXME: use rtl_ustr_ascii_shortenedCompareIgnoreCase_WithLength when available */ if( nIndex && ( 10 <= pTmp->length - nIndex ) ) { rtl_uString * pServer = NULL; rtl_uString_newFromStr_WithLength( &pServer, pTmp->buffer + nIndex, 10 ); /* server is case insensitive */ rtl_ustr_toAsciiLowerCase_WithLength( pServer->buffer, pServer->length ); if( ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"localhost/", 10 ) ) || ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( pServer->buffer, pServer->length,"127.0.0.1/", 10 ) ) ) { /* don't exclude the '/' */ nIndex += 9; } rtl_uString_release( pServer ); } if( nIndex ) rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + nIndex, pTmp->length - nIndex ); /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */ if( (sal_Unicode) '~' == pTmp->buffer[0] ) { /* check if another user is specified */ if( ( 1 == pTmp->length ) || ( (sal_Unicode)'/' == pTmp->buffer[1] ) ) { rtl_uString *pTmp2 = NULL; /* osl_getHomeDir returns file URL */ osl_getHomeDir( osl_getCurrentSecurity(), &pTmp2 ); /* remove "file://" prefix */ rtl_uString_newFromStr_WithLength( &pTmp2, pTmp2->buffer + 7, pTmp2->length - 7 ); /* replace '~' in original string */ rtl_uString_newReplaceStrAt( &pTmp, pTmp, 0, 1, pTmp2 ); rtl_uString_release( pTmp2 ); } else { /* FIXME: replace ~user with users home directory */ return osl_File_E_INVAL; } } /* temporary check for top 5 wrong usage strings (which are valid but unlikly filenames) */ /* OSL_ASSERT( !findWrongUsage( pTmp->buffer, pTmp->length ) ); */ *pustrSystemPath = pTmp; return osl_File_E_None; #endif // 0 } /**************************************************************************** * osl_getSystemPathFromFileURL_Ex - helper function * clients may specify if they want to accept relative * URLs or not ****************************************************************************/ oslFileError osl_getSystemPathFromFileURL_Ex( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath, sal_Bool bAllowRelative) { return _osl_getSystemPathFromFileURL( ustrFileURL, pustrSystemPath, bAllowRelative); #if 0 rtl_uString* temp = 0; oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp); if (osl_File_E_None == osl_error) { if (bAllowRelative || (UNICHAR_SLASH == temp->buffer[0]) || (UNICHAR_COLON == temp->buffer[1] && UNICHAR_SLASH == temp->buffer[2])) { *pustrSystemPath = temp; } else { rtl_uString_release(temp); osl_error = osl_File_E_INVAL; } } return osl_error; #endif } namespace /* private */ { #if 0 // YD /****************************************************** * Helper function, return a pinter to the final '\0' * of a string ******************************************************/ sal_Unicode* ustrtoend(sal_Unicode* pStr) { return (pStr + rtl_ustr_getLength(pStr)); } /********************************************* ********************************************/ sal_Unicode* ustrcpy(const sal_Unicode* s, sal_Unicode* d) { const sal_Unicode* sc = s; sal_Unicode* dc = d; while ((*dc++ = *sc++)) /**/; return d; } /********************************************* ********************************************/ sal_Unicode* ustrncpy(const sal_Unicode* s, sal_Unicode* d, unsigned int n) { const sal_Unicode* sc = s; sal_Unicode* dc = d; unsigned int i = n; while (i--) *dc++ = *sc++; if (n) *dc = 0; return d; } /********************************************* ********************************************/ sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d) { sal_Unicode* p = ustrtoend(d); *p++ = chr; *p = 0; return d; } /********************************************* ********************************************/ sal_Unicode* ustrcat(const sal_Unicode* s, sal_Unicode* d) { sal_Unicode* dc = ustrtoend(d); ustrcpy(s, dc); return d; } /****************************************************** * ******************************************************/ bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr) { sal_Unicode* p = ustrtoend(pStr); if (p > pStr) p--; return (*p == Chr); } /****************************************************** * Ensure that the given string has the specified last * character if necessary append it ******************************************************/ sal_Unicode* _strensurelast(sal_Unicode* pStr, sal_Unicode Chr) { if (!_islastchr(pStr, Chr)) ustrchrcat(Chr, pStr); return pStr; } /****************************************************** * Remove the last part of a path, a path that has * only a '/' or no '/' at all will be returned * unmodified ******************************************************/ sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath) { /* we always may skip -2 because we may at least stand on a '/' but either there is no other character before this '/' or it's another character than the '/' */ sal_Unicode* p = ustrtoend(aPath) - 2; // move back to the next path separator // or to the start of the string while ((p > aPath) && (*p != UNICHAR_SLASH)) p--; if (p >= aPath) { if (UNICHAR_SLASH == *p) { p++; *p = '\0'; } else { *p = '\0'; } } return aPath; } /****************************************************** * ******************************************************/ oslFileError _osl_resolvepath( /*inout*/ sal_Unicode* path, /*inout*/ sal_Unicode* current_pos, /*in */ sal_Unicode* sentinel, /*inout*/ bool* failed) { oslFileError ferr = osl_File_E_None; if (!*failed) { char unresolved_path[PATH_MAX]; if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path))) return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG); char resolved_path[PATH_MAX]; if (realpath(unresolved_path, resolved_path)) { if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX)) return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG); current_pos = ustrtoend(path) - 1; } else { if (EACCES == errno || ENOTDIR == errno || ENOENT == errno) *failed = true; else ferr = oslTranslateFileError(OSL_FET_ERROR, errno); } } return ferr; } /****************************************************** * Works even with non existing paths. The resulting * path must not exceed PATH_MAX else * osl_File_E_NAMETOOLONG is the result ******************************************************/ oslFileError osl_getAbsoluteFileURL_impl_(const rtl::OUString& unresolved_path, rtl::OUString& resolved_path) { // the given unresolved path must not exceed PATH_MAX if (unresolved_path.getLength() >= (PATH_MAX - 2)) return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG); sal_Unicode path_resolved_so_far[PATH_MAX]; const sal_Unicode* punresolved = unresolved_path.getStr(); sal_Unicode* presolvedsf = path_resolved_so_far; // reserve space for leading '/' and trailing '\0' // do not exceed this limit sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2; // if realpath fails with error ENOTDIR, EACCES or ENOENT // we will not call it again, because _osl_realpath should also // work with non existing directories etc. bool realpath_failed = false; oslFileError ferr; path_resolved_so_far[0] = '\0'; while (*punresolved != '\0') { // ignore '/.' , skip one part back when '/..' if ((UNICHAR_DOT == *punresolved) && (UNICHAR_SLASH == *presolvedsf)) { if ('\0' == *(punresolved + 1)) { punresolved++; continue; } else if (UNICHAR_SLASH == *(punresolved + 1)) { punresolved += 2; continue; } else if ((UNICHAR_DOT == *(punresolved + 1)) && ('\0' == *(punresolved + 2) || (UNICHAR_SLASH == *(punresolved + 2)))) { _rmlastpathtoken(path_resolved_so_far); presolvedsf = ustrtoend(path_resolved_so_far) - 1; if (UNICHAR_SLASH == *(punresolved + 2)) punresolved += 3; else punresolved += 2; continue; } else // a file or directory name may start with '.' { if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG); ustrchrcat(*punresolved++, path_resolved_so_far); if ('\0' == *punresolved && !realpath_failed) { ferr = _osl_resolvepath( path_resolved_so_far, presolvedsf, sentinel, &realpath_failed); if (osl_File_E_None != ferr) return ferr; } } } else if (UNICHAR_SLASH == *punresolved) { if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG); ustrchrcat(*punresolved++, path_resolved_so_far); if (!realpath_failed) { ferr = _osl_resolvepath( path_resolved_so_far, presolvedsf, sentinel, &realpath_failed); if (osl_File_E_None != ferr) return ferr; if (!_islastchr(path_resolved_so_far, UNICHAR_SLASH)) { if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG); ustrchrcat(UNICHAR_SLASH, path_resolved_so_far); } } } else // any other character { if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) return oslTranslateFileError(OSL_FET_ERROR, ENAMETOOLONG); ustrchrcat(*punresolved++, path_resolved_so_far); if ('\0' == *punresolved && !realpath_failed) { ferr = _osl_resolvepath( path_resolved_so_far, presolvedsf, sentinel, &realpath_failed); if (osl_File_E_None != ferr) return ferr; } } } sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far); OSL_ASSERT(len < PATH_MAX); resolved_path = rtl::OUString(path_resolved_so_far, len); return osl_File_E_None; } #endif // 0 // YD } // end namespace private #if OSL_DEBUG_LEVEL > 0 //##################################################### void _osl_warnFile( const char *message, rtl_uString *ustrFile ) { char szBuffer[2048]; if (ustrFile) { rtl_String *strFile = NULL; rtl_uString2String( &strFile, rtl_uString_getStr( ustrFile ), rtl_uString_getLength( ustrFile ), osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS ); snprintf( szBuffer, sizeof(szBuffer), message, strFile->buffer ); rtl_string_release( strFile ); message = szBuffer; } OSL_ENSURE( 0, message ); } #endif // OSL_DEBUG_LEVEL > 0 /****************************************************** * osl_getAbsoluteFileURL ******************************************************/ //oslFileError osl_getAbsoluteFileURL(rtl_uString* ustrBaseDirURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL) oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL ) { oslFileError eError; rtl_uString *ustrRelSysPath = NULL; rtl_uString *ustrBaseSysPath = NULL; if ( ustrBaseURL && ustrBaseURL->length ) { eError = _osl_getSystemPathFromFileURL( ustrBaseURL, &ustrBaseSysPath, sal_False ); OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" ); eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_True ); } else { eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_False ); OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" ); } if ( !eError ) { CHAR szBuffer[_MAX_PATH]; CHAR szRelSysPath[_MAX_PATH]; CHAR szCurrentDir[_MAX_PATH]; int result; char* cwd; int rc; /*@@@ToDo Bad, bad hack, this only works if the base path really exists which is not necessary according to RFC2396 The whole FileURL implementation should be merged with the rtl/uri class. */ if ( ustrBaseSysPath ) { CHAR szBaseSysPath[_MAX_PATH]; if (!g_CurrentDirectoryMutex) g_CurrentDirectoryMutex = osl_createMutex(); osl_acquireMutex( g_CurrentDirectoryMutex ); cwd = getcwd( szCurrentDir, sizeof(szCurrentDir) ); UnicodeToText( szBaseSysPath, sizeof(szBaseSysPath), ustrBaseSysPath->buffer, ustrBaseSysPath->length); rc = chdir( szBaseSysPath); } UnicodeToText( szRelSysPath, sizeof(szRelSysPath), ustrRelSysPath->buffer, ustrRelSysPath->length); result = !_abspath( szBuffer, szRelSysPath, sizeof(szBuffer)); if ( ustrBaseSysPath ) { rc = chdir( szCurrentDir ); osl_releaseMutex( g_CurrentDirectoryMutex ); } if ( result ) { rtl_uString *ustrAbsSysPath = NULL; oslMakeUStrFromPsz( szBuffer, &ustrAbsSysPath); eError = osl_getFileURLFromSystemPath( ustrAbsSysPath, pustrAbsoluteURL ); if ( ustrAbsSysPath ) rtl_uString_release( ustrAbsSysPath ); } else eError = osl_File_E_INVAL; } if ( ustrBaseSysPath ) rtl_uString_release( ustrBaseSysPath ); if ( ustrRelSysPath ) rtl_uString_release( ustrRelSysPath ); return eError; #if 0 FileBase::RC rc; rtl::OUString unresolved_path; rc = FileBase::getSystemPathFromFileURL(rtl::OUString(ustrRelativeURL), unresolved_path); if(FileBase::E_None != rc) return oslFileError(rc); if (systemPathIsRelativePath(unresolved_path)) { rtl::OUString base_path; rc = (FileBase::RC) osl_getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData, sal_False); if (FileBase::E_None != rc) return oslFileError(rc); rtl::OUString abs_path; systemPathMakeAbsolutePath(base_path, unresolved_path, abs_path); unresolved_path = abs_path; } rtl::OUString resolved_path; rc = (FileBase::RC) osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path); if (FileBase::E_None == rc) { rc = (FileBase::RC) osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL); OSL_ASSERT(FileBase::E_None == rc); } return oslFileError(rc); #endif // 0 } namespace /* private */ { /********************************************* No separate error code if unicode to text conversion or getenv fails because for the caller there is no difference why a file could not be found in $PATH ********************************************/ bool find_in_PATH(const rtl::OUString& file_path, rtl::OUString& result) { bool bfound = false; rtl::OUString path = rtl::OUString::createFromAscii("PATH"); rtl::OUString env_path; if (osl_Process_E_None == osl_getEnvironment(path.pData, &env_path.pData)) bfound = osl::searchPath(file_path, env_path, result); return bfound; } /********************************************* No separate error code if unicode to text conversion or getcwd fails because for the caller there is no difference why a file could not be found in CDW ********************************************/ bool find_in_CWD(const rtl::OUString& file_path, rtl::OUString& result) { bool bfound = false; rtl::OUString cwd_url; if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData)) { rtl::OUString cwd; FileBase::getSystemPathFromFileURL(cwd_url, cwd); bfound = osl::searchPath(file_path, cwd, result); } return bfound; } /********************************************* ********************************************/ bool find_in_searchPath(const rtl::OUString& file_path, rtl_uString* search_path, rtl::OUString& result) { return (search_path && osl::searchPath(file_path, rtl::OUString(search_path), result)); } } // end namespace private /**************************************************************************** * osl_searchFileURL ***************************************************************************/ oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL) { OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter"); FileBase::RC rc; rtl::OUString file_path; // try to interpret search path as file url else assume it's a system path list rc = FileBase::getSystemPathFromFileURL(rtl::OUString(ustrFilePath), file_path); if ((FileBase::E_None != rc) && (FileBase::E_INVAL == rc)) file_path = ustrFilePath; else if (FileBase::E_None != rc) return oslFileError(rc); bool bfound = false; rtl::OUString result; if (find_in_searchPath(file_path, ustrSearchPath, result) || find_in_PATH(file_path, result) || find_in_CWD(file_path, result)) { rtl::OUString resolved; if (osl::realpath(result, resolved)) { #if OSL_DEBUG_LEVEL > 0 oslFileError osl_error = #endif osl_getFileURLFromSystemPath(resolved.pData, pustrURL); OSL_ASSERT(osl_File_E_None == osl_error); bfound = true; } } return bfound ? osl_File_E_None : osl_File_E_NOENT; } /**************************************************************************** * FileURLToPath ***************************************************************************/ oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL) { rtl_uString* ustrSystemPath = NULL; oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &ustrSystemPath); if(osl_File_E_None != osl_error) return osl_error; osl_systemPathRemoveSeparator(ustrSystemPath); /* convert unicode path to text */ if(!UnicodeToText( buffer, bufLen, ustrSystemPath->buffer, ustrSystemPath->length)) osl_error = oslTranslateFileError(OSL_FET_ERROR, errno); rtl_uString_release(ustrSystemPath); return osl_error; }