|
|
// ========================================================================
// 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); }
|