/* 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; In EXWFORM, the class heirarchy looks like... class IEcbBase; class CLocalEcb : public CEcbBaseImpl; History ======= 6/27/99 dondu Created */ #ifndef _ECBIMPL_INC #define _ECBIMPL_INC #include template 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 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 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 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 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(LpwszRequestUrl()), reinterpret_cast(&cbPath), reinterpret_cast(&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(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(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 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(LpszRequestUrl()), NULL, reinterpret_cast(&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(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(m_sb.Alloc(m_cchVrootPath)); memcpy (m_rgchVrootPath, mi.lpszPath, mi.cchMatchingPath); m_rgchVrootPath[mi.cchMatchingPath] = '\0'; } } // ------------------------------------------------------------------------ // // CEcbBaseImpl::GetMapExInfo() // template 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 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(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 BOOL CEcbBaseImpl<_T>::FGetServerVariable( LPCSTR pszName, LPWSTR pwszValue, DWORD * pcchValue ) const { BOOL fResult = FALSE; CStackBuffer 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(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(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 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(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 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(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 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(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 UINT CEcbBaseImpl<_T>::CchGetServerName( LPCSTR* ppszServer ) const { if ( !m_lpszServerName ) { DWORD cbName; DWORD cbPort; CStackBuffer lpszName; CStackBuffer 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(m_sb.Alloc(cbName + cbPort)); // Format the whole thing as "[:]" where // : 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(strlen(m_lpszServerName)); } *ppszServer = m_lpszServerName; return m_cchServerName; } // ------------------------------------------------------------------------ // // CEcbBaseImpl::CchGetServerNameW // // Fetch and cache the server name, including port number // template 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(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 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 LPCSTR CEcbBaseImpl<_T>::LpszUrlPrefix() const { return (FSsl() ? gc_szUrl_Prefix_Secure : gc_szUrl_Prefix); } template LPCWSTR CEcbBaseImpl<_T>::LpwszUrlPrefix() const { return (FSsl() ? gc_wszUrl_Prefix_Secure : gc_wszUrl_Prefix); } template 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 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 LPCWSTR CEcbBaseImpl<_T>::LpwszRequestUrl() const { if (!m_pwszRequestUrl) { SCODE sc; CStackBuffer pszAcceptLanguage; CStackBuffer 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(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(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(PwszUrlStrippedOfPrefix(m_pwszRequestUrl)); } return m_pwszRequestUrl; } // ------------------------------------------------------------------------ // // CEcbBaseImpl::LpszRequestUrl() // template LPCSTR CEcbBaseImpl<_T>::LpszRequestUrl() const { if (!m_pszRequestUrl) { LPCWSTR pwszRequestUrl; UINT cbRequestUrl; UINT cchRequestUrl; pwszRequestUrl = LpwszRequestUrl(); cchRequestUrl = static_cast(wcslen(pwszRequestUrl)); cbRequestUrl = cchRequestUrl * 3; m_pszRequestUrl = reinterpret_cast(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 LPCWSTR CEcbBaseImpl<_T>::LpwszMethod() const { if (!m_pwszMethod) { LPCSTR pszMethod; UINT cbMethod; UINT cchMethod; pszMethod = LpszMethod(); cbMethod = static_cast(strlen(pszMethod)); m_pwszMethod = reinterpret_cast( 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 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(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( 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 UINT CEcbBaseImpl<_T>::CbGetRawURL (LPCSTR* ppszRawURL) const { if (!m_pszRawURL) { DWORD cbRawURL; CStackBuffer 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(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 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 VOID CEcbBaseImpl<_T>::SetLcidAccepted(LCID lcid) { m_lcid = lcid; } #endif