You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1444 lines
38 KiB
1444 lines
38 KiB
// ========================================================================
|
|
// H T T P E X T \ U R L M A P . C P P
|
|
//
|
|
// Copyright Microsoft Corporation 1997-1999.
|
|
//
|
|
// This file contains all necessary routines to deal with IIS URLs
|
|
// properly. This file is part of HTTPEXT, as in HTTPEXT, we need to
|
|
// handle URLs the same way IIS would.
|
|
//
|
|
// ========================================================================
|
|
|
|
#include <_davfs.h>
|
|
#include <langtocpid.h>
|
|
|
|
//$ REVIEW: BUG:NT5:196814
|
|
//
|
|
// <string.hxx> is an IIS header file that exposes the CanonURL() api.
|
|
// It is exported from IISRTL.DLL and we should be able to call it
|
|
// instead of us stealing their code.
|
|
//
|
|
//$ HACK:
|
|
//
|
|
// <string.hxx> includes <buffer.hxx> which includes <nt.h> and all of
|
|
// its minions. DAV has already included all of the <winnt.h> and its
|
|
// minions. The <nt.h> and <winnt.h> are at odds, so we are defining
|
|
// NT_INCLUDED, _NTRTL_, _NTURTL_, DBG_ASSERT(), IntializeListHead(),
|
|
// and RemoveEntryList() to disable those conflicts.
|
|
//
|
|
#define NT_INCLUDED
|
|
#define _NTRTL_
|
|
#define _NTURTL_
|
|
#define InitializeListHead(_p)
|
|
#define RemoveEntryList(_p)
|
|
#define DBG_ASSERT Assert
|
|
#pragma warning (disable:4390)
|
|
#include <string.hxx>
|
|
#pragma warning (default:4390)
|
|
|
|
//
|
|
//$ HACK: end
|
|
//$ REVIEW: end
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
enum {
|
|
|
|
ACTION_NOTHING = 0x00000000,
|
|
ACTION_EMIT_CH = 0x00010000,
|
|
ACTION_EMIT_DOT_CH = 0x00020000,
|
|
ACTION_EMIT_DOT_DOT_CH = 0x00030000,
|
|
ACTION_BACKUP = 0x00040000,
|
|
ACTION_MASK = 0xFFFF0000
|
|
|
|
};
|
|
|
|
// States and State translations ---------------------------------------------
|
|
//
|
|
const UINT gc_rguStateTable[16] = {
|
|
|
|
// State 0
|
|
//
|
|
0 , // other
|
|
0 , // "."
|
|
4 , // EOS
|
|
1 , // "\"
|
|
|
|
// State 1
|
|
//
|
|
0 , // other
|
|
2 , // "."
|
|
4 , // EOS
|
|
1 , // "\"
|
|
|
|
// State 2
|
|
//
|
|
0 , // other
|
|
3 , // "."
|
|
4 , // EOS
|
|
1 , // "\"
|
|
|
|
// State 3
|
|
//
|
|
0 , // other
|
|
0 , // "."
|
|
4 , // EOS
|
|
1 // "\"
|
|
};
|
|
|
|
const UINT gc_rguActionTable[16] = {
|
|
|
|
// State 0
|
|
//
|
|
ACTION_EMIT_CH, // other
|
|
ACTION_EMIT_CH, // "."
|
|
ACTION_EMIT_CH, // EOS
|
|
ACTION_EMIT_CH, // "\"
|
|
|
|
// State 1
|
|
//
|
|
ACTION_EMIT_CH, // other
|
|
ACTION_NOTHING, // "."
|
|
ACTION_EMIT_CH, // EOS
|
|
ACTION_NOTHING, // "\"
|
|
|
|
// State 2
|
|
//
|
|
ACTION_EMIT_DOT_CH, // other
|
|
ACTION_NOTHING, // "."
|
|
ACTION_EMIT_CH, // EOS
|
|
ACTION_NOTHING, // "\"
|
|
|
|
// State 3
|
|
//
|
|
ACTION_EMIT_DOT_DOT_CH, // other
|
|
ACTION_EMIT_DOT_DOT_CH, // "."
|
|
ACTION_BACKUP, // EOS
|
|
ACTION_BACKUP // "\"
|
|
};
|
|
|
|
// The following table provides the index for various ISA Latin1 characters
|
|
// in the incoming URL.
|
|
//
|
|
// It assumes that the URL is ISO Latin1 == ASCII
|
|
//
|
|
const UINT gc_rguIndexForChar[] = {
|
|
|
|
2, // null char
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 thru 10
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 11 thru 20
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21 thru 30
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 31 thru 40
|
|
0, 0, 0, 0, 0, 1, 3, 0, 0, 0, // 41 thru 50 46 = '.' 47 = '/'
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 51 thru 60
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 61 thru 70
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 71 thru 80
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 81 thru 90
|
|
0, 3, 0, 0, 0, 0, 0, 0, 0, 0, // 91 thru 100 92 = '\\'
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 101 thru 110
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 111 thru 120
|
|
0, 0, 0, 0, 0, 0, 0, 0 // 121 thru 128
|
|
};
|
|
|
|
// FIsUTF8TrailingByte -------------------------------------------------------
|
|
//
|
|
// Function returns TRUE if the given character is UTF-8 trailing byte
|
|
//
|
|
inline BOOL FIsUTF8TrailingByte (CHAR ch)
|
|
{
|
|
return (0x80 == (ch & 0xc0));
|
|
}
|
|
|
|
// FIsUTF8Url ----------------------------------------------------------------
|
|
//
|
|
// Function returns TRUE if the given string can be treated as UTF-8
|
|
//
|
|
BOOL __fastcall
|
|
FIsUTF8Url (/* [in] */ LPCSTR pszUrl)
|
|
{
|
|
CHAR ch;
|
|
|
|
while (0 != (ch = *pszUrl++))
|
|
{
|
|
// Sniff for a lead-byte
|
|
//
|
|
if (ch & 0x80)
|
|
{
|
|
CHAR chT1;
|
|
CHAR chT2;
|
|
|
|
// Pick off the trailing bytes
|
|
//
|
|
chT1 = *pszUrl++;
|
|
if (chT1)
|
|
chT2 = *pszUrl;
|
|
else
|
|
chT2 = 0;
|
|
|
|
// Handle the three byte case
|
|
// 1110xxxx 10xxxxxx 10xxxxxx
|
|
//
|
|
if (((ch & 0xF0) == 0xE0) &&
|
|
FIsUTF8TrailingByte (chT1) &&
|
|
FIsUTF8TrailingByte (chT2))
|
|
{
|
|
// We found a UTF-8 character. Keep going.
|
|
//
|
|
pszUrl++;
|
|
continue;
|
|
}
|
|
// Also watch for the two byte case
|
|
// 110xxxxx 10xxxxxx
|
|
//
|
|
else if (((ch & 0xE0) == 0xC0) && FIsUTF8TrailingByte (chT1))
|
|
{
|
|
// We found a UTF-8 character. Keep going.
|
|
//
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// If we had a lead-byte but no UTF trailing bytes, then
|
|
// this cannot be a UTF8 url.
|
|
//
|
|
DebugTrace ("FIsUTF8Url(): url contains UTF8 lead byte with no trailing\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hey, we made it through without any non-singlebyte chars, so we can
|
|
// operate as if this is a UTF8 url.
|
|
//
|
|
DebugTrace ("FIsUTF8Url(): url contains only UTF8 characters\n");
|
|
return TRUE;
|
|
}
|
|
|
|
// ScCanonicalizeURL ---------------------------------------------------------
|
|
//
|
|
// Wide version of the CanonURL() function, which lives in iisrtl.lib
|
|
//
|
|
// PURPOSE: Sanitizes a path by removing bogus path elements.
|
|
//
|
|
// As expected, "/./" entries are simply removed, and
|
|
// "/../" entries are removed along with the previous
|
|
// path element.
|
|
//
|
|
// To maintain compatibility with URL path semantics
|
|
// additional transformations are required. All backward
|
|
// slashes "\\" are converted to forward slashes. Any
|
|
// repeated forward slashes (such as "///") are mapped to
|
|
// single backslashes.
|
|
//
|
|
// A state table (see the p_StateTable global at the
|
|
// beginning of this file) is used to perform most of
|
|
// the transformations. The table's rows are indexed
|
|
// by current state, and the columns are indexed by
|
|
// the current character's "class" (either slash, dot,
|
|
// NULL, or other). Each entry in the table consists
|
|
// of the new state tagged with an action to perform.
|
|
// See the ACTION_* constants for the valid action
|
|
// codes.
|
|
//
|
|
// PARAMETERS:
|
|
//
|
|
// pwszSrc - url to canonicalize
|
|
// pwszDest - buffer to fill
|
|
// pcch - number of characters written into the buffer
|
|
// (which includes '\0' termination)
|
|
//
|
|
// RETURN CODES:
|
|
//
|
|
// S_OK.
|
|
//
|
|
// NOTE: This function assumes that destination buffer is
|
|
// equal or biger than the source.
|
|
//
|
|
SCODE __fastcall
|
|
ScCanonicalizeURL( /* [in] */ LPCWSTR pwszSrc,
|
|
/* [in/out] */ LPWSTR pwszDest,
|
|
/* [out] */ UINT * pcch )
|
|
{
|
|
LPCWSTR pwszPath;
|
|
UINT uiCh;
|
|
UINT uiIndex = 0; // State = 0
|
|
|
|
Assert( pwszSrc );
|
|
Assert( pwszDest );
|
|
Assert( pcch );
|
|
|
|
// Zero out return
|
|
//
|
|
*pcch = 0;
|
|
|
|
// Remember start of the buffer into which we will canonicalize
|
|
//
|
|
pwszPath = pwszDest;
|
|
|
|
// Loop until we enter state 4 (the final, accepting state).
|
|
//
|
|
do {
|
|
|
|
// Grab the next character from the path and compute its
|
|
// next state. While we're at it, map any forward
|
|
// slashes to backward slashes.
|
|
//
|
|
uiIndex = gc_rguStateTable[uiIndex] * 4; // 4 = # states
|
|
uiCh = *pwszSrc++;
|
|
|
|
uiIndex += ((uiCh >= 0x80) ? 0 : gc_rguIndexForChar[uiCh]);
|
|
|
|
// Perform the action associated with the state.
|
|
//
|
|
switch( gc_rguActionTable[uiIndex] )
|
|
{
|
|
case ACTION_EMIT_DOT_DOT_CH :
|
|
|
|
*pwszDest++ = L'.';
|
|
|
|
/* fall through */
|
|
|
|
case ACTION_EMIT_DOT_CH :
|
|
|
|
*pwszDest++ = L'.';
|
|
|
|
/* fall through */
|
|
|
|
case ACTION_EMIT_CH :
|
|
|
|
*pwszDest++ = static_cast<WCHAR>(uiCh);
|
|
|
|
/* fall through */
|
|
|
|
case ACTION_NOTHING :
|
|
|
|
break;
|
|
|
|
case ACTION_BACKUP :
|
|
if ( (pwszDest > (pwszPath + 1) ) && (*pwszPath == L'/'))
|
|
{
|
|
pwszDest--;
|
|
Assert( *pwszDest == L'/' );
|
|
|
|
*pwszDest = L'\0';
|
|
pwszDest = wcsrchr( pwszPath, L'/') + 1;
|
|
}
|
|
|
|
*pwszDest = L'\0';
|
|
break;
|
|
|
|
default :
|
|
|
|
TrapSz("Invalid action code in state table!");
|
|
uiIndex = 2; // move to invalid state
|
|
Assert( 4 == gc_rguStateTable[uiIndex] );
|
|
*pwszDest++ = L'\0';
|
|
break;
|
|
}
|
|
|
|
} while( gc_rguStateTable[uiIndex] != 4 );
|
|
|
|
// Point to terminating nul
|
|
//
|
|
if (ACTION_EMIT_CH == gc_rguActionTable[uiIndex])
|
|
{
|
|
pwszDest--;
|
|
}
|
|
|
|
Assert((L'\0' == *pwszDest) && (pwszDest >= pwszPath));
|
|
|
|
// Return number of characters written
|
|
//
|
|
*pcch = static_cast<UINT>(pwszDest - pwszPath + 1);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
SCODE __fastcall
|
|
ScCanonicalizePrefixedURL( /* [in] */ LPCWSTR pwszSrc,
|
|
/* [in] */ LPWSTR pwszDest,
|
|
/* [out] */ UINT * pcch )
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
LPCWSTR pwszStripped;
|
|
UINT cchStripped;
|
|
UINT cch = 0;
|
|
|
|
Assert(pwszSrc);
|
|
Assert(pwszDest);
|
|
Assert(pcch);
|
|
|
|
// Zero out return
|
|
//
|
|
*pcch = 0;
|
|
|
|
pwszStripped = PwszUrlStrippedOfPrefix(pwszSrc);
|
|
cchStripped = static_cast<UINT>(pwszStripped - pwszSrc);
|
|
|
|
// Copy the prefix over to the destination. I do not use
|
|
// memcpy here as source and destination may overlap,
|
|
// and in such case those functions are not recomended.
|
|
//
|
|
for (UINT ui = 0; ui < cchStripped; ui++)
|
|
{
|
|
pwszDest[ui] = pwszSrc[ui];
|
|
}
|
|
|
|
// Canonicalize the remainder of te URL
|
|
//
|
|
sc = ScCanonicalizeURL(pwszStripped,
|
|
pwszDest + cchStripped,
|
|
&cch);
|
|
if (S_OK != sc)
|
|
{
|
|
Assert(S_FALSE != sc);
|
|
DebugTrace("ScCanonicalizePrefixedURL() - ScCanonicalizeUrl() failed 0x%08lX\n", sc);
|
|
goto ret;
|
|
}
|
|
|
|
// Return the number of characters written
|
|
//
|
|
*pcch = cchStripped + cch;
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
// ScConvertToWide -----------------------------------------------------------
|
|
//
|
|
SCODE __fastcall
|
|
ScConvertToWide(/* [in] */ LPCSTR pszSource,
|
|
/* [in/out] */ UINT * pcchDest,
|
|
/* [out] */ LPWSTR pwszDest,
|
|
/* [in] */ LPCSTR pszAcceptLang,
|
|
/* [in] */ BOOL fUrlConversion)
|
|
{
|
|
SCODE sc = S_OK;
|
|
CStackBuffer<CHAR, MAX_PATH> pszToConvert;
|
|
UINT cpid = CP_UTF8;
|
|
UINT cb;
|
|
UINT cch;
|
|
|
|
Assert(pszSource);
|
|
Assert(pcchDest);
|
|
Assert(pwszDest);
|
|
|
|
if (fUrlConversion)
|
|
{
|
|
// Allocate the space to escape URL into.
|
|
//
|
|
cb = static_cast<UINT>(strlen(pszSource));
|
|
if (NULL == pszToConvert.resize(cb + 1))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
DebugTrace("ScConvertToWide() - Error while allocating memory 0x%08lX\n", sc);
|
|
goto ret;
|
|
}
|
|
|
|
// Unescape to the new buffer. Unescaping can only shrink the size,
|
|
// so we have enough buffer allocated.
|
|
//
|
|
HttpUriUnescape(pszSource, pszToConvert.get());
|
|
|
|
// Perform a quick pass over the url looking for non-UTF8 characters.
|
|
// Remember if we need to continue to scan for UTF8 characters.
|
|
//
|
|
if (!FIsUTF8Url(pszToConvert.get()))
|
|
{
|
|
// ... cannot do CP_UTF8, assume CP_ACP.
|
|
//
|
|
cpid = CP_ACP;
|
|
}
|
|
|
|
// If the URL cannot be treated as UTF8 then find out the code page for it
|
|
//
|
|
if (CP_UTF8 != cpid)
|
|
{
|
|
if (pszAcceptLang)
|
|
{
|
|
HDRITER hdri(pszAcceptLang);
|
|
LPCSTR psz;
|
|
|
|
// Let us try guessing the cpid from the language string
|
|
// Try all the languages in the header. We stop at the
|
|
// first language for which we have a cpid mapping. If
|
|
// none of the languages specified in the header have cpid
|
|
// mappings, then we will end up with the default cpid
|
|
// CP_ACP
|
|
//
|
|
for (psz = hdri.PszNext(); psz; psz = hdri.PszNext())
|
|
{
|
|
if (CLangToCpidCache::FFindCpid(psz, &cpid))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Swap the pointer and recalculate the size
|
|
//
|
|
pszSource = pszToConvert.get();
|
|
}
|
|
|
|
// Find out the length of the string we will convert
|
|
//
|
|
cb = static_cast<UINT>(strlen(pszSource));
|
|
|
|
// Translate to unicode including '\0' termination
|
|
//
|
|
cch = MultiByteToWideChar(cpid,
|
|
(CP_UTF8 != cpid) ? MB_ERR_INVALID_CHARS : 0,
|
|
pszSource,
|
|
cb + 1,
|
|
pwszDest,
|
|
*pcchDest);
|
|
if (0 == cch)
|
|
{
|
|
// If buffer was not sufficient
|
|
//
|
|
if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
|
|
{
|
|
// Find out the size needed
|
|
//
|
|
cch = MultiByteToWideChar(cpid,
|
|
(CP_UTF8 != cpid) ? MB_ERR_INVALID_CHARS : 0,
|
|
pszSource,
|
|
cb + 1,
|
|
NULL,
|
|
0);
|
|
if (0 == cch)
|
|
{
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
DebugTrace("ScConvertToWide() - MultiByteToWideChar() failed to fetch size 0x%08lX - CPID: %d\n", sc, cpid);
|
|
goto ret;
|
|
}
|
|
|
|
// Return the size and warning back
|
|
//
|
|
*pcchDest = cch;
|
|
sc = S_FALSE;
|
|
goto ret;
|
|
}
|
|
else
|
|
{
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
DebugTrace("ScConvertToWide() - MultiByteToWideChar() failed 0x%08lX - CPID: %d\n", sc, cpid);
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
*pcchDest = cch;
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
// ScNormalizeUrl ------------------------------------------------------------
|
|
//
|
|
// PURPOSE: Normalization of a url.
|
|
//
|
|
// Has two components to the operation:
|
|
//
|
|
// 1) All sequences of %xx are replaced by a single character that
|
|
// has a value that is equal to the hex representation of the
|
|
// following two characters.
|
|
//
|
|
// 2) All path modification sequences are stripped out and the url
|
|
// is adjusted accordingly. The set of path modification sequences
|
|
// that we recognize are as follows:
|
|
//
|
|
// "//" reduces to "/"
|
|
// "/./" reduces to "/"
|
|
// "/../" strips off the last path segment
|
|
//
|
|
// It is important to note that the unescaping happens first!
|
|
//
|
|
// NOTE: this function does NOT currently normalize the path separators
|
|
// All '\' are NOT replaced with '/' in this function or vice versa.
|
|
// The code is implemented such that slashes replaced due to a double
|
|
// slash such as "//", "\\", "\/", or "/\" are defaulted to forward
|
|
// slashes '/'
|
|
//
|
|
// A state table (see the gc_rguStateTable global at the beginning
|
|
// of this file) is used to perform most of the transformations. The
|
|
// table's rows are indexed by current state, and the columns are indexed
|
|
// by the current character's "class" (either slash, dot, NULL, or other).
|
|
// Each entry in the table consists of the new state tagged with an action
|
|
// to perform. See the ACTION_* constants for the valid action codes.//
|
|
//
|
|
// PARAMETERS:
|
|
//
|
|
// pwszSourceUrl -- the URL to be normalized
|
|
// pcchNormalizedUrl -- the amount of characters available in buffer
|
|
// pointed by pwszNormalizedUrl
|
|
// pwszNormalizedUrl -- the place to put the normalized URL
|
|
//
|
|
// RETURN CODES:
|
|
//
|
|
// S_OK: Everything went well, the URL was normalized into pwszNormalizedUrl.
|
|
// S_FALSE: Buffer was not sufficient. Required size is in *pcchNormalizedUrl.
|
|
// E_OUTOFMEMORY: Memory alocation failure
|
|
// ...other errors that we could get from the conversion routines
|
|
//
|
|
SCODE __fastcall
|
|
ScNormalizeUrl (
|
|
/* [in] */ LPCWSTR pwszSourceUrl,
|
|
/* [in/out] */ UINT * pcchNormalizedUrl,
|
|
/* [out] */ LPWSTR pwszNormalizedUrl,
|
|
/* [in] */ LPCSTR pszAcceptLang)
|
|
{
|
|
SCODE sc = S_OK;
|
|
CStackBuffer<CHAR, MAX_PATH> pszSourceUrl;
|
|
UINT cchSourceUrl;
|
|
UINT cbSourceUrl;
|
|
|
|
Assert(pwszSourceUrl);
|
|
Assert(pcchNormalizedUrl);
|
|
Assert(pwszNormalizedUrl);
|
|
|
|
// We are given the wide version of the URL, so someone who
|
|
// converted it already should have done that correctly. So
|
|
// we will convert it to CP_UTF8
|
|
//
|
|
cchSourceUrl = static_cast<UINT>(wcslen(pwszSourceUrl));
|
|
cbSourceUrl = cchSourceUrl * 3;
|
|
if (NULL == pszSourceUrl.resize(cbSourceUrl + 1))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
DebugTrace("ScNormalizeUrl() - Error while allocating memory 0x%08lX\n", sc);
|
|
goto ret;
|
|
}
|
|
|
|
cbSourceUrl = WideCharToMultiByte(CP_UTF8,
|
|
0,
|
|
pwszSourceUrl,
|
|
cchSourceUrl + 1,
|
|
pszSourceUrl.get(),
|
|
cbSourceUrl + 1,
|
|
NULL,
|
|
NULL);
|
|
if (0 == cbSourceUrl)
|
|
{
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
DebugTrace("ScNormalizeUrl() - WideCharToMultiByte() failed 0x%08lX\n", sc);
|
|
goto ret;
|
|
}
|
|
|
|
sc = ScNormalizeUrl(pszSourceUrl.get(),
|
|
pcchNormalizedUrl,
|
|
pwszNormalizedUrl,
|
|
pszAcceptLang);
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace("ScNormalizeUrl() - ScNormalizeUrl() failed 0x%08lX\n", sc);
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
SCODE __fastcall
|
|
ScNormalizeUrl (
|
|
/* [in] */ LPCSTR pszSourceUrl,
|
|
/* [in/out] */ UINT * pcchNormalizedUrl,
|
|
/* [out] */ LPWSTR pwszNormalizedUrl,
|
|
/* [in] */ LPCSTR pszAcceptLang)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
Assert(pszSourceUrl);
|
|
Assert(pcchNormalizedUrl);
|
|
Assert(pwszNormalizedUrl);
|
|
|
|
// Convert the URL to UNICODE into the given buffer.
|
|
// Function may return S_FALSE, so make sure we
|
|
// check the return code correctly - against S_OK
|
|
//
|
|
sc = ScConvertToWide(pszSourceUrl,
|
|
pcchNormalizedUrl,
|
|
pwszNormalizedUrl,
|
|
pszAcceptLang,
|
|
TRUE);
|
|
if (S_OK != sc)
|
|
{
|
|
DebugTrace("ScNormalizeUrl() - ScConvertToWide() returned 0x%08lX\n", sc);
|
|
goto ret;
|
|
}
|
|
|
|
// Canonicalize in place, take into account that URL may be fully
|
|
// qualified.
|
|
//
|
|
sc = ScCanonicalizePrefixedURL(pwszNormalizedUrl,
|
|
pwszNormalizedUrl,
|
|
pcchNormalizedUrl);
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace("ScNormalizeUrl() - ScCanonicalizePrefixedURL() failed 0x%08lX\n", sc);
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
|
|
return sc;
|
|
}
|
|
|
|
// ScStoragePathFromUrl ------------------------------------------------------
|
|
//
|
|
// PURPOSE: Url to storage path translation.
|
|
//
|
|
SCODE __fastcall
|
|
ScStoragePathFromUrl (
|
|
/* [in] */ const IEcb & ecb,
|
|
/* [in] */ LPCWSTR pwszUrl,
|
|
/* [out] */ LPWSTR wszStgID,
|
|
/* [in/out] */ UINT * pcch,
|
|
/* [out] */ CVRoot ** ppcvr)
|
|
{
|
|
Assert (pwszUrl);
|
|
Assert (wszStgID);
|
|
Assert (pcch);
|
|
|
|
SCODE sc = S_OK;
|
|
HSE_UNICODE_URL_MAPEX_INFO mi;
|
|
LPCWSTR pwszVRoot;
|
|
UINT cchVRoot;
|
|
UINT cch = 0;
|
|
UINT cchUrl = 0;
|
|
|
|
#undef ALLOW_RELATIVE_URL_TRANSLATION
|
|
#ifdef ALLOW_RELATIVE_URL_TRANSLATION
|
|
|
|
CStackBuffer<WCHAR,256> pwszNew;
|
|
|
|
#endif // ALLOW_RELATIVE_URL_TRANSLATION
|
|
|
|
// Lets make sure this funcion is never called with a
|
|
// prefixed url.
|
|
//
|
|
sc = ScStripAndCheckHttpPrefix (ecb, &pwszUrl);
|
|
if (FAILED (sc))
|
|
return sc;
|
|
|
|
// Make sure that the url is absolute
|
|
//
|
|
if (L'/' != *pwszUrl)
|
|
{
|
|
|
|
#ifdef ALLOW_RELATIVE_URL_TRANSLATION
|
|
|
|
//$ REVIEW:
|
|
//
|
|
// This code is here should we ever decide we need
|
|
// to support relative url processing.
|
|
//
|
|
// Construct an absolute url from the relative one
|
|
//
|
|
UINT cchRequestUrl = wcslen(ecb.LpwszRequestUrl());
|
|
UINT cchUrl = static_cast<UINT>(wcslen(pwszUrl));
|
|
|
|
if (NULL == pwszNew.resize(CbSizeWsz(cchRequestUrl + cchUrl)))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
DebugTrace("ScStoragePathFromUrl() - CStackBuffer::resize() failed 0x%08lX\n", sc);
|
|
return sc;
|
|
}
|
|
|
|
memcpy (pwszNew.get(), ecb.LpwszRequestUrl(), cchRequestUrl * sizeof(WCHAR));
|
|
memcpy (pwszNew.get(), pwszUrl, (cchUrl + 1) * sizeof(WCHAR));
|
|
|
|
// Now pszURI points to the generated absolute URI
|
|
//
|
|
pwszUrl = pwszNew.get();
|
|
//
|
|
//$ REVIEW: end
|
|
|
|
#else
|
|
|
|
DebugTrace ("ScStoragePathFromUrl(): cannot translate relative URIs\n");
|
|
return E_DAV_BAD_DESTINATION;
|
|
|
|
#endif // ALLOW_RELATIVE_URL_TRANSLATION
|
|
}
|
|
|
|
// OK, here is where virtual root spanning needs to be supported...
|
|
//
|
|
// When the virtual root of the request url does not match the
|
|
// the virtual root for the url being translated, extra work
|
|
// needs to be done.
|
|
//
|
|
// There are two ways to do this.
|
|
//
|
|
// 1) Call back to IIS and have it do the translation for us
|
|
// 2) Use our metabase cache to rip through each virtual root
|
|
// and find the longest matching virtual root.
|
|
//
|
|
// At first thought, the second method seems efficient. However,
|
|
// the changes being made to the metabase cache do not make this
|
|
// an easy matter. The cache no longer will be containing just
|
|
// virtual roots, so the lookup will not be as cheap.
|
|
//
|
|
//$ REVIEW: In fact, I believe that we must do the virtual lookup
|
|
// via IIS for all translations. The sub-virtual root thing keeps
|
|
// gnawing at me.
|
|
//
|
|
cchUrl = static_cast<UINT>(wcslen(pwszUrl));
|
|
sc = ecb.ScReqMapUrlToPathEx(pwszUrl, &mi);
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace("ScStoragePathFromUrl() - IEcb::SSFReqMapUrlPathEx() failed 0x%08lX\n", sc);
|
|
return sc;
|
|
}
|
|
|
|
// Try and figure out if the url spanned a virtual root at all.
|
|
//
|
|
cchVRoot = ecb.CchGetVirtualRootW(&pwszVRoot);
|
|
if (cchVRoot != mi.cchMatchingURL)
|
|
{
|
|
// This case is not so cut-n-dry..
|
|
//
|
|
// Since CchGetVirtualRoot() should always return a url
|
|
// that does not have a trailing slash, the matching count
|
|
// could be off by one and the root may actually be the
|
|
// same!
|
|
//
|
|
// Assuming "/vroot" is the Virtual Root in question, this if
|
|
// statement protects against the following:
|
|
// 1. catches a two completely different sized vroots.
|
|
// disqualifies matches that are too short or
|
|
// too long "/vr", but allows "/vroot/" because need to
|
|
// handle IIS bug (NT:432359).
|
|
// 2. checks to make sure the URL is slash terminated. This
|
|
// allows "/vroot/" (again because of NT:432359), but
|
|
// disqualifies vroots such as "/vrootA"
|
|
// 3. allows "/vroot" to pass if mi.cchMatchingURL is off by
|
|
// one (again because of NT:432359).
|
|
//
|
|
if ((cchVRoot + 1 != mi.cchMatchingURL) || // 1
|
|
((L'/' != pwszUrl[cchVRoot]) && // 2
|
|
(L'\0' != pwszUrl[cchVRoot]))) // 3
|
|
{
|
|
// If we're here the virtual root of the URL does not match
|
|
// the current virtual root...
|
|
//
|
|
DebugTrace ("ScStoragePathFromUrl() - urls do not "
|
|
"share common virtual root\n"
|
|
"-- pwszUrl: %ls\n"
|
|
"-- pwszVirtualRoot: %ls\n"
|
|
"-- cchVirtualRoot: %ld\n",
|
|
pwszUrl,
|
|
pwszVRoot,
|
|
cchVRoot);
|
|
|
|
// Tell the caller that the virtual root is spanned. This allows
|
|
// the call to succeed, but the caller to fail the call if spanning
|
|
// is not allowed.
|
|
//
|
|
sc = W_DAV_SPANS_VIRTUAL_ROOTS;
|
|
}
|
|
else
|
|
{
|
|
// If we're here we know that the current virtual root matches
|
|
// the virtual root of the URL, and the following character in
|
|
// the URL is a slash or a NULL termination. cchMatchingURL is
|
|
// EXACTLY 1 greater than the number of characters in the virtual
|
|
// root (cchVRoot) due to the IIS bug (NT:432359).
|
|
//
|
|
// Theoretically, if cchMatchingURL matches and matches
|
|
// one more than the number of characters in the
|
|
// vroot, the characters will match! Thus we should assert this case.
|
|
//
|
|
Assert (!_wcsnicmp(pwszVRoot, pwszUrl, cchVRoot));
|
|
|
|
// In this case, mi.cchMatchingURL actually _includes_ the
|
|
// slash. Below, when we copy in the trailing part of the
|
|
// URL, we skip mi.cchMatchingURL characters in the URL
|
|
// before copying in the trailing URL. This has the
|
|
// unfortunate side effect in this case of missing the
|
|
// slash that is at the beginning of the URL after the
|
|
// virtual root, so you could end up with a path that looks
|
|
// like:
|
|
// \\.\BackOfficeStorage\mydom.extest.microsoft.com\MBXuser1/Inbox
|
|
// rather than:
|
|
// \\.\BackOfficeStorage\mydom.extest.microsoft.com\MBX/user1/Inbox
|
|
//
|
|
// So decrement miw.cchMatchingURL here to handle this.
|
|
//
|
|
DebugTrace ("ScStoragePathFromUrl() : mi.cchMatchingURL included a slash!\n");
|
|
mi.cchMatchingURL--;
|
|
}
|
|
}
|
|
// If we are hitting this conditional if statement, we know that
|
|
// the mi.cchMatchingURL is the same as the number of characters
|
|
// in the vroot.
|
|
// 1. We already checked for difference in the vroot lengts above
|
|
// and if length of the vroot was 0 then they actually matched
|
|
// 2. We know that due to an IIS bug (NT:432359), cchMatchingURL
|
|
// could be 1 character too long. This lines checks for that
|
|
// case. If that is the case, we know that the VRoot is one
|
|
// character longer than the virtual root of the URL -- ie
|
|
// we are spanning virtual roots.
|
|
// 3. If the strings aren't in fact the same then we know that
|
|
// cchMatchingURL matched to a different virtual root than
|
|
// pszVRoot.
|
|
//
|
|
else if ((0 != cchVRoot) && // 1
|
|
((L'\0' == pwszUrl[cchVRoot - 1]) || // 2
|
|
_wcsnicmp(pwszVRoot, pwszUrl, cchVRoot))) // 3
|
|
{
|
|
DebugTrace ("ScStoragePathFromUrl(): urls do not "
|
|
"share common virtual root\n"
|
|
"-- pwszUrl: %ls\n"
|
|
"-- pwszVirtualRoot: %ls\n"
|
|
"-- cchVirtualRoot: %ld\n",
|
|
pwszUrl,
|
|
pwszVRoot,
|
|
cchVRoot);
|
|
|
|
// Tell the caller that the virtual root is spanned. This allows
|
|
// the call to succeed, but the caller to fail the call if spanning
|
|
// is not allowed.
|
|
//
|
|
sc = W_DAV_SPANS_VIRTUAL_ROOTS;
|
|
}
|
|
|
|
// If we span, and the caller wants it, look up the vroot
|
|
// for them.
|
|
//
|
|
if ((W_DAV_SPANS_VIRTUAL_ROOTS == sc) && ppcvr)
|
|
{
|
|
auto_ref_ptr<CVRoot> arp;
|
|
CStackBuffer<WCHAR, MAX_PATH> pwsz;
|
|
CStackBuffer<WCHAR, MAX_PATH> pwszMetaPath;
|
|
if (NULL == pwsz.resize((mi.cchMatchingURL + 1) * sizeof(WCHAR)))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
DebugTrace("ScStoragePathFromUrl() - CStackBuffer::resize() failed 0x%08lX\n", sc);
|
|
return sc;
|
|
}
|
|
|
|
memcpy(pwsz.get(), pwszUrl, mi.cchMatchingURL * sizeof(WCHAR));
|
|
pwsz[mi.cchMatchingURL] = L'\0';
|
|
if (NULL == pwszMetaPath.resize(::CbMDPathW(ecb, pwsz.get())))
|
|
{
|
|
sc = E_OUTOFMEMORY;
|
|
DebugTrace("ScStoragePathFromUrl() - CStackBuffer::resize() failed 0x%08lX\n", sc);
|
|
return sc;
|
|
}
|
|
|
|
MDPathFromURIW (ecb, pwsz.get(), pwszMetaPath.get());
|
|
_wcslwr (pwszMetaPath.get());
|
|
|
|
// Find the vroot
|
|
//
|
|
if (!CChildVRCache::FFindVroot (ecb, pwszMetaPath.get(), arp))
|
|
{
|
|
DebugTrace ("ScStoragePathFromUrl(): spanned virtual root not available\n");
|
|
return E_DAV_BAD_DESTINATION;
|
|
}
|
|
|
|
*ppcvr = arp.relinquish();
|
|
}
|
|
|
|
// Adjust the matching path the same way as we did matching URL
|
|
//
|
|
if ( mi.cchMatchingPath )
|
|
{
|
|
LPCWSTR pwsz = mi.lpszPath + mi.cchMatchingPath - 1;
|
|
|
|
if ( L'\\' == *pwsz )
|
|
{
|
|
while ((0 < mi.cchMatchingPath) &&
|
|
(L'\\' == *pwsz) &&
|
|
(!FIsDriveTrailingChar(pwsz, mi.cchMatchingPath)))
|
|
{
|
|
mi.cchMatchingPath--;
|
|
pwsz--;
|
|
}
|
|
}
|
|
else if ( L'\0' == *pwsz )
|
|
{
|
|
mi.cchMatchingPath--;
|
|
}
|
|
}
|
|
|
|
// If there is not enough space in the buffer provided, a return
|
|
// of S_FALSE tells the caller to realloc and try again!
|
|
//
|
|
Assert (*pcch);
|
|
cch = mi.cchMatchingPath + cchUrl - mi.cchMatchingURL + 1;
|
|
if (*pcch < cch)
|
|
{
|
|
DebugTrace ("ScStoragePathFromUrl (IIS URL Version): buffer too "
|
|
"small for url translation\n");
|
|
*pcch = cch;
|
|
|
|
//$ REVIEW: take ownership of the abandoned ref if one was abandoned
|
|
//
|
|
if (ppcvr)
|
|
{
|
|
auto_ref_ptr<CVRoot> arp;
|
|
arp.take_ownership(*ppcvr);
|
|
*ppcvr = NULL;
|
|
}
|
|
//
|
|
//$ REVIEW: end.
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
// Copy the Matching Path to the beginning of rgwchStgID
|
|
//
|
|
memcpy(wszStgID, mi.lpszPath, mi.cchMatchingPath * sizeof(WCHAR));
|
|
|
|
// Copy the request URL after the vroot, including '\0' termination
|
|
//
|
|
Assert (cchUrl >= mi.cchMatchingURL);
|
|
memcpy (wszStgID + mi.cchMatchingPath,
|
|
pwszUrl + mi.cchMatchingURL,
|
|
(cchUrl - mi.cchMatchingURL + 1) * sizeof(WCHAR));
|
|
|
|
// Change all '/' that came from URL to '\\'
|
|
//
|
|
for (LPWSTR pwch = wszStgID + mi.cchMatchingPath; *pwch; pwch++)
|
|
if (L'/' == *pwch) *pwch = L'\\';
|
|
|
|
// At this point, cch is the actual number of chars in the destination
|
|
// -- including the null
|
|
//
|
|
*pcch = cch;
|
|
Assert (L'\0' == wszStgID[cch - 1]);
|
|
Assert (L'\0' != wszStgID[cch - 2]);
|
|
return sc;
|
|
}
|
|
|
|
// Storage path to url translation -------------------------------------------
|
|
//
|
|
SCODE __fastcall
|
|
ScUrlFromStoragePath (
|
|
/* [in] */ const IEcbBase & ecb,
|
|
/* [in] */ LPCWSTR pwszPath,
|
|
/* [out] */ LPWSTR pwszUrl,
|
|
/* [in/out] */ UINT * pcch,
|
|
/* [in] */ LPCWSTR pwszServer)
|
|
{
|
|
WCHAR * pwch;
|
|
LPCWSTR pwszPrefix;
|
|
LPCWSTR pwszVroot;
|
|
LPCWSTR pwszVrPath;
|
|
UINT cch;
|
|
UINT cchPath;
|
|
UINT cchMatching;
|
|
UINT cchAdjust;
|
|
UINT cchPrefix;
|
|
UINT cchServer;
|
|
UINT cchVroot;
|
|
UINT cchTrailing;
|
|
|
|
// Find the number of path characters that match the
|
|
// virtual root
|
|
//
|
|
cchVroot = ecb.CchGetVirtualRootW (&pwszVroot);
|
|
|
|
// We always return fully qualified Urls -- so we need to know
|
|
// the server name and the prefix.
|
|
//
|
|
cchPrefix = ecb.CchUrlPrefixW (&pwszPrefix);
|
|
|
|
// If server name is not given yet take default one
|
|
//
|
|
if (!pwszServer)
|
|
{
|
|
cchServer = ecb.CchGetServerNameW (&pwszServer);
|
|
}
|
|
else
|
|
{
|
|
cchServer = static_cast<UINT>(wcslen(pwszServer));
|
|
}
|
|
|
|
// The number of characters to be skiped needs to include the physical
|
|
// vroot path.
|
|
//
|
|
cchMatching = ecb.CchGetMatchingPathW (&pwszVrPath);
|
|
|
|
// If the matching path is ending with '\\' we need to ajust accordingly
|
|
// as that symbol in the matching path is "overlapping" with the start
|
|
// of trailing URL part. To construct the URL correctly we need to make
|
|
// sure that we do not skip that separator. Also handle it the best way
|
|
// we can if someone is trying to commit suicide by putting '/' at the
|
|
// end of the matching path.
|
|
//
|
|
if ((0 != cchMatching) &&
|
|
(L'\\' == pwszVrPath[cchMatching - 1] || L'/' == pwszVrPath[cchMatching - 1]) )
|
|
{
|
|
cchAdjust = 1;
|
|
}
|
|
else
|
|
{
|
|
cchAdjust = 0;
|
|
}
|
|
|
|
// So, at this point, the length of the resulting url is the length
|
|
// of the servername, virtual root and trailing path all put together.
|
|
//
|
|
cchPath = static_cast<UINT>(wcslen(pwszPath));
|
|
|
|
// We assume that the path we are passed in is always fully qualified
|
|
// with the vroot. Assert that. Calculate the length of trailing
|
|
// portion including '\0' termination.
|
|
//
|
|
Assert (cchPath + cchAdjust >= cchMatching);
|
|
cchTrailing = cchPath - cchMatching + cchAdjust + 1;
|
|
cch = cchPrefix + cchServer + cchVroot + cchTrailing;
|
|
|
|
// If there is not enough room, a return value of S_FALSE will
|
|
// properly instruct the caller to realloc and call again.
|
|
//
|
|
if (*pcch < cch)
|
|
{
|
|
DebugTrace ("ScUrlFromStoragePath(): buffer too small for path translation.\n");
|
|
*pcch = cch;
|
|
return S_FALSE;
|
|
}
|
|
|
|
// Start building the url by copying over the prefix and servername.
|
|
//
|
|
memcpy (pwszUrl, pwszPrefix, cchPrefix * sizeof(WCHAR));
|
|
memcpy (pwszUrl + cchPrefix, pwszServer, cchServer * sizeof(WCHAR));
|
|
cch = cchPrefix + cchServer;
|
|
|
|
// Copy over the virtual root
|
|
//
|
|
memcpy (pwszUrl + cch, pwszVroot, cchVroot * sizeof(WCHAR));
|
|
cch += cchVroot;
|
|
|
|
//$ REVIEW: I don't know what happens here when we want to be able to
|
|
// span virtual roots with MOVE/COPY and what not. However, it will
|
|
// be up to the caller to fail this if that is the case.
|
|
//
|
|
if (!FSizedPathConflict (pwszPath,
|
|
cchPath,
|
|
pwszVrPath,
|
|
cchMatching))
|
|
{
|
|
DebugTrace ("ScUrlFromStoragePath (IIS URL Version): translation not "
|
|
"scoped by current virtual root\n");
|
|
return E_DAV_BAD_DESTINATION;
|
|
}
|
|
//
|
|
//$ REVIEW: end
|
|
|
|
// While copying make sure that we are not skiping the '\' separator
|
|
// at the beginning of the trailing URL. That is what cchAdjust stands for.
|
|
//
|
|
memcpy( pwszUrl + cch, pwszPath + cchMatching - cchAdjust, cchTrailing * sizeof(WCHAR));
|
|
|
|
// Lastly, swap all '\\' to '/'
|
|
//
|
|
for (pwch = pwszUrl + cch;
|
|
NULL != (pwch = wcschr (pwch, L'\\'));
|
|
)
|
|
{
|
|
*pwch++ = L'/';
|
|
}
|
|
|
|
// Pass back the length, cchTrailing includes the null-termination at this
|
|
// point.
|
|
//
|
|
*pcch = cch + cchTrailing;
|
|
Assert (0 == pwszUrl[cch + cchTrailing - 1]);
|
|
Assert (0 != pwszUrl[cch + cchTrailing - 2]);
|
|
|
|
DebugTrace ("ScUrlFromStoragePath(): translated path:\n"
|
|
"- path \"%ls\" maps to \"%ls\"\n"
|
|
"- cchMatchingPath = %d\n"
|
|
"- cchVroot = %d\n",
|
|
pwszPath,
|
|
pwszUrl,
|
|
cchMatching,
|
|
cchVroot);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
SCODE __fastcall
|
|
ScUrlFromSpannedStoragePath (
|
|
/* [in] */ LPCWSTR pwszPath,
|
|
/* [in] */ CVRoot & vr,
|
|
/* [in] */ LPWSTR pwszUrl,
|
|
/* [in/out] */ UINT * pcch)
|
|
{
|
|
WCHAR * pwch;
|
|
|
|
LPCWSTR pwszPort;
|
|
LPCWSTR pwszServer;
|
|
LPCWSTR pwszVRoot;
|
|
LPCWSTR pwszVRPath;
|
|
UINT cch;
|
|
UINT cchPort;
|
|
UINT cchServer;
|
|
UINT cchTotal;
|
|
UINT cchTrailing;
|
|
UINT cchVRoot;
|
|
|
|
// Make sure that the path and the virtual root context share a
|
|
// common base path!
|
|
//
|
|
cch = vr.CchGetVRPath(&pwszVRPath);
|
|
if (_wcsnicmp (pwszPath, pwszVRPath, cch))
|
|
{
|
|
DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): path "
|
|
"is not from virtual root\n");
|
|
return E_DAV_BAD_DESTINATION;
|
|
}
|
|
pwszPath += cch;
|
|
|
|
// If the next character is not a moniker separator, then this can't
|
|
// be a match
|
|
//
|
|
if (*pwszPath && (*pwszPath != L'\\'))
|
|
{
|
|
DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): path "
|
|
"is not from virtual root\n");
|
|
return E_DAV_BAD_DESTINATION;
|
|
}
|
|
|
|
// A concatination of the url prefix, server, port, vroot prefix and
|
|
// the remaining path gives us our URL.
|
|
//
|
|
cchTrailing = static_cast<UINT>(wcslen (pwszPath));
|
|
cchVRoot = vr.CchGetVRoot(&pwszVRoot);
|
|
cchServer = vr.CchGetServerName(&pwszServer);
|
|
cchPort = vr.CchGetPort(&pwszPort);
|
|
cch = cchTrailing +
|
|
cchVRoot +
|
|
cchPort +
|
|
cchServer +
|
|
CchConstString(gc_wszUrl_Prefix_Secure) + 1;
|
|
|
|
if (*pcch < cch)
|
|
{
|
|
DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): spanned "
|
|
"translation buffer too small\n");
|
|
|
|
*pcch = cch;
|
|
return S_FALSE;
|
|
}
|
|
|
|
// A small note about codepages....
|
|
//
|
|
// Start constructing the url by grabbing the appropriate prefix
|
|
//
|
|
if (vr.FSecure())
|
|
{
|
|
cchTotal = gc_cchszUrl_Prefix_Secure;
|
|
memcpy (pwszUrl, gc_wszUrl_Prefix_Secure, cchTotal * sizeof(WCHAR));
|
|
}
|
|
else
|
|
{
|
|
cchTotal = gc_cchszUrl_Prefix;
|
|
memcpy (pwszUrl, gc_wszUrl_Prefix, cchTotal * sizeof(WCHAR));
|
|
}
|
|
|
|
// Tack on the server name
|
|
//
|
|
memcpy (pwszUrl + cchTotal, pwszServer, cchServer * sizeof(WCHAR));
|
|
cchTotal += cchServer;
|
|
|
|
// Tack on the port if it is neither the default or a secure port
|
|
//
|
|
if (!vr.FDefaultPort() && !vr.FSecure())
|
|
{
|
|
memcpy (pwszUrl + cchTotal, pwszPort, cchPort * sizeof(WCHAR));
|
|
cchTotal += cchPort;
|
|
}
|
|
|
|
// Add the vroot
|
|
//
|
|
memcpy (pwszUrl + cchTotal, pwszVRoot, cchVRoot * sizeof(WCHAR));
|
|
cchTotal += cchVRoot;
|
|
|
|
// Add the trailing path.
|
|
//
|
|
// IMPORTANT: The resulting cch will include the NULL
|
|
// termination.
|
|
//
|
|
if (cch < cchTotal + cchTrailing + 1)
|
|
{
|
|
DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): spanned "
|
|
"translation buffer too small\n");
|
|
|
|
*pcch = cchTotal + cchTrailing + 1;
|
|
return S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
memcpy (pwszUrl + cchTotal, pwszPath, (cchTrailing + 1) * sizeof(WCHAR));
|
|
}
|
|
|
|
Assert (L'\0' == pwszUrl[cchTotal + cchTrailing]);
|
|
Assert (L'\0' != pwszUrl[cchTotal + cchTrailing - 1]);
|
|
|
|
// Translate all '\\' to '/'
|
|
//
|
|
for (pwch = pwszUrl + cchTrailing + 1; *pwch; pwch++)
|
|
{
|
|
if (L'\\' == *pwch)
|
|
{
|
|
*pwch = L'/';
|
|
}
|
|
}
|
|
|
|
DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): spanned "
|
|
"storage path fixed as '%S'\n", pwszUrl);
|
|
*pcch = cchTotal + cchTrailing + 1;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Wire urls -----------------------------------------------------------------
|
|
//
|
|
SCODE __fastcall
|
|
ScWireUrlFromWideLocalUrl (
|
|
/* [in] */ UINT cchLocal,
|
|
/* [in] */ LPCWSTR pwszLocalUrl,
|
|
/* [in/out] */ auto_heap_ptr<CHAR>& pszWireUrl)
|
|
{
|
|
UINT ib = 0;
|
|
|
|
// Since the url is already wide, all we need to do is
|
|
// to reduce the url to a UTF8 entity.
|
|
//
|
|
// We could call the Win32 WideCharToMultiByte(), but we
|
|
// already know that production, and it would be best to
|
|
// skip the system call if possible.
|
|
//
|
|
// Allocate enough space as if every char had maximum expansion
|
|
//
|
|
CStackBuffer<CHAR,MAX_PATH> psz;
|
|
if (NULL == psz.resize((cchLocal * 3) + 1))
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (cchLocal)
|
|
{
|
|
// Currently we get UTF-8 url-s onto the wire. Do we ever
|
|
// want to pipe out any other codepage?
|
|
//
|
|
ib = WideCharToUTF8(pwszLocalUrl,
|
|
cchLocal,
|
|
psz.get(),
|
|
(cchLocal * 3));
|
|
Assert(ib);
|
|
}
|
|
|
|
// Termination...
|
|
//
|
|
psz[ib] = 0;
|
|
|
|
// Escape it
|
|
//
|
|
HttpUriEscape (psz.get(), pszWireUrl);
|
|
return S_OK;
|
|
}
|
|
|
|
SCODE __fastcall
|
|
ScWireUrlFromStoragePath (
|
|
/* [in] */ IMethUtilBase * pmu,
|
|
/* [in] */ LPCWSTR pwszStoragePath,
|
|
/* [in] */ BOOL fCollection,
|
|
/* [in] */ CVRoot * pcvrTranslate,
|
|
/* [in/out] */ auto_heap_ptr<CHAR>& pszWireUrl)
|
|
{
|
|
Assert (pwszStoragePath);
|
|
Assert (NULL == pszWireUrl.get());
|
|
|
|
SCODE sc = S_OK;
|
|
|
|
// Take a best guess for size and try and convert
|
|
// NOTE: we allocate space allowing for the trailing
|
|
// slash on directories - thus for the calls filling
|
|
// the buffer we indicate that available space is one
|
|
// character less than actually allocated.
|
|
//
|
|
CStackBuffer<WCHAR,128> pwszUrl;
|
|
|
|
//$ REVIEW: WINRAID:462078: The "-1" below has to do
|
|
// with making sure that there is enough space to append
|
|
// a trailing slash at the end of the url for directories.
|
|
//
|
|
UINT cch = pwszUrl.celems() - 1;
|
|
//
|
|
//$ REVIEW: end.
|
|
|
|
if (pcvrTranslate == NULL)
|
|
{
|
|
sc = pmu->ScUrlFromStoragePath (pwszStoragePath, pwszUrl.get(), &cch);
|
|
if (S_FALSE == sc)
|
|
{
|
|
// Try again, but with a bigger size.
|
|
//
|
|
if (NULL == pwszUrl.resize(CbSizeWsz(cch)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
sc = pmu->ScUrlFromStoragePath (pwszStoragePath, pwszUrl.get(), &cch);
|
|
}
|
|
if (S_OK != sc)
|
|
{
|
|
DebugTrace ("ScWireUrlFromStoragePath (IIS URL Version): "
|
|
"failed to translate path to href\n");
|
|
return sc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sc = ScUrlFromSpannedStoragePath (pwszStoragePath,
|
|
*pcvrTranslate,
|
|
pwszUrl.get(),
|
|
&cch);
|
|
if (S_FALSE == sc)
|
|
{
|
|
// Try again, but with a bigger size.
|
|
//
|
|
if (NULL == pwszUrl.resize(CbSizeWsz(cch)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
sc = ScUrlFromSpannedStoragePath (pwszStoragePath,
|
|
*pcvrTranslate,
|
|
pwszUrl.get(),
|
|
&cch);
|
|
}
|
|
if (S_OK != sc)
|
|
{
|
|
DebugTrace ("ScWireUrlFromStoragePath (IIS URL Version): "
|
|
"failed to translate path to href\n");
|
|
return sc;
|
|
}
|
|
}
|
|
|
|
// cch includes the termination char
|
|
//
|
|
Assert (cch);
|
|
Assert (L'\0' == pwszUrl[cch - 1]);
|
|
Assert (L'\0' != pwszUrl[cch - 2]);
|
|
|
|
// For directories, check the trailing slash
|
|
//
|
|
if (fCollection && (L'/' != pwszUrl[cch - 2]))
|
|
{
|
|
// Add the trailing '/'
|
|
//
|
|
// Remember we've added one extra bytes when allocating pwszUrl
|
|
//
|
|
pwszUrl[cch - 1] = L'/';
|
|
pwszUrl[cch] = L'\0';
|
|
cch += 1;
|
|
}
|
|
|
|
return ScWireUrlFromWideLocalUrl (cch - 1, pwszUrl.get(), pszWireUrl);
|
|
}
|