blob: 70fdd0fce9fb9df9ffe99f4304e5d63a3c8bad7b [file] [log] [blame]
/**************************************************************
*
* 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.
*
*************************************************************/
#define UNICODE
#define _UNICODE
#define _WIN32_WINNT_0x0500
#include "systools/win32/uwinapi.h"
#include "file_url.h"
#include "file_error.h"
#include "rtl/alloc.h"
#include "osl/diagnose.h"
#include "osl/file.h"
#include "osl/mutex.h"
#include "path_helper.hxx"
#include <stdio.h>
#include <tchar.h>
#if OSL_DEBUG_LEVEL > 0
#define OSL_ENSURE_FILE( cond, msg, file ) ( (cond) ? (void)0 : _osl_warnFile( msg, file ) )
#else
#define OSL_ENSURE_FILE( cond, msg, file ) ((void)0)
#endif
#define ELEMENTS_OF_ARRAY(arr) (sizeof(arr)/(sizeof((arr)[0])))
#define WSTR_SYSTEM_ROOT_PATH L"\\\\.\\"
#define WSTR_LONG_PATH_PREFIX L"\\\\?\\"
#define WSTR_LONG_PATH_PREFIX_UNC L"\\\\?\\UNC\\"
//##################################################################
// FileURL functions
//##################################################################
extern "C" oslMutex g_CurrentDirectoryMutex; /* Initialized in dllentry.c */
oslMutex g_CurrentDirectoryMutex = 0;
//#####################################################
static BOOL 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 even if long path with "\\?\" prefix is used */
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;
}
//#####################################################
#define CHARSET_SEPARATOR TEXT("\\/")
DWORD IsValidFilePath(rtl_uString *path, LPCTSTR *lppError, DWORD dwFlags, rtl_uString **corrected)
{
LPCTSTR lpszPath = reinterpret_cast< LPCTSTR >(path->buffer);
LPCTSTR lpComponent = lpszPath;
BOOL fValid = TRUE;
DWORD dwPathType = PATHTYPE_ERROR;
sal_Int32 nLength = rtl_uString_getLength( path );
if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
if ( !lpszPath )
fValid = FALSE;
DWORD dwCandidatPathType = PATHTYPE_ERROR;
if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, reinterpret_cast<const sal_Unicode *>(WSTR_LONG_PATH_PREFIX_UNC), ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX_UNC) - 1, ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX_UNC) - 1 ) )
{
/* This is long path in UNC notation */
lpComponent = lpszPath + ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX_UNC) - 1;
dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
}
else if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, reinterpret_cast<const sal_Unicode *>(WSTR_LONG_PATH_PREFIX), ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1, ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1 ) )
{
/* This is long path */
lpComponent = lpszPath + ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1;
if ( _istalpha( lpComponent[0] ) && ':' == lpComponent[1] )
{
lpComponent += 2;
dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
}
}
else if ( 2 == _tcsspn( lpszPath, CHARSET_SEPARATOR ) )
{
/* The UNC path notation */
lpComponent = lpszPath + 2;
dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
}
else if ( _istalpha( lpszPath[0] ) && ':' == lpszPath[1] )
{
/* Local path verification. Must start with <drive>: */
lpComponent = lpszPath + 2;
dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
}
if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
{
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 = dwCandidatPathType;
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;
}
}
}
}
else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
{
if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
lpComponent++;
else if ( *lpComponent )
fValid = FALSE;
dwPathType = dwCandidatPathType;
/* Now we are behind the backslash or it was a simple drive without backslash */
if ( fValid && !*lpComponent )
{
lpComponent = NULL;
dwPathType |= PATHTYPE_IS_VOLUME;
}
}
else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
{
/* Can be a relative path */
lpComponent = lpszPath;
/* Relative path can start with a backslash */
if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
{
lpComponent++;
if ( !*lpComponent )
lpComponent = NULL;
}
dwPathType = PATHTYPE_RELATIVE;
}
else
{
/* Anything else is an error */
fValid = FALSE;
lpComponent = lpszPath;
}
/* Now validate each component of the path */
while ( fValid && lpComponent )
{
// Correct path by merging consecutive slashes:
if (*lpComponent == '\\' && corrected != NULL) {
sal_Int32 i = lpComponent - lpszPath;
rtl_uString_newReplaceStrAt(corrected, path, i, 1, NULL);
//TODO: handle out-of-memory
lpszPath = reinterpret_cast< LPCTSTR >((*corrected)->buffer);
lpComponent = lpszPath + i;
}
fValid = IsValidFilePathComponent( lpComponent, &lpComponent, dwFlags );
if ( fValid && lpComponent )
{
lpComponent++;
/* If the string behind the backslash is empty, we've done */
if ( !*lpComponent )
lpComponent = NULL;
}
}
/* The path can be longer than MAX_PATH only in case it has the longpath prefix */
if ( fValid && !( dwPathType & PATHTYPE_IS_LONGPATH ) && _tcslen( lpszPath ) >= MAX_PATH )
{
fValid = FALSE;
lpComponent = lpszPath + MAX_PATH;
}
if ( lppError )
*lppError = lpComponent;
return fValid ? dwPathType : PATHTYPE_ERROR;
}
//#####################################################
static sal_Int32 PathRemoveFileSpec(LPTSTR lpPath, LPTSTR lpFileName, sal_Int32 nFileBufLen )
{
sal_Int32 nRemoved = 0;
if ( nFileBufLen )
{
lpFileName[0] = 0;
LPTSTR lpLastBkSlash = _tcsrchr( lpPath, '\\' );
LPTSTR lpLastSlash = _tcsrchr( lpPath, '/' );
LPTSTR lpLastDelimiter = lpLastSlash > lpLastBkSlash ? lpLastSlash : lpLastBkSlash;
if ( lpLastDelimiter )
{
sal_Int32 nDelLen = _tcslen( lpLastDelimiter );
if ( 1 == nDelLen )
{
if ( lpLastDelimiter > lpPath && *(lpLastDelimiter - 1) != ':' )
{
*lpLastDelimiter = 0;
*lpFileName = 0;
nRemoved = nDelLen;
}
}
else if ( nDelLen && nDelLen - 1 < nFileBufLen )
{
_tcscpy( lpFileName, lpLastDelimiter + 1 );
*(++lpLastDelimiter) = 0;
nRemoved = nDelLen - 1;
}
}
}
return nRemoved;
}
//#####################################################
// Undocumented in SHELL32.DLL ordinal 32
static LPTSTR PathAddBackslash(LPTSTR lpPath, sal_Int32 nBufLen)
{
LPTSTR lpEndPath = NULL;
if ( lpPath )
{
int nLen = _tcslen(lpPath);
if ( !nLen || lpPath[nLen-1] != '\\' && lpPath[nLen-1] != '/' && nLen < nBufLen - 1 )
{
lpEndPath = lpPath + nLen;
*lpEndPath++ = '\\';
*lpEndPath = 0;
}
}
return lpEndPath;
}
//#####################################################
// Same as GetLongPathName but also 95/NT4
static DWORD GetCaseCorrectPathNameEx(
LPTSTR lpszPath, // path buffer to convert
DWORD cchBuffer, // size of path buffer
DWORD nSkipLevels,
BOOL bCheckExistence )
{
::osl::LongPathBuffer< WCHAR > szFile( MAX_PATH + 1 );
sal_Int32 nRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
sal_Int32 nLastStepRemoved = nRemoved;
while ( nLastStepRemoved && szFile[0] == 0 )
{
// remove separators
nLastStepRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
nRemoved += nLastStepRemoved;
}
if ( nRemoved )
{
BOOL bSkipThis = FALSE;
if ( 0 == _tcscmp( szFile, TEXT("..") ) )
{
bSkipThis = TRUE;
nSkipLevels += 1;
}
else if ( 0 == _tcscmp( szFile, TEXT(".") ) )
{
bSkipThis = TRUE;
}
else if ( nSkipLevels )
{
bSkipThis = TRUE;
nSkipLevels--;
}
else
bSkipThis = FALSE;
GetCaseCorrectPathNameEx( lpszPath, cchBuffer, nSkipLevels, bCheckExistence );
PathAddBackslash( lpszPath, cchBuffer );
/* Analyze parent if not only a trailing backslash was cutted but a real file spec */
if ( !bSkipThis )
{
if ( bCheckExistence )
{
::osl::LongPathBuffer< WCHAR > aShortPath( MAX_LONG_PATH );
_tcscpy( aShortPath, lpszPath );
_tcscat( aShortPath, szFile );
WIN32_FIND_DATA aFindFileData;
HANDLE hFind = FindFirstFile( aShortPath, &aFindFileData );
if ( IsValidHandle(hFind) )
{
_tcscat( lpszPath, aFindFileData.cFileName[0] ? aFindFileData.cFileName : aFindFileData.cAlternateFileName );
FindClose( hFind );
}
else
lpszPath[0] = 0;
}
else
{
/* add the segment name back */
_tcscat( lpszPath, szFile );
}
}
}
else
{
/* File specification can't be removed therefore the short path is either a drive
or a network share. If still levels to skip are left, the path specification
tries to travel below the file system root */
if ( nSkipLevels )
lpszPath[0] = 0;
else
_tcsupr( lpszPath );
}
return _tcslen( lpszPath );
}
//#####################################################
#define WSTR_SYSTEM_ROOT_PATH L"\\\\.\\"
DWORD GetCaseCorrectPathName(
LPCTSTR lpszShortPath, // file name
LPTSTR lpszLongPath, // path buffer
DWORD cchBuffer, // size of path buffer
BOOL bCheckExistence
)
{
/* Special handling for "\\.\" as system root */
if ( lpszShortPath && 0 == wcscmp( lpszShortPath, WSTR_SYSTEM_ROOT_PATH ) )
{
if ( cchBuffer >= ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) )
{
wcscpy( lpszLongPath, WSTR_SYSTEM_ROOT_PATH );
return ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1;
}
else
{
return ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1;
}
}
else if ( lpszShortPath )
{
if ( _tcslen( lpszShortPath ) <= cchBuffer )
{
_tcscpy( lpszLongPath, lpszShortPath );
return GetCaseCorrectPathNameEx( lpszLongPath, cchBuffer, 0, bCheckExistence );
}
}
return 0;
}
//#############################################
static 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<sal_Char*>(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;
}
//#############################################
static 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 _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_shortenedCompareIgnoreAsciiCase_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_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\", 7 ) )
{
sal_uInt32 nSkip;
if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\\\", 8 ) )
nSkip = 8;
else if (
0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\localhost\\", 17 ) ||
0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\127.0.0.1\\", 17 )
)
nSkip = 17;
else
nSkip = 5;
/* Indicates local root */
if ( nDecodedLen == nSkip )
rtl_uString_newFromStr_WithLength( &strTempPath, reinterpret_cast<const sal_Unicode*>(WSTR_SYSTEM_ROOT_PATH), ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1 );
else
{
/* do not separate the directory and file case, so the maximal path lengs without prefix is MAX_PATH-12 */
if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
{
rtl_uString_newFromStr_WithLength( &strTempPath, pDecodedURL + nSkip, nDecodedLen - nSkip );
}
else
{
::osl::LongPathBuffer< sal_Unicode > aBuf( MAX_LONG_PATH );
sal_uInt32 nNewLen = GetCaseCorrectPathName( reinterpret_cast<LPCTSTR>(pDecodedURL + nSkip),
::osl::mingw_reinterpret_cast<LPTSTR>(aBuf),
aBuf.getBufSizeInSymbols(),
sal_False );
if ( nNewLen <= MAX_PATH - 12
|| 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, reinterpret_cast<const sal_Unicode*>(WSTR_SYSTEM_ROOT_PATH), ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1, ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1 )
|| 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX), ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1, ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1 ) )
{
rtl_uString_newFromStr_WithLength( &strTempPath, aBuf, nNewLen );
}
else if ( pDecodedURL[nSkip] == (sal_Unicode)'\\' && pDecodedURL[nSkip+1] == (sal_Unicode)'\\' )
{
/* it should be an UNC path, use the according prefix */
rtl_uString *strSuffix = NULL;
rtl_uString *strPrefix = NULL;
rtl_uString_newFromStr_WithLength( &strPrefix, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX_UNC), ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX_UNC ) - 1 );
rtl_uString_newFromStr_WithLength( &strSuffix, aBuf + 2, nNewLen - 2 );
rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
rtl_uString_release( strPrefix );
rtl_uString_release( strSuffix );
}
else
{
rtl_uString *strSuffix = NULL;
rtl_uString *strPrefix = NULL;
rtl_uString_newFromStr_WithLength( &strPrefix, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX), ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX ) - 1 );
rtl_uString_newFromStr_WithLength( &strSuffix, aBuf, nNewLen );
rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
rtl_uString_release( strPrefix );
rtl_uString_release( strSuffix );
}
}
}
if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
nError = osl_File_E_None;
}
else if ( bAllowRelative ) /* This maybe a relative file URL */
{
/* In future the relative path could be converted to absolute if it is too long */
rtl_uString_assign( &strTempPath, strDecodedURL );
if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
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;
}
//#############################################
oslFileError _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, NULL, VALIDATEPATH_ALLOW_RELATIVE, NULL);
if (dwPathType)
{
rtl_uString *strTempPath = NULL;
if ( dwPathType & PATHTYPE_IS_LONGPATH )
{
rtl_uString *strBuffer = NULL;
sal_uInt32 nIgnore = 0;
sal_uInt32 nLength = 0;
/* the path has the longpath prefix, lets remove it */
switch ( dwPathType & PATHTYPE_MASK_TYPE )
{
case PATHTYPE_ABSOLUTE_UNC:
nIgnore = ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX_UNC ) - 1;
OSL_ENSURE( nIgnore == 8, "Unexpected long path UNC prefix!" );
/* generate the normal UNC path */
nLength = rtl_uString_getLength( strPath );
rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore - 2, nLength - nIgnore + 2 );
strBuffer->buffer[0] = '\\';
rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
rtl_uString_release( strBuffer );
break;
case PATHTYPE_ABSOLUTE_LOCAL:
nIgnore = ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX ) - 1;
OSL_ENSURE( nIgnore == 4, "Unexpected long path prefix!" );
/* generate the normal path */
nLength = rtl_uString_getLength( strPath );
rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore, nLength - nIgnore );
rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
rtl_uString_release( strBuffer );
break;
default:
OSL_ASSERT( "Unexpected long path format!" );
rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
break;
}
}
else
{
/* 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* ustrPath, rtl_uString** pustrURL )
{
return _osl_getFileURLFromSystemPath( ustrPath, pustrURL );
}
//#####################################################
oslFileError SAL_CALL osl_getSystemPathFromFileURL(
rtl_uString *ustrURL, rtl_uString **pustrPath)
{
return _osl_getSystemPathFromFileURL( ustrURL, pustrPath, sal_True );
}
//#####################################################
oslFileError SAL_CALL osl_searchFileURL(
rtl_uString *ustrFileName,
rtl_uString *ustrSystemSearchPath,
rtl_uString **pustrPath)
{
rtl_uString *ustrUNCPath = NULL;
rtl_uString *ustrSysPath = NULL;
oslFileError error;
/* First try to interpret the file name as an URL even a relative one */
error = _osl_getSystemPathFromFileURL( ustrFileName, &ustrUNCPath, sal_True );
/* So far we either have an UNC path or something invalid
Now create a system path */
if ( osl_File_E_None == error )
error = _osl_getSystemPathFromFileURL( ustrUNCPath, &ustrSysPath, sal_True );
if ( osl_File_E_None == error )
{
DWORD nBufferLength;
DWORD dwResult;
LPTSTR lpBuffer = NULL;
LPTSTR lpszFilePart;
/* Repeat calling SearchPath ...
Start with MAX_PATH for the buffer. In most cases this
will be enough and does not force the loop to runtwice */
dwResult = MAX_PATH;
do
{
/* If search path is empty use a NULL pointer instead according to MSDN documentation of SearchPath */
LPCTSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? reinterpret_cast<LPCTSTR>(ustrSystemSearchPath->buffer) : NULL;
LPCTSTR lpszSearchFile = reinterpret_cast<LPCTSTR>(ustrSysPath->buffer);
/* Allocate space for buffer according to previous returned count of required chars */
/* +1 is not neccessary if we follow MSDN documentation but for robustness we do so */
nBufferLength = dwResult + 1;
lpBuffer = lpBuffer ?
reinterpret_cast<LPTSTR>(rtl_reallocateMemory(lpBuffer, nBufferLength * sizeof(TCHAR))) :
reinterpret_cast<LPTSTR>(rtl_allocateMemory(nBufferLength * sizeof(TCHAR)));
dwResult = SearchPath( lpszSearchPath, lpszSearchFile, NULL, nBufferLength, lpBuffer, &lpszFilePart );
} while ( dwResult && dwResult >= nBufferLength );
/* ... until an error occures or buffer is large enough.
dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
if ( dwResult )
{
rtl_uString_newFromStr( &ustrSysPath, reinterpret_cast<const sal_Unicode*>(lpBuffer) );
error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
}
else
{
WIN32_FIND_DATA aFindFileData;
HANDLE hFind;
/* Somthing went wrong, perhaps the path was absolute */
error = oslTranslateFileError( GetLastError() );
hFind = FindFirstFile( reinterpret_cast<LPCTSTR>(ustrSysPath->buffer), &aFindFileData );
if ( IsValidHandle(hFind) )
{
error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
FindClose( hFind );
}
}
rtl_freeMemory( lpBuffer );
}
if ( ustrSysPath )
rtl_uString_release( ustrSysPath );
if ( ustrUNCPath )
rtl_uString_release( ustrUNCPath );
return error;
}
//#####################################################
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 )
{
::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
::osl::LongPathBuffer< sal_Unicode > aCurrentDir( MAX_LONG_PATH );
LPTSTR lpFilePart = NULL;
DWORD dwResult;
/*@@@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 )
{
osl_acquireMutex( g_CurrentDirectoryMutex );
GetCurrentDirectoryW( aCurrentDir.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aCurrentDir) );
SetCurrentDirectoryW( reinterpret_cast<LPCWSTR>(ustrBaseSysPath->buffer) );
}
dwResult = GetFullPathNameW( reinterpret_cast<LPCWSTR>(ustrRelSysPath->buffer), aBuffer.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aBuffer), &lpFilePart );
if ( ustrBaseSysPath )
{
SetCurrentDirectoryW( ::osl::mingw_reinterpret_cast<LPCWSTR>(aCurrentDir) );
osl_releaseMutex( g_CurrentDirectoryMutex );
}
if ( dwResult )
{
if ( dwResult >= aBuffer.getBufSizeInSymbols() )
eError = osl_File_E_INVAL;
else
{
rtl_uString *ustrAbsSysPath = NULL;
rtl_uString_newFromStr( &ustrAbsSysPath, aBuffer );
eError = osl_getFileURLFromSystemPath( ustrAbsSysPath, pustrAbsoluteURL );
if ( ustrAbsSysPath )
rtl_uString_release( ustrAbsSysPath );
}
}
else
eError = oslTranslateFileError( GetLastError() );
}
if ( ustrBaseSysPath )
rtl_uString_release( ustrBaseSysPath );
if ( ustrRelSysPath )
rtl_uString_release( ustrRelSysPath );
return eError;
}
//#####################################################
oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
{
rtl_uString_newFromString(strValid, strRequested);
return osl_File_E_None;
}