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.
1493 lines
39 KiB
1493 lines
39 KiB
/*
|
|
Copyright (c) 1999 Microsoft Corporation, All Rights Reserved
|
|
|
|
Description
|
|
===========
|
|
This header defines the template class CEcbBaseImpl<>, which is used to implement
|
|
specific ECB-related functions which are required by the WebClient.
|
|
|
|
The reason for implementing the function this way is so that _WEBMAIL can be
|
|
linked into multiple projects, and so that each of those projects can use this
|
|
template to provide the ECB-related functions. That is to say, we want to share
|
|
the code, and this is the way we ended up doing it.
|
|
|
|
Specifically, _WEBMAIL accepts pointers of type IEcbBase. And _DAVPRS uses
|
|
objects of type IEcb, which is derived from IEcbBase - and all of the functions
|
|
in this template class used to be implemented by _DAVPRS. And EXWFORM wants to
|
|
use _WEBMAIL, and wants to share the implementation of these functions with
|
|
_DAVPRS, but doesn't want to have to implement all of IEcb.
|
|
|
|
So, in _DAVPRS, the class heirarchy looks like...
|
|
class IEcbBase;
|
|
class IEcb : public IEcbBase;
|
|
class CEcb : public CEcbBaseImpl<IEcb>;
|
|
|
|
In EXWFORM, the class heirarchy looks like...
|
|
class IEcbBase;
|
|
class CLocalEcb : public CEcbBaseImpl<IEcbBase>;
|
|
|
|
History
|
|
=======
|
|
6/27/99 dondu Created
|
|
*/
|
|
|
|
|
|
#ifndef _ECBIMPL_INC
|
|
#define _ECBIMPL_INC
|
|
|
|
#include <except.h>
|
|
|
|
|
|
template<class _T>
|
|
class CEcbBaseImpl : public _T
|
|
{
|
|
protected:
|
|
|
|
// Buffer for cached constant strings. Must be WCHAR because it is
|
|
// used for both CHAR and WCHAR strings. Alignment must be pesimistic.
|
|
//
|
|
mutable ChainedStringBuffer<WCHAR> m_sb;
|
|
|
|
// Skinny vroot information
|
|
//
|
|
mutable UINT m_cchVroot;
|
|
mutable LPSTR m_rgchVroot;
|
|
|
|
// Wide character vroot information
|
|
//
|
|
mutable UINT m_cchVrootW;
|
|
mutable LPWSTR m_rgwchVroot;
|
|
|
|
// Skinny vroot path
|
|
//
|
|
mutable UINT m_cchVrootPath;
|
|
mutable LPSTR m_rgchVrootPath;
|
|
|
|
// Wide character vroot path
|
|
//
|
|
mutable UINT m_cchVrootPathW;
|
|
mutable LPWSTR m_rgwchVrootPath;
|
|
|
|
// Skinny server name
|
|
//
|
|
mutable UINT m_cchServerName;
|
|
mutable LPSTR m_lpszServerName;
|
|
|
|
// Wide character server name
|
|
//
|
|
mutable UINT m_cchServerNameW;
|
|
mutable LPWSTR m_pwszServerName;
|
|
|
|
// Cached path translated from request URI (e.g. L"c:\davfs\foo.txt")
|
|
//
|
|
mutable LPWSTR m_pwszPathTranslated;
|
|
|
|
// Cached request URL in skinny form.
|
|
//
|
|
mutable LPSTR m_pszRequestUrl;
|
|
|
|
// Cached request URL in wide form.
|
|
//
|
|
mutable LPWSTR m_pwszRequestUrl;
|
|
|
|
// Cached raw URL
|
|
//
|
|
mutable LPSTR m_pszRawURL;
|
|
mutable UINT m_cbRawURL;
|
|
|
|
// Cached LCID of request/response language
|
|
//
|
|
mutable ULONG m_lcid;
|
|
|
|
// ECB port secure state
|
|
//
|
|
mutable enum {
|
|
|
|
HTTPS_UNKNOWN,
|
|
NORMAL,
|
|
SECURE
|
|
} m_secure;
|
|
mutable BOOL m_fFESecured;
|
|
|
|
// Cached Accept-Language: header
|
|
//
|
|
mutable auto_heap_ptr<CHAR> m_pszAcceptLanguage;
|
|
|
|
// Wide method name. Skinny version is on raw ECB
|
|
//
|
|
mutable LPWSTR m_pwszMethod;
|
|
|
|
private:
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CEcbBaseImpl(const CEcbBaseImpl&);
|
|
CEcbBaseImpl& operator=(const CEcbBaseImpl&);
|
|
|
|
// Internal private helpers for caching vroot information
|
|
//
|
|
VOID GetMapExInfo60After() const;
|
|
VOID GetMapExInfo60Before() const;
|
|
|
|
protected:
|
|
|
|
CEcbBaseImpl(EXTENSION_CONTROL_BLOCK& ecb);
|
|
|
|
// Internal helper for caching vroot information
|
|
//
|
|
VOID GetMapExInfo() const;
|
|
|
|
public:
|
|
|
|
// Server variables
|
|
//
|
|
virtual BOOL FGetServerVariable( LPCSTR pszName, LPSTR pszValue, DWORD * pcbValue ) const;
|
|
virtual BOOL FGetServerVariable( LPCSTR pszName, LPWSTR pwszValue, DWORD * pcchValue ) const;
|
|
|
|
// Virtual root information
|
|
//
|
|
virtual UINT CchGetVirtualRoot( LPCSTR * ppszVroot ) const;
|
|
virtual UINT CchGetVirtualRootW( LPCWSTR * ppwszVroot ) const;
|
|
|
|
virtual UINT CchGetMatchingPathW( LPCWSTR * ppwszMatchingPath ) const;
|
|
|
|
// Server name
|
|
//
|
|
virtual UINT CchGetServerName( LPCSTR* ppszServer) const;
|
|
virtual UINT CchGetServerNameW( LPCWSTR* ppwszServer) const;
|
|
|
|
// URL prefix
|
|
//
|
|
virtual LPCSTR LpszUrlPrefix() const;
|
|
virtual LPCWSTR LpwszUrlPrefix() const;
|
|
virtual UINT CchUrlPrefix( LPCSTR * ppszPrefix ) const;
|
|
virtual UINT CchUrlPrefixW( LPCWSTR * ppwszPrefix ) const;
|
|
|
|
// ACCESSORS
|
|
//
|
|
virtual LPCSTR LpszRequestUrl() const;
|
|
virtual LPCWSTR LpwszRequestUrl() const;
|
|
virtual LPCWSTR LpwszMethod() const;
|
|
virtual LPCWSTR LpwszPathTranslated() const;
|
|
virtual UINT CbGetRawURL (LPCSTR * ppszRawURL) const;
|
|
virtual ULONG LcidAccepted() const;
|
|
virtual VOID SetLcidAccepted(LCID lcid);
|
|
|
|
virtual BOOL FSsl() const;
|
|
virtual BOOL FFrontEndSecured() const { return FSsl() && m_fFESecured; }
|
|
};
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::CEcbBaseImpl()
|
|
//
|
|
template<class _T>
|
|
CEcbBaseImpl<_T>::CEcbBaseImpl(EXTENSION_CONTROL_BLOCK& ecb) :
|
|
_T(ecb),
|
|
m_sb(1024), // 1K for constant cached strings
|
|
m_cchVroot(0),
|
|
m_rgchVroot(NULL),
|
|
m_cchVrootW(0),
|
|
m_rgwchVroot(NULL),
|
|
m_cchVrootPath(0),
|
|
m_rgchVrootPath(NULL),
|
|
m_cchVrootPathW(0),
|
|
m_rgwchVrootPath(NULL),
|
|
m_cchServerName(0),
|
|
m_lpszServerName(NULL),
|
|
m_cchServerNameW(0),
|
|
m_pwszServerName(NULL),
|
|
m_pwszPathTranslated(NULL),
|
|
m_pszRequestUrl(NULL),
|
|
m_pwszRequestUrl(NULL),
|
|
m_pszRawURL(NULL),
|
|
m_cbRawURL(0),
|
|
m_lcid(0),
|
|
m_secure(HTTPS_UNKNOWN),
|
|
m_fFESecured(FALSE),
|
|
m_pwszMethod(NULL)
|
|
{
|
|
#ifdef DBG
|
|
|
|
// This is here (in the DBG build only) to help generate
|
|
// compile errors for any inappropriate use of this template
|
|
// class - basically, you're not supposed to use this class
|
|
// with anything other than IEcbBase, or something which
|
|
// derives from IEcbBase.
|
|
//
|
|
IEcbBase* p;
|
|
p = reinterpret_cast<_T *> (NULL);
|
|
|
|
#endif
|
|
// nothing
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::GetMapExInfo60After()
|
|
//
|
|
template<class _T>
|
|
VOID CEcbBaseImpl<_T>::GetMapExInfo60After() const
|
|
{
|
|
if ( !m_rgwchVroot )
|
|
{
|
|
HSE_UNICODE_URL_MAPEX_INFO mi;
|
|
UINT cbPath = sizeof(mi.lpszPath);
|
|
|
|
//$REMOVE after 156176 is fixed START
|
|
//
|
|
mi.lpszPath[0] = L'\0';
|
|
mi.cchMatchingPath = 0;
|
|
//
|
|
//$REMOVE after 156176 is fixed END
|
|
|
|
// No cached wide vroot data. Get mapings for the request URL.
|
|
//
|
|
// We can get the virtual root by translating the path and using
|
|
// the count of matched characters in the URL.
|
|
//
|
|
// NOTE: ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX)
|
|
// has a bug - it requires the count of bytes available for the
|
|
// path. We know that MAX_PATH is available in HSE_UNICODE_URL_MAPEX_INFO
|
|
// so pass in the right value to work around the crash.
|
|
//
|
|
if ( !m_pecb->ServerSupportFunction( m_pecb->ConnID,
|
|
HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX,
|
|
const_cast<LPWSTR>(LpwszRequestUrl()),
|
|
reinterpret_cast<DWORD *>(&cbPath),
|
|
reinterpret_cast<DWORD *>(&mi) ))
|
|
{
|
|
// There is a fix for Windows Bugs 156176 that we need to do the
|
|
// following check for. It applies to IIS 6.0 (+) path only. In IIS 5.0
|
|
// the maping functions were silently succeeding, and truncating the
|
|
// buffer that contained the mapped path if it exceeded MAX_PATH.
|
|
// That behaviour suited us, but is not very nice, so IIS 6.0 chose
|
|
// to still fill in the buffer as before, but fail with special error
|
|
// (ERROR_INSUFFICIENT_BUFFER). That error still means success to us,
|
|
// so fail only if we see something different
|
|
//
|
|
if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
|
|
{
|
|
// Function does not allow to return failures, so the only option
|
|
// is to throw. We cannot proceed if we did not get the data anyway.
|
|
// If this function succeeds once, subsequent calls to it are non
|
|
// failing.
|
|
//
|
|
DebugTrace ("CEcbBaseImpl<_T>::GetMapExInfo60After() - ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX) failed 0x%08lX\n", GetLastError());
|
|
throw CLastErrorException();
|
|
}
|
|
}
|
|
|
|
//$REMOVE after 156176 is fixed START
|
|
//
|
|
if (L'\0' == mi.lpszPath[0])
|
|
{
|
|
DebugTrace ("CEcbBaseImpl<_T>::GetMapExInfo60After() - ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX) failed 0x%08lX\n", GetLastError());
|
|
throw CLastErrorException();
|
|
}
|
|
//
|
|
//$REMOVE after 156176 is fixed END
|
|
|
|
EcbTrace ("Dav: caching request URI maping info (path for IIS 6.0 and later):\n"
|
|
" URL \"%ls\" maps to \"%ls\"\n"
|
|
" dwFlags = 0x%08x\n"
|
|
" cchMatchingPath = %d\n"
|
|
" cchMatchingURL = %d\n",
|
|
LpwszRequestUrl(),
|
|
mi.lpszPath,
|
|
mi.dwFlags,
|
|
mi.cchMatchingPath,
|
|
mi.cchMatchingURL);
|
|
|
|
// Adjust the matching URL ...
|
|
//
|
|
if ( mi.cchMatchingURL )
|
|
{
|
|
LPCWSTR pwsz = LpwszRequestUrl() + mi.cchMatchingURL - 1;
|
|
|
|
// ... do not include the trailing slash, if any...
|
|
//
|
|
if ( L'/' == *pwsz )
|
|
{
|
|
mi.cchMatchingURL -= 1;
|
|
}
|
|
|
|
// ... also we found a case (INDEX on the vroot) where the
|
|
// cchMatching... points to the '\0' (where a trailing slash
|
|
// would be IF DAV methods required a trailing slash). So,
|
|
// also chop off any trailing '\0' here! --BeckyAn 21Aug1997
|
|
//
|
|
else if ( L'\0' == *pwsz )
|
|
{
|
|
mi.cchMatchingURL -= 1;
|
|
}
|
|
}
|
|
|
|
// Cache the vroot data.
|
|
// Corollary: m_cchVrootW should always be > 0 when we have data.
|
|
//
|
|
m_cchVrootW = mi.cchMatchingURL + 1;
|
|
m_rgwchVroot = reinterpret_cast<LPWSTR>(m_sb.Alloc(m_cchVrootW * sizeof(WCHAR)));
|
|
memcpy (m_rgwchVroot, LpwszRequestUrl(), m_cchVrootW * sizeof(WCHAR));
|
|
m_rgwchVroot[m_cchVrootW - 1] = L'\0';
|
|
|
|
// 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--;
|
|
}
|
|
}
|
|
|
|
// Cache the matching path data.
|
|
// Corollary: m_cchVrootPathW should always be > 0 when we have data.
|
|
//
|
|
m_cchVrootPathW = mi.cchMatchingPath + 1;
|
|
m_rgwchVrootPath = reinterpret_cast<LPWSTR>(m_sb.Alloc(m_cchVrootPathW * sizeof(WCHAR)));
|
|
memcpy (m_rgwchVrootPath, mi.lpszPath, mi.cchMatchingPath * sizeof(WCHAR));
|
|
m_rgwchVrootPath[mi.cchMatchingPath] = L'\0';
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::GetMapExInfo60Before()
|
|
//
|
|
template<class _T>
|
|
VOID CEcbBaseImpl<_T>::GetMapExInfo60Before() const
|
|
{
|
|
if ( !m_rgchVroot )
|
|
{
|
|
HSE_URL_MAPEX_INFO mi;
|
|
|
|
// No cached wide vroot data. Get mapings for the request URL.
|
|
//
|
|
// We can get the virtual root by translating the path and using
|
|
// the count of matched characters in the URL
|
|
//
|
|
// NOTE: ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX)
|
|
// does not require the count of bytes available for the path.
|
|
// So we just pass in NULL and it will figure out the available size
|
|
// itself - it knows the form of HSE_URL_MAPEX_INFO too, and that
|
|
// it gives MAX_PATH butes for the translated path.
|
|
//
|
|
if ( !m_pecb->ServerSupportFunction( m_pecb->ConnID,
|
|
HSE_REQ_MAP_URL_TO_PATH_EX,
|
|
const_cast<LPSTR>(LpszRequestUrl()),
|
|
NULL,
|
|
reinterpret_cast<DWORD *>(&mi) ))
|
|
{
|
|
// Function does not allow to return failures, so the only option
|
|
// is to throw. We cannot proceed if we did not get the data anyway.
|
|
// If this function succeeds once, subsequent calls to it are non
|
|
// failing.
|
|
//
|
|
DebugTrace ("CEcbBaseImpl<_T>::GetMapExInfo60Before() - ServerSupportFunction(HSE_REQ_MAP_URL_TO_PATH_EX) failed 0x%08lX\n", GetLastError());
|
|
throw CLastErrorException();
|
|
}
|
|
|
|
EcbTrace ("Dav: caching request URI maping info (path for pre IIS 6.0):\n"
|
|
" URL \"%hs\" maps to \"%hs\"\n"
|
|
" dwFlags = 0x%08x\n"
|
|
" cchMatchingPath = %d\n"
|
|
" cchMatchingURL = %d\n",
|
|
LpszRequestUrl(),
|
|
mi.lpszPath,
|
|
mi.dwFlags,
|
|
mi.cchMatchingPath,
|
|
mi.cchMatchingURL);
|
|
|
|
// Adjust the matching URL ...
|
|
//
|
|
if ( mi.cchMatchingURL )
|
|
{
|
|
LPCSTR psz = LpszRequestUrl() + mi.cchMatchingURL - 1;
|
|
|
|
// ... do not include the trailing slash, if any...
|
|
//
|
|
if ( '/' == *psz )
|
|
{
|
|
//$ RAID: NT:359868
|
|
//
|
|
// This is the first of many places where we need to be very
|
|
// careful with our usage of single character checks. Namely,
|
|
// in DBCS land, we need to check for lead bytes before treating
|
|
// the last char as if it is a slash.
|
|
//
|
|
if (!FIsDBCSTrailingByte (psz, mi.cchMatchingURL))
|
|
mi.cchMatchingURL -= 1;
|
|
//
|
|
//$ RAID: end.
|
|
}
|
|
|
|
// ... also we found a case (INDEX on the vroot) where the
|
|
// cchMatching... points to the '\0' (where a trailing slash
|
|
// would be IF DAV methods required a trailing slash). So,
|
|
// also chop off any trailing '\0' here! --BeckyAn 21Aug1997
|
|
//
|
|
else if ( '\0' == *psz )
|
|
{
|
|
mi.cchMatchingURL -= 1;
|
|
}
|
|
}
|
|
|
|
// Cache the vroot data.
|
|
// Corollary: m_cchVrootW should always be > 0 when we have data.
|
|
//
|
|
m_cchVroot = mi.cchMatchingURL + 1;
|
|
m_rgchVroot = reinterpret_cast<LPSTR>(m_sb.Alloc(m_cchVroot));
|
|
memcpy (m_rgchVroot, LpszRequestUrl(), m_cchVroot);
|
|
m_rgchVroot[m_cchVroot - 1] = '\0';
|
|
|
|
// Adjust the matching path the same way as we did maching URL
|
|
//
|
|
if ( mi.cchMatchingPath )
|
|
{
|
|
LPCSTR psz = mi.lpszPath + mi.cchMatchingPath - 1;
|
|
|
|
if ( '\\' == *psz )
|
|
{
|
|
//$ RAID: NT:359868
|
|
//
|
|
// This is the second of many places where we need to be very
|
|
// careful with our usage of single character checks. Namely,
|
|
// in DBCS land, we need to check for lead bytes before treating
|
|
// the last char as if it is a backslash.
|
|
//
|
|
while ((0 < mi.cchMatchingPath) &&
|
|
('\\' == *psz) &&
|
|
(!FIsDBCSTrailingByte (psz, mi.cchMatchingPath)) &&
|
|
(!FIsDriveTrailingChar (psz, mi.cchMatchingPath)))
|
|
{
|
|
mi.cchMatchingPath--;
|
|
psz--;
|
|
}
|
|
|
|
//
|
|
//$ RAID: end.
|
|
}
|
|
else if ( '\0' == *psz )
|
|
{
|
|
mi.cchMatchingPath--;
|
|
}
|
|
}
|
|
|
|
// Cache the matching path data.
|
|
// Corollary: m_cchVrootPath should always be > 0 when we have data.
|
|
//
|
|
m_cchVrootPath = mi.cchMatchingPath + 1;
|
|
m_rgchVrootPath = reinterpret_cast<LPSTR>(m_sb.Alloc(m_cchVrootPath));
|
|
memcpy (m_rgchVrootPath, mi.lpszPath, mi.cchMatchingPath);
|
|
m_rgchVrootPath[mi.cchMatchingPath] = '\0';
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::GetMapExInfo()
|
|
//
|
|
template<class _T>
|
|
VOID CEcbBaseImpl<_T>::GetMapExInfo() const
|
|
{
|
|
if ( m_pecb->dwVersion >= IIS_VERSION_6_0 )
|
|
{
|
|
GetMapExInfo60After();
|
|
}
|
|
else
|
|
{
|
|
GetMapExInfo60Before();
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// FGetServerVariable()
|
|
//
|
|
// Get the value of an ECB variable (e.g. "SERVER_NAME")
|
|
//
|
|
template<class _T>
|
|
BOOL
|
|
CEcbBaseImpl<_T>::FGetServerVariable( LPCSTR pszName, LPSTR pszValue,
|
|
DWORD * pcbValue ) const
|
|
{
|
|
BOOL fResult = FALSE;
|
|
|
|
|
|
Assert( m_pecb );
|
|
Assert( !IsBadWritePtr( pcbValue, sizeof(DWORD) ) );
|
|
Assert( *pcbValue > 0 );
|
|
Assert( !IsBadWritePtr( pszValue, *pcbValue ) );
|
|
|
|
if ( m_pecb->GetServerVariable( m_pecb->ConnID,
|
|
const_cast<LPSTR>(pszName),
|
|
pszValue,
|
|
pcbValue ) )
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
else if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
AssertSz( GetLastError() == ERROR_INVALID_INDEX, "Unexpected last error from GetServerVariable()\n" );
|
|
*pcbValue = 0;
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
template<class _T>
|
|
BOOL
|
|
CEcbBaseImpl<_T>::FGetServerVariable( LPCSTR pszName, LPWSTR pwszValue,
|
|
DWORD * pcchValue ) const
|
|
{
|
|
BOOL fResult = FALSE;
|
|
CStackBuffer<CHAR> pszValue;
|
|
DWORD cbValue;
|
|
UINT cch;
|
|
|
|
Assert( m_pecb );
|
|
Assert( !IsBadWritePtr( pcchValue, sizeof(DWORD) ) );
|
|
Assert( *pcchValue > 0 );
|
|
Assert( !IsBadWritePtr( pwszValue, *pcchValue * sizeof(WCHAR) ) );
|
|
|
|
// Assume that 1 wide character can be made of 3 skinny ones,
|
|
// which may be true in the case codepage is CP_UTF8
|
|
//
|
|
cbValue = *pcchValue * 3;
|
|
if (NULL != pszValue.resize(cbValue))
|
|
{
|
|
if ( m_pecb->GetServerVariable( m_pecb->ConnID,
|
|
const_cast<LPSTR>(pszName),
|
|
pszValue.get(),
|
|
&cbValue ) )
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
else if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
|
|
{
|
|
if (NULL != pszValue.resize(cbValue))
|
|
{
|
|
if ( m_pecb->GetServerVariable( m_pecb->ConnID,
|
|
const_cast<LPSTR>(pszName),
|
|
pszValue.get(),
|
|
&cbValue ) )
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// By now we should be succesfull in geting data as the buffer provided
|
|
// was big enough
|
|
//
|
|
if (FALSE == fResult)
|
|
{
|
|
EcbTrace( "Dav: CEcbBaseImpl<_T>::FGetServerVariable(). Error 0x%08lX from GetServerVariable()\n", GetLastError() );
|
|
*pcchValue = 0;
|
|
goto ret;
|
|
}
|
|
|
|
// We have the data, need to convert it to wide version, assume we will fail
|
|
//
|
|
fResult = FALSE;
|
|
cch = MultiByteToWideChar(CP_ACP,
|
|
MB_ERR_INVALID_CHARS,
|
|
pszValue.get(),
|
|
cbValue,
|
|
pwszValue,
|
|
*pcchValue);
|
|
if (0 == cch)
|
|
{
|
|
// The function failed...
|
|
//
|
|
if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
|
|
{
|
|
// ... figure out the necessary size for the buffer
|
|
//
|
|
cch = MultiByteToWideChar(CP_ACP,
|
|
MB_ERR_INVALID_CHARS,
|
|
pszValue.get(),
|
|
cbValue,
|
|
NULL,
|
|
0);
|
|
if (0 == cch)
|
|
{
|
|
// We still failed
|
|
//
|
|
AssertSz( ERROR_INSUFFICIENT_BUFFER != GetLastError(), "We should not fail with ERROR_INSUFFICIENT BUFFER here.\n" );
|
|
|
|
EcbTrace( "Dav: CEcbBaseImpl<_T>::FGetServerVariable(). Error 0x%08lX from MultiByteToWideChar() "
|
|
"while trying to find sufficient length for conversion.\n", GetLastError() );
|
|
|
|
*pcchValue = 0;
|
|
goto ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ... failure was fatal
|
|
//
|
|
EcbTrace( "Dav: CEcbBaseImpl<_T>::FGetServerVariable(), Error 0x%08lX from MultiByteToWideChar() "
|
|
"while trying to convert.\n", GetLastError() );
|
|
|
|
*pcchValue = 0;
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
*pcchValue = cch;
|
|
fResult = TRUE;
|
|
|
|
ret:
|
|
|
|
return fResult;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::CchGetVirtualRoot
|
|
//
|
|
// Fetch and cache the vroot information.
|
|
//
|
|
template<class _T>
|
|
UINT CEcbBaseImpl<_T>::CchGetVirtualRoot( LPCSTR * ppszVroot ) const
|
|
{
|
|
// Tidiness. If we fail, want to return a NULL.
|
|
// (We return zero for the cch if we fail.)
|
|
// Pre-set it here.
|
|
//
|
|
Assert( ppszVroot );
|
|
*ppszVroot = NULL;
|
|
|
|
// Check if we have cached vroot data.
|
|
//
|
|
GetMapExInfo();
|
|
|
|
// If the skinny version of vroot is not available generate
|
|
// an cache one
|
|
//
|
|
if (NULL == m_rgchVroot)
|
|
{
|
|
// We got the maping info, so if the skinny version of the
|
|
// vroot was not available, then at least wide will be there.
|
|
// That would meen that we are on IIS 6.0 or later. Also we
|
|
// should have 0 bytes for skinny vroot at this point.
|
|
//
|
|
Assert(m_rgwchVroot);
|
|
Assert(m_pecb->dwVersion >= IIS_VERSION_6_0);
|
|
Assert(0 == m_cchVroot);
|
|
|
|
UINT cb = m_cchVrootW * 3;
|
|
m_rgchVroot = reinterpret_cast<LPSTR>(m_sb.Alloc(cb));
|
|
m_cchVroot = WideCharToMultiByte ( CP_ACP,
|
|
0,
|
|
m_rgwchVroot,
|
|
m_cchVrootW,
|
|
m_rgchVroot,
|
|
cb,
|
|
0,
|
|
0 );
|
|
if (0 == m_cchVroot)
|
|
{
|
|
DebugTrace ("Dav: CEcbBaseImpl::CchGetVirtualRoot failed(%ld)\n", GetLastError());
|
|
throw CLastErrorException();
|
|
}
|
|
}
|
|
|
|
// Give the data back to the caller from our cache.
|
|
//
|
|
*ppszVroot = m_rgchVroot;
|
|
return m_cchVroot - 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::CchGetVirtualRootW
|
|
//
|
|
// Fetch and cache the vroot information.
|
|
//
|
|
template<class _T>
|
|
UINT CEcbBaseImpl<_T>::CchGetVirtualRootW( LPCWSTR * ppwszVroot ) const
|
|
{
|
|
// Tidiness. If we fail, want to return a NULL.
|
|
// (We return zero for the cch if we fail.)
|
|
// Pre-set it here.
|
|
//
|
|
Assert( ppwszVroot );
|
|
*ppwszVroot = NULL;
|
|
|
|
// Check if we have cached vroot data.
|
|
//
|
|
GetMapExInfo();
|
|
|
|
// If the wide version of vroot is not available generate
|
|
// an cache one
|
|
//
|
|
if (NULL == m_rgwchVroot)
|
|
{
|
|
// We got the maping info, so if the wide version of the
|
|
// vroot was not available, then at least wide will be there.
|
|
// That would meen that we are on pre IIS 6.0 version. Also we
|
|
// should have 0 bytes for wide vroot at this point.
|
|
//
|
|
Assert(m_rgchVroot);
|
|
Assert(m_pecb->dwVersion < IIS_VERSION_6_0);
|
|
Assert(0 == m_cchVrootW);
|
|
|
|
UINT cb = m_cchVroot * sizeof(WCHAR);
|
|
m_rgwchVroot = reinterpret_cast<LPWSTR>(m_sb.Alloc(cb));
|
|
m_cchVrootW = MultiByteToWideChar ( CP_ACP,
|
|
MB_ERR_INVALID_CHARS,
|
|
m_rgchVroot,
|
|
m_cchVroot,
|
|
m_rgwchVroot,
|
|
m_cchVroot);
|
|
if (0 == m_cchVrootW)
|
|
{
|
|
DebugTrace ("Dav: CEcbBaseImpl::CchGetVirtualRootW failed(%ld)\n", GetLastError());
|
|
throw CLastErrorException();
|
|
}
|
|
}
|
|
|
|
// Give the data back to the caller from our cache.
|
|
//
|
|
*ppwszVroot = m_rgwchVroot;
|
|
return m_cchVrootW - 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::CchGetMatchingPathW
|
|
//
|
|
// Fetch and cache the matching path information.
|
|
//
|
|
template<class _T>
|
|
UINT CEcbBaseImpl<_T>::CchGetMatchingPathW( LPCWSTR * ppwszPath ) const
|
|
{
|
|
// Tidiness. If we fail, want to return a NULL.
|
|
// (We return zero for the cch if we fail.)
|
|
// Pre-set it here.
|
|
//
|
|
Assert( ppwszPath );
|
|
*ppwszPath = NULL;
|
|
|
|
// Check if we have cached vroot data.
|
|
//
|
|
GetMapExInfo();
|
|
|
|
// Give the data back to the caller from our cache.
|
|
//
|
|
if (NULL == m_rgwchVrootPath)
|
|
{
|
|
// We got the maping info, so if the wide version of the
|
|
// matching path was not available, then at least skinny will
|
|
// be there. That would meen that we are on pre IIS 6.0 version.
|
|
// Also we should have 0 bytes for wide matching path at
|
|
// this point.
|
|
//
|
|
Assert(m_rgchVrootPath);
|
|
Assert(m_pecb->dwVersion < IIS_VERSION_6_0);
|
|
Assert(0 == m_cchVrootPathW);
|
|
|
|
UINT cb = m_cchVrootPath * sizeof(WCHAR);
|
|
m_rgwchVrootPath = reinterpret_cast<LPWSTR>(m_sb.Alloc(cb));
|
|
m_cchVrootPathW = MultiByteToWideChar ( CP_ACP,
|
|
MB_ERR_INVALID_CHARS,
|
|
m_rgchVrootPath,
|
|
m_cchVrootPath,
|
|
m_rgwchVrootPath,
|
|
m_cchVrootPath);
|
|
if (0 == m_cchVrootPathW)
|
|
{
|
|
DebugTrace ("Dav: CEcbBaseImpl::CchGetMatchingPathW failed(%ld)\n", GetLastError());
|
|
throw CLastErrorException();
|
|
}
|
|
}
|
|
|
|
// Give the data back to the caller from our cache.
|
|
//
|
|
*ppwszPath = m_rgwchVrootPath;
|
|
return m_cchVrootPathW - 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::CchGetServerName
|
|
//
|
|
// Fetch and cache the server name, including port number
|
|
//
|
|
template<class _T>
|
|
UINT
|
|
CEcbBaseImpl<_T>::CchGetServerName( LPCSTR* ppszServer ) const
|
|
{
|
|
if ( !m_lpszServerName )
|
|
{
|
|
DWORD cbName;
|
|
DWORD cbPort;
|
|
CStackBuffer<CHAR> lpszName;
|
|
CStackBuffer<CHAR> lpszPort;
|
|
|
|
cbName = lpszName.celems();
|
|
for ( ;; )
|
|
{
|
|
DWORD cbCur = cbName;
|
|
|
|
lpszName.resize(cbName);
|
|
if ( FGetServerVariable( gc_szServer_Name,
|
|
lpszName.get(),
|
|
&cbName ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( cbName == 0 )
|
|
{
|
|
lpszName[0] = '\0';
|
|
++cbName;
|
|
break;
|
|
}
|
|
|
|
// If the size was big enough but we still failed, we
|
|
// are probably out of memory.
|
|
//
|
|
if (cbName && (cbCur >= cbName))
|
|
{
|
|
throw CHresultException(E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
cbPort = lpszPort.celems();
|
|
for ( ;; )
|
|
{
|
|
lpszPort.resize(cbPort);
|
|
if ( FGetServerVariable( gc_szServer_Port,
|
|
lpszPort.get(),
|
|
&cbPort ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( cbPort == 0 )
|
|
{
|
|
lpszPort[0] = '\0';
|
|
++cbPort;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// We should not have "PORT" values greater
|
|
// than 4 digits anyway....
|
|
//
|
|
throw CHresultException(E_INVALIDARG);
|
|
}
|
|
}
|
|
|
|
// Limit the servname/port combination to 256 (including NULL)
|
|
//
|
|
if (256 < (cbName + cbPort))
|
|
{
|
|
throw CHresultException(E_INVALIDARG);
|
|
}
|
|
|
|
// Allocate enough space for the server name and port plus
|
|
// a ':' separator. Note that the ':' replaces the '\0' at
|
|
// the end of the name, so we don't need to add 1 for it here.
|
|
//
|
|
m_lpszServerName = reinterpret_cast<LPSTR>(m_sb.Alloc(cbName + cbPort));
|
|
|
|
// Format the whole thing as "<name>[:<port>]" where
|
|
// :<port> is only included if the port is not the default
|
|
// port for the connection (:443 for SSL or :80 for standard)
|
|
//
|
|
CopyMemory( m_lpszServerName,
|
|
lpszName.get(),
|
|
cbName );
|
|
|
|
// If we are secure and the port is "443", or if the port is
|
|
// the default one, then there is no need to append the port
|
|
// number to the server name.
|
|
//
|
|
if (( FSsl() && !strcmp( lpszPort.get(), gc_sz443 )) ||
|
|
!strcmp( lpszPort.get(), gc_sz80 ))
|
|
{
|
|
// It was easier to write the conditional this way and
|
|
// have the real work done in the "else" clause.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
// Append the port to the server name
|
|
//
|
|
m_lpszServerName[cbName-1] = ':';
|
|
CopyMemory( m_lpszServerName + cbName,
|
|
lpszPort.get(),
|
|
cbPort );
|
|
}
|
|
|
|
m_cchServerName = static_cast<UINT>(strlen(m_lpszServerName));
|
|
}
|
|
|
|
*ppszServer = m_lpszServerName;
|
|
return m_cchServerName;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::CchGetServerNameW
|
|
//
|
|
// Fetch and cache the server name, including port number
|
|
//
|
|
template<class _T>
|
|
UINT
|
|
CEcbBaseImpl<_T>::CchGetServerNameW( LPCWSTR* ppwszServer ) const
|
|
{
|
|
if ( !m_pwszServerName )
|
|
{
|
|
// Fetch the server name and include 0 termination in its length
|
|
//
|
|
LPCSTR pszServerName = NULL;
|
|
UINT cbServerName = CchGetServerName(&pszServerName) + 1;
|
|
|
|
// We are looking for the wide server name for the first time.
|
|
// The character count should be zero at that point.
|
|
//
|
|
Assert(!m_cchServerNameW);
|
|
|
|
UINT cb = cbServerName * sizeof(WCHAR);
|
|
m_pwszServerName = reinterpret_cast<LPWSTR>(m_sb.Alloc(cb));
|
|
m_cchServerNameW = MultiByteToWideChar ( CP_ACP,
|
|
MB_ERR_INVALID_CHARS,
|
|
pszServerName,
|
|
cbServerName,
|
|
m_pwszServerName,
|
|
cbServerName);
|
|
if (0 == m_cchServerNameW)
|
|
{
|
|
DebugTrace ("Dav: CEcbBaseImpl::CchGetServerNameW failed(%ld)\n", GetLastError());
|
|
throw CLastErrorException();
|
|
}
|
|
|
|
// Subtract 0 termination so that we would behave the same way as
|
|
// the skinny version of the function
|
|
//
|
|
m_cchServerNameW--;
|
|
}
|
|
|
|
*ppwszServer = m_pwszServerName;
|
|
return m_cchServerNameW;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::LpszUrlPrefix
|
|
//
|
|
// Fetch and cache the url prefix
|
|
//
|
|
extern const __declspec(selectany) CHAR gsc_szHTTPS[] = "HTTPS";
|
|
extern const __declspec(selectany) CHAR gsc_szFrontEndHTTPS[] = "HTTP_FRONT_END_HTTPS";
|
|
extern const __declspec(selectany) CHAR gsc_szOn[] = "on";
|
|
|
|
template<class _T>
|
|
BOOL
|
|
CEcbBaseImpl<_T>::FSsl() const
|
|
{
|
|
if (m_secure == HTTPS_UNKNOWN)
|
|
{
|
|
// Start out believing that we are not in a secure environment
|
|
//
|
|
m_secure = NORMAL;
|
|
|
|
// We want to ask the ECB for the server variables that indicate
|
|
// whether or not the constructed urls should be secured or not.
|
|
//
|
|
// In the case of a FE/BE topology, the FE will include a header
|
|
// "Front-End-HTTPS" that indicates whether or not the FE/BE was
|
|
// secured via SSL. In the absence of the header, we should try
|
|
// to fallback to the IIS "HTTPS" header. For either header, we
|
|
// we check its value -- in both cases, it should either be "on"
|
|
// or "off"
|
|
//
|
|
// IMPORTANT: you have to check for the FE entry first! That is
|
|
// the overriding value for this configuration.
|
|
//
|
|
CHAR szHttps[8];
|
|
ULONG cb = sizeof(szHttps);
|
|
ULONG cbFE = sizeof(szHttps);
|
|
|
|
m_fFESecured = FGetServerVariable (gsc_szFrontEndHTTPS, szHttps, &cbFE);
|
|
if (m_fFESecured || FGetServerVariable (gsc_szHTTPS, szHttps, &cb))
|
|
{
|
|
if (!_stricmp(szHttps, gsc_szOn))
|
|
m_secure = SECURE;
|
|
}
|
|
}
|
|
return (SECURE == m_secure);
|
|
}
|
|
|
|
template<class _T>
|
|
LPCSTR
|
|
CEcbBaseImpl<_T>::LpszUrlPrefix() const
|
|
{
|
|
return (FSsl() ? gc_szUrl_Prefix_Secure : gc_szUrl_Prefix);
|
|
}
|
|
|
|
template<class _T>
|
|
LPCWSTR
|
|
CEcbBaseImpl<_T>::LpwszUrlPrefix() const
|
|
{
|
|
return (FSsl() ? gc_wszUrl_Prefix_Secure : gc_wszUrl_Prefix);
|
|
}
|
|
|
|
template<class _T>
|
|
UINT
|
|
CEcbBaseImpl<_T>::CchUrlPrefix( LPCSTR * ppszPrefix ) const
|
|
{
|
|
// Make sure that we know which prefix we are working with...
|
|
//
|
|
LPCSTR psz = LpszUrlPrefix();
|
|
|
|
// If the caller wants the pointer, too, give it to 'em
|
|
//
|
|
if (ppszPrefix)
|
|
*ppszPrefix = psz;
|
|
|
|
// Return the appropriate size
|
|
//
|
|
return ((m_secure == SECURE)
|
|
? gc_cchszUrl_Prefix_Secure
|
|
: gc_cchszUrl_Prefix);
|
|
}
|
|
|
|
template<class _T>
|
|
UINT
|
|
CEcbBaseImpl<_T>::CchUrlPrefixW( LPCWSTR * ppwszPrefix ) const
|
|
{
|
|
// Make sure that we know which prefix we are working with...
|
|
//
|
|
LPCWSTR pwsz = LpwszUrlPrefix();
|
|
|
|
// If the caller wants the pointer, too, give it to 'em
|
|
//
|
|
if (ppwszPrefix)
|
|
*ppwszPrefix = pwsz;
|
|
|
|
// Return the appropriate size
|
|
//
|
|
return ((m_secure == SECURE)
|
|
? gc_cchszUrl_Prefix_Secure
|
|
: gc_cchszUrl_Prefix);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::LpszRequestUrl()
|
|
//
|
|
template<class _T>
|
|
LPCWSTR
|
|
CEcbBaseImpl<_T>::LpwszRequestUrl() const
|
|
{
|
|
if (!m_pwszRequestUrl)
|
|
{
|
|
SCODE sc;
|
|
|
|
CStackBuffer<CHAR> pszAcceptLanguage;
|
|
CStackBuffer<CHAR> pszRawUrlCopy;
|
|
LPCSTR pszQueryStringStart = NULL;
|
|
LPCSTR pszRawUrl = NULL;
|
|
UINT cbRawUrl = 0;
|
|
UINT cchRequestUrl = 0;
|
|
|
|
// Grab the raw URL.
|
|
//
|
|
cbRawUrl = CbGetRawURL(&pszRawUrl);
|
|
|
|
// We also need to cut off the URL at the beginning of the query
|
|
// string, if there is one.
|
|
//
|
|
pszQueryStringStart = strchr(pszRawUrl, '?');
|
|
if (pszQueryStringStart)
|
|
{
|
|
// If there is a query string we need to make a copy of
|
|
// the raw URL to work with.
|
|
//
|
|
cbRawUrl = static_cast<UINT>(pszQueryStringStart - pszRawUrl);
|
|
|
|
// Allocate a buffer, and copy it in!
|
|
//
|
|
pszRawUrlCopy.resize(cbRawUrl + 1);
|
|
memcpy(pszRawUrlCopy.get(), pszRawUrl, cbRawUrl);
|
|
pszRawUrlCopy[cbRawUrl] = '\0';
|
|
|
|
// Now set up to normalize from this copy. Do not forget
|
|
// to increment cbRawUrl to include '\0' termination.
|
|
//
|
|
cbRawUrl++;
|
|
pszRawUrl = pszRawUrlCopy.get();
|
|
}
|
|
|
|
// Before normalizing the url, get the Accept-Language: header
|
|
// to pass it in. This will be used in figuring out the correct
|
|
// code page to use to decode non-UTF8 urls.
|
|
//
|
|
for ( DWORD cbValue = 256; cbValue > 0; )
|
|
{
|
|
DWORD cbCur = cbValue;
|
|
pszAcceptLanguage.resize(cbValue);
|
|
|
|
// Zero the string.
|
|
//
|
|
pszAcceptLanguage[0] = '\0';
|
|
|
|
// Get the header value
|
|
//
|
|
if ( FGetServerVariable( "HTTP_ACCEPT_LANGUAGE",
|
|
pszAcceptLanguage.get(),
|
|
&cbValue ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If the size needed is the same as the size returned, but the
|
|
// header retrieval still fails, then we are probably out of memory.
|
|
//
|
|
if (cbValue && (cbCur >= cbValue))
|
|
{
|
|
throw CHresultException(E_OUTOFMEMORY);
|
|
}
|
|
|
|
}
|
|
|
|
// Now, normalize the URL and we're done
|
|
//
|
|
cchRequestUrl = cbRawUrl;
|
|
m_pwszRequestUrl = reinterpret_cast<LPWSTR>(m_sb.Alloc (cchRequestUrl * sizeof(WCHAR)));
|
|
|
|
sc = ScNormalizeUrl(pszRawUrl,
|
|
&cchRequestUrl,
|
|
m_pwszRequestUrl,
|
|
pszAcceptLanguage.get());
|
|
if (S_OK != sc)
|
|
{
|
|
// We should never get S_FALSE here, since we've passed enough buffer space.
|
|
// Most often callers of this function assume that it cannot return NULL,
|
|
// and on the other hand we cannot do anything without request URL. Thus
|
|
// throw the last error exception.
|
|
//
|
|
Assert(S_FALSE != sc);
|
|
DebugTrace("CEcbBaseImpl::LpwszRequestUrl() - ScNormalizeUrl() failed with error 0x%08lX\n", sc);
|
|
SetLastError(sc);
|
|
throw CLastErrorException();
|
|
}
|
|
|
|
// Store the pointer to stripped request URL
|
|
//
|
|
m_pwszRequestUrl = const_cast<LPWSTR>(PwszUrlStrippedOfPrefix(m_pwszRequestUrl));
|
|
}
|
|
|
|
return m_pwszRequestUrl;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::LpszRequestUrl()
|
|
//
|
|
template<class _T>
|
|
LPCSTR
|
|
CEcbBaseImpl<_T>::LpszRequestUrl() const
|
|
{
|
|
if (!m_pszRequestUrl)
|
|
{
|
|
LPCWSTR pwszRequestUrl;
|
|
UINT cbRequestUrl;
|
|
UINT cchRequestUrl;
|
|
|
|
pwszRequestUrl = LpwszRequestUrl();
|
|
cchRequestUrl = static_cast<UINT>(wcslen(pwszRequestUrl));
|
|
cbRequestUrl = cchRequestUrl * 3;
|
|
m_pszRequestUrl = reinterpret_cast<LPSTR>(m_sb.Alloc (cbRequestUrl + 1));
|
|
|
|
// The reason for choosing CP_ACP codepage here is that it matches
|
|
// the old behaviour.
|
|
//
|
|
cbRequestUrl = WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
pwszRequestUrl,
|
|
cchRequestUrl + 1,
|
|
m_pszRequestUrl,
|
|
cbRequestUrl + 1,
|
|
NULL,
|
|
NULL);
|
|
if (0 == cbRequestUrl)
|
|
{
|
|
DebugTrace( "CEcbBaseImpl::LpszRequestUrl() - WideCharToMultiByte() failed 0x%08lX\n",
|
|
HRESULT_FROM_WIN32(GetLastError()) );
|
|
|
|
throw CLastErrorException();
|
|
}
|
|
}
|
|
|
|
return m_pszRequestUrl;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::LpwszRequestUrl()
|
|
//
|
|
template<class _T>
|
|
LPCWSTR
|
|
CEcbBaseImpl<_T>::LpwszMethod() const
|
|
{
|
|
if (!m_pwszMethod)
|
|
{
|
|
LPCSTR pszMethod;
|
|
UINT cbMethod;
|
|
UINT cchMethod;
|
|
|
|
pszMethod = LpszMethod();
|
|
cbMethod = static_cast<UINT>(strlen(pszMethod));
|
|
|
|
m_pwszMethod = reinterpret_cast<LPWSTR>(
|
|
m_sb.Alloc (CbSizeWsz(cbMethod)));
|
|
|
|
cchMethod = MultiByteToWideChar(CP_ACP,
|
|
MB_ERR_INVALID_CHARS,
|
|
pszMethod,
|
|
cbMethod + 1,
|
|
m_pwszMethod,
|
|
cbMethod + 1);
|
|
if (0 == cchMethod)
|
|
{
|
|
DebugTrace( "CEcbBaseImpl::LpwszRequestUrl() - MultiByteToWideChar() failed 0x%08lX\n",
|
|
HRESULT_FROM_WIN32(GetLastError()) );
|
|
|
|
throw CLastErrorException();
|
|
}
|
|
}
|
|
|
|
return m_pwszMethod;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::LpwszPathTranslated()
|
|
//
|
|
template<class _T>
|
|
LPCWSTR
|
|
CEcbBaseImpl<_T>::LpwszPathTranslated() const
|
|
{
|
|
// Cache the path info in the first call
|
|
//
|
|
if (!m_pwszPathTranslated)
|
|
{
|
|
LPCWSTR pwszRequestUrl;
|
|
UINT cchRequestUrl;
|
|
LPCWSTR pwszMatching;
|
|
UINT cchMatching;
|
|
LPCWSTR pwszVroot;
|
|
UINT cchVroot;
|
|
UINT cchPathTranslated;
|
|
|
|
// Grab the request URL.
|
|
//
|
|
pwszRequestUrl = LpwszRequestUrl();
|
|
cchRequestUrl = static_cast<UINT>(wcslen(pwszRequestUrl));
|
|
|
|
// Grab the matching path information.
|
|
//
|
|
pwszMatching = NULL;
|
|
cchMatching = CchGetMatchingPathW(&pwszMatching);
|
|
|
|
// Grab the virtual root information.
|
|
//
|
|
pwszVroot = NULL;
|
|
cchVroot = CchGetVirtualRootW(&pwszVroot);
|
|
|
|
// Move the request URL pointer over to snip off the virtual root.
|
|
//
|
|
pwszRequestUrl += cchVroot;
|
|
cchRequestUrl -= cchVroot;
|
|
|
|
// Allocate enough space for the matching path and the request URL, and
|
|
// copy the pieces in.
|
|
//
|
|
m_pwszPathTranslated = reinterpret_cast<LPWSTR>(
|
|
m_sb.Alloc (CbSizeWsz(cchMatching + cchRequestUrl)));
|
|
|
|
// Copy the matching path.
|
|
//
|
|
memcpy (m_pwszPathTranslated, pwszMatching, cchMatching * sizeof(WCHAR));
|
|
|
|
// Copy the request URL after the vroot, including '\0' termination
|
|
//
|
|
memcpy (m_pwszPathTranslated + cchMatching, pwszRequestUrl, (cchRequestUrl + 1) * sizeof(WCHAR));
|
|
|
|
// Change all '/' that came from URL to '\\'
|
|
//
|
|
for (LPWSTR pwch = m_pwszPathTranslated + cchMatching; *pwch; pwch++)
|
|
{
|
|
if (L'/' == *pwch)
|
|
{
|
|
*pwch = L'\\';
|
|
}
|
|
}
|
|
|
|
// We must remove all trailing slashes, in case the path is not empty string
|
|
//
|
|
cchPathTranslated = cchMatching + cchRequestUrl;
|
|
if (0 < cchPathTranslated)
|
|
{
|
|
LPWSTR pwszTrailing = m_pwszPathTranslated + cchPathTranslated - 1;
|
|
|
|
// Since URL is normalized there may be not more than one trailing slash.
|
|
// We check only for backslash, as we already changed all forward slashes
|
|
// to backslashes. Also do not remove trailing slash for the root of the
|
|
// drive.
|
|
//
|
|
if ((L'\\' == *pwszTrailing) &&
|
|
(!FIsDriveTrailingChar(pwszTrailing, cchPathTranslated)))
|
|
{
|
|
cchPathTranslated--;
|
|
*pwszTrailing = L'\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
return m_pwszPathTranslated;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::CbGetRawURL
|
|
//
|
|
// Fetch and cache the raw URL
|
|
//
|
|
template<class _T>
|
|
UINT
|
|
CEcbBaseImpl<_T>::CbGetRawURL (LPCSTR* ppszRawURL) const
|
|
{
|
|
if (!m_pszRawURL)
|
|
{
|
|
DWORD cbRawURL;
|
|
CStackBuffer<CHAR,MAX_PATH> pszRawURL;
|
|
|
|
cbRawURL = pszRawURL.size();
|
|
for ( ;; )
|
|
{
|
|
DWORD cbCur = cbRawURL;
|
|
pszRawURL.resize(cbRawURL);
|
|
if (FGetServerVariable ("UNENCODED_URL",
|
|
pszRawURL.get(),
|
|
&cbRawURL))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (cbRawURL == 0)
|
|
{
|
|
pszRawURL[0] = '\0';
|
|
cbRawURL++;
|
|
break;
|
|
}
|
|
|
|
// If the size needed is the same as the size returned, but the
|
|
// header retrieval still fails, then we are probably out of memory.
|
|
//
|
|
if (cbCur >= cbRawURL)
|
|
{
|
|
throw CHresultException(E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
Assert ('\0' == pszRawURL[cbRawURL - 1]);
|
|
UrlTrace("CEcbBaseImpl::CbGetRawURL(): Raw URL = %s\n", pszRawURL.get());
|
|
|
|
// Copy the data to our object.
|
|
//
|
|
m_pszRawURL = reinterpret_cast<LPSTR>(m_sb.Alloc (cbRawURL));
|
|
memcpy (m_pszRawURL, pszRawURL.get(), cbRawURL);
|
|
m_cbRawURL = cbRawURL;
|
|
}
|
|
|
|
// Return the cached values to the caller.
|
|
//
|
|
*ppszRawURL = m_pszRawURL;
|
|
return m_cbRawURL;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::LcidAccepted()
|
|
//
|
|
// Fetch, cache, and return the LCID of the accepted language
|
|
// based on the value of the Accept-Language header (if any).
|
|
//
|
|
// The default LCID is the special constant referring to the
|
|
// default system locale.
|
|
//
|
|
//$REVIEW$
|
|
// The const at the end of the function needs to be removed
|
|
//
|
|
template<class _T>
|
|
ULONG
|
|
CEcbBaseImpl<_T>::LcidAccepted() const
|
|
{
|
|
if ( !m_lcid )
|
|
{
|
|
LPCSTR psz = m_pszAcceptLanguage.get();
|
|
HDRITER hdri(psz);
|
|
ULONG lcid;
|
|
|
|
m_lcid = LOCALE_NEUTRAL; // must be the same as lcidDefault in mdbeif.hxx
|
|
|
|
for (psz = hdri.PszNext(); psz; psz = hdri.PszNext())
|
|
if (FLookupLCID(psz, &lcid))
|
|
{
|
|
m_lcid = LANGIDFROMLCID(lcid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return m_lcid;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CEcbBaseImpl::SetLcidAccepted
|
|
//
|
|
// Sets the LCID for the request. We call this function when
|
|
// we dont have an AcceptLang header and we want to override
|
|
// the default LCID (with the LCID in the Cookie)
|
|
//
|
|
//$REVIEW$
|
|
// After RTM, this function should be merged with the LcidAccepted() function.
|
|
// The LcidAccepted() should check AcceptLang header and if it is not present,
|
|
// should check the lcid in the cookie.
|
|
//
|
|
template<class _T>
|
|
VOID
|
|
CEcbBaseImpl<_T>::SetLcidAccepted(LCID lcid)
|
|
{
|
|
m_lcid = lcid;
|
|
}
|
|
|
|
|
|
#endif
|