Leaked source code of windows server 2003
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

  1. /*
  2. Copyright (c) 1999 Microsoft Corporation, All Rights Reserved
  3. Description
  4. ===========
  5. This header defines the template class CEcbBaseImpl<>, which is used to implement
  6. specific ECB-related functions which are required by the WebClient.
  7. The reason for implementing the function this way is so that _WEBMAIL can be
  8. linked into multiple projects, and so that each of those projects can use this
  9. template to provide the ECB-related functions. That is to say, we want to share
  10. the code, and this is the way we ended up doing it.
  11. Specifically, _WEBMAIL accepts pointers of type IEcbBase. And _DAVPRS uses
  12. objects of type IEcb, which is derived from IEcbBase - and all of the functions
  13. in this template class used to be implemented by _DAVPRS. And EXWFORM wants to
  14. use _WEBMAIL, and wants to share the implementation of these functions with
  15. _DAVPRS, but doesn't want to have to implement all of IEcb.
  16. So, in _DAVPRS, the class heirarchy looks like...
  17. class IEcbBase;
  18. class IEcb : public IEcbBase;
  19. class CEcb : public CEcbBaseImpl<IEcb>;
  20. In EXWFORM, the class heirarchy looks like...
  21. class IEcbBase;
  22. class CLocalEcb : public CEcbBaseImpl<IEcbBase>;
  23. History
  24. =======
  25. 6/27/99 dondu Created
  26. */
  27. #ifndef _ECBIMPL_INC
  28. #define _ECBIMPL_INC
  29. #include <except.h>
  30. template<class _T>
  31. class CEcbBaseImpl : public _T
  32. {
  33. protected:
  34. // Buffer for cached constant strings. Must be WCHAR because it is
  35. // used for both CHAR and WCHAR strings. Alignment must be pesimistic.
  36. //
  37. mutable ChainedStringBuffer<WCHAR> m_sb;
  38. // Skinny vroot information
  39. //
  40. mutable UINT m_cchVroot;
  41. mutable LPSTR m_rgchVroot;
  42. // Wide character vroot information
  43. //
  44. mutable UINT m_cchVrootW;
  45. mutable LPWSTR m_rgwchVroot;
  46. // Skinny vroot path
  47. //
  48. mutable UINT m_cchVrootPath;
  49. mutable LPSTR m_rgchVrootPath;
  50. // Wide character vroot path
  51. //
  52. mutable UINT m_cchVrootPathW;
  53. mutable LPWSTR m_rgwchVrootPath;
  54. // Skinny server name
  55. //
  56. mutable UINT m_cchServerName;
  57. mutable LPSTR m_lpszServerName;
  58. // Wide character server name
  59. //
  60. mutable UINT m_cchServerNameW;
  61. mutable LPWSTR m_pwszServerName;
  62. // Cached path translated from request URI (e.g. L"c:\davfs\foo.txt")
  63. //
  64. mutable LPWSTR m_pwszPathTranslated;
  65. // Cached request URL in skinny form.
  66. //
  67. mutable LPSTR m_pszRequestUrl;
  68. // Cached request URL in wide form.
  69. //
  70. mutable LPWSTR m_pwszRequestUrl;
  71. // Cached raw URL
  72. //
  73. mutable LPSTR m_pszRawURL;
  74. mutable UINT m_cbRawURL;
  75. // Cached LCID of request/response language
  76. //
  77. mutable ULONG m_lcid;
  78. // ECB port secure state
  79. //
  80. mutable enum {
  81. HTTPS_UNKNOWN,
  82. NORMAL,
  83. SECURE
  84. } m_secure;
  85. mutable BOOL m_fFESecured;
  86. // Cached Accept-Language: header
  87. //
  88. mutable auto_heap_ptr<CHAR> m_pszAcceptLanguage;
  89. // Wide method name. Skinny version is on raw ECB
  90. //
  91. mutable LPWSTR m_pwszMethod;
  92. private:
  93. // NOT IMPLEMENTED
  94. //
  95. CEcbBaseImpl(const CEcbBaseImpl&);
  96. CEcbBaseImpl& operator=(const CEcbBaseImpl&);
  97. // Internal private helpers for caching vroot information
  98. //
  99. VOID GetMapExInfo60After() const;
  100. VOID GetMapExInfo60Before() const;
  101. protected:
  102. CEcbBaseImpl(EXTENSION_CONTROL_BLOCK& ecb);
  103. // Internal helper for caching vroot information
  104. //
  105. VOID GetMapExInfo() const;
  106. public:
  107. // Server variables
  108. //
  109. virtual BOOL FGetServerVariable( LPCSTR pszName, LPSTR pszValue, DWORD * pcbValue ) const;
  110. virtual BOOL FGetServerVariable( LPCSTR pszName, LPWSTR pwszValue, DWORD * pcchValue ) const;
  111. // Virtual root information
  112. //
  113. virtual UINT CchGetVirtualRoot( LPCSTR * ppszVroot ) const;
  114. virtual UINT CchGetVirtualRootW( LPCWSTR * ppwszVroot ) const;
  115. virtual UINT CchGetMatchingPathW( LPCWSTR * ppwszMatchingPath ) const;
  116. // Server name
  117. //
  118. virtual UINT CchGetServerName( LPCSTR* ppszServer) const;
  119. virtual UINT CchGetServerNameW( LPCWSTR* ppwszServer) const;
  120. // URL prefix
  121. //
  122. virtual LPCSTR LpszUrlPrefix() const;
  123. virtual LPCWSTR LpwszUrlPrefix() const;
  124. virtual UINT CchUrlPrefix( LPCSTR * ppszPrefix ) const;
  125. virtual UINT CchUrlPrefixW( LPCWSTR * ppwszPrefix ) const;
  126. // ACCESSORS
  127. //
  128. virtual LPCSTR LpszRequestUrl() const;
  129. virtual LPCWSTR LpwszRequestUrl() const;
  130. virtual LPCWSTR LpwszMethod() const;
  131. virtual LPCWSTR LpwszPathTranslated() const;
  132. virtual UINT CbGetRawURL (LPCSTR * ppszRawURL) const;
  133. virtual ULONG LcidAccepted() const;
  134. virtual VOID SetLcidAccepted(LCID lcid);
  135. virtual BOOL FSsl() const;
  136. virtual BOOL FFrontEndSecured() const { return FSsl() && m_fFESecured; }
  137. };
  138. // ------------------------------------------------------------------------
  139. //
  140. // CEcbBaseImpl::CEcbBaseImpl()
  141. //
  142. template<class _T>
  143. CEcbBaseImpl<_T>::CEcbBaseImpl(EXTENSION_CONTROL_BLOCK& ecb) :
  144. _T(ecb),
  145. m_sb(1024), // 1K for constant cached strings
  146. m_cchVroot(0),
  147. m_rgchVroot(NULL),
  148. m_cchVrootW(0),
  149. m_rgwchVroot(NULL),
  150. m_cchVrootPath(0),
  151. m_rgchVrootPath(NULL),
  152. m_cchVrootPathW(0),
  153. m_rgwchVrootPath(NULL),
  154. m_cchServerName(0),
  155. m_lpszServerName(NULL),
  156. m_cchServerNameW(0),
  157. m_pwszServerName(NULL),
  158. m_pwszPathTranslated(NULL),
  159. m_pszRequestUrl(NULL),
  160. m_pwszRequestUrl(NULL),
  161. m_pszRawURL(NULL),
  162. m_cbRawURL(0),
  163. m_lcid(0),
  164. m_secure(HTTPS_UNKNOWN),
  165. m_fFESecured(FALSE),
  166. m_pwszMethod(NULL)
  167. {
  168. #ifdef DBG
  169. // This is here (in the DBG build only) to help generate
  170. // compile errors for any inappropriate use of this template
  171. // class - basically, you're not supposed to use this class
  172. // with anything other than IEcbBase, or something which
  173. // derives from IEcbBase.
  174. //
  175. IEcbBase* p;
  176. p = reinterpret_cast<_T *> (NULL);
  177. #endif
  178. // nothing
  179. }
  180. // ------------------------------------------------------------------------
  181. //
  182. // CEcbBaseImpl::GetMapExInfo60After()
  183. //
  184. template<class _T>
  185. VOID CEcbBaseImpl<_T>::GetMapExInfo60After() const
  186. {
  187. if ( !m_rgwchVroot )
  188. {
  189. HSE_UNICODE_URL_MAPEX_INFO mi;
  190. UINT cbPath = sizeof(mi.lpszPath);
  191. //$REMOVE after 156176 is fixed START
  192. //
  193. mi.lpszPath[0] = L'\0';
  194. mi.cchMatchingPath = 0;
  195. //
  196. //$REMOVE after 156176 is fixed END
  197. // No cached wide vroot data. Get mapings for the request URL.
  198. //
  199. // We can get the virtual root by translating the path and using
  200. // the count of matched characters in the URL.
  201. //
  202. // NOTE: ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX)
  203. // has a bug - it requires the count of bytes available for the
  204. // path. We know that MAX_PATH is available in HSE_UNICODE_URL_MAPEX_INFO
  205. // so pass in the right value to work around the crash.
  206. //
  207. if ( !m_pecb->ServerSupportFunction( m_pecb->ConnID,
  208. HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX,
  209. const_cast<LPWSTR>(LpwszRequestUrl()),
  210. reinterpret_cast<DWORD *>(&cbPath),
  211. reinterpret_cast<DWORD *>(&mi) ))
  212. {
  213. // There is a fix for Windows Bugs 156176 that we need to do the
  214. // following check for. It applies to IIS 6.0 (+) path only. In IIS 5.0
  215. // the maping functions were silently succeeding, and truncating the
  216. // buffer that contained the mapped path if it exceeded MAX_PATH.
  217. // That behaviour suited us, but is not very nice, so IIS 6.0 chose
  218. // to still fill in the buffer as before, but fail with special error
  219. // (ERROR_INSUFFICIENT_BUFFER). That error still means success to us,
  220. // so fail only if we see something different
  221. //
  222. if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
  223. {
  224. // Function does not allow to return failures, so the only option
  225. // is to throw. We cannot proceed if we did not get the data anyway.
  226. // If this function succeeds once, subsequent calls to it are non
  227. // failing.
  228. //
  229. DebugTrace ("CEcbBaseImpl<_T>::GetMapExInfo60After() - ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX) failed 0x%08lX\n", GetLastError());
  230. throw CLastErrorException();
  231. }
  232. }
  233. //$REMOVE after 156176 is fixed START
  234. //
  235. if (L'\0' == mi.lpszPath[0])
  236. {
  237. DebugTrace ("CEcbBaseImpl<_T>::GetMapExInfo60After() - ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX) failed 0x%08lX\n", GetLastError());
  238. throw CLastErrorException();
  239. }
  240. //
  241. //$REMOVE after 156176 is fixed END
  242. EcbTrace ("Dav: caching request URI maping info (path for IIS 6.0 and later):\n"
  243. " URL \"%ls\" maps to \"%ls\"\n"
  244. " dwFlags = 0x%08x\n"
  245. " cchMatchingPath = %d\n"
  246. " cchMatchingURL = %d\n",
  247. LpwszRequestUrl(),
  248. mi.lpszPath,
  249. mi.dwFlags,
  250. mi.cchMatchingPath,
  251. mi.cchMatchingURL);
  252. // Adjust the matching URL ...
  253. //
  254. if ( mi.cchMatchingURL )
  255. {
  256. LPCWSTR pwsz = LpwszRequestUrl() + mi.cchMatchingURL - 1;
  257. // ... do not include the trailing slash, if any...
  258. //
  259. if ( L'/' == *pwsz )
  260. {
  261. mi.cchMatchingURL -= 1;
  262. }
  263. // ... also we found a case (INDEX on the vroot) where the
  264. // cchMatching... points to the '\0' (where a trailing slash
  265. // would be IF DAV methods required a trailing slash). So,
  266. // also chop off any trailing '\0' here! --BeckyAn 21Aug1997
  267. //
  268. else if ( L'\0' == *pwsz )
  269. {
  270. mi.cchMatchingURL -= 1;
  271. }
  272. }
  273. // Cache the vroot data.
  274. // Corollary: m_cchVrootW should always be > 0 when we have data.
  275. //
  276. m_cchVrootW = mi.cchMatchingURL + 1;
  277. m_rgwchVroot = reinterpret_cast<LPWSTR>(m_sb.Alloc(m_cchVrootW * sizeof(WCHAR)));
  278. memcpy (m_rgwchVroot, LpwszRequestUrl(), m_cchVrootW * sizeof(WCHAR));
  279. m_rgwchVroot[m_cchVrootW - 1] = L'\0';
  280. // Adjust the matching path the same way as we did matching URL
  281. //
  282. if ( mi.cchMatchingPath )
  283. {
  284. LPCWSTR pwsz = mi.lpszPath + mi.cchMatchingPath - 1;
  285. if ( L'\\' == *pwsz )
  286. {
  287. while ((0 < mi.cchMatchingPath) &&
  288. (L'\\' == *pwsz) &&
  289. (!FIsDriveTrailingChar(pwsz, mi.cchMatchingPath)))
  290. {
  291. mi.cchMatchingPath--;
  292. pwsz--;
  293. }
  294. }
  295. else if ( L'\0' == *pwsz )
  296. {
  297. mi.cchMatchingPath--;
  298. }
  299. }
  300. // Cache the matching path data.
  301. // Corollary: m_cchVrootPathW should always be > 0 when we have data.
  302. //
  303. m_cchVrootPathW = mi.cchMatchingPath + 1;
  304. m_rgwchVrootPath = reinterpret_cast<LPWSTR>(m_sb.Alloc(m_cchVrootPathW * sizeof(WCHAR)));
  305. memcpy (m_rgwchVrootPath, mi.lpszPath, mi.cchMatchingPath * sizeof(WCHAR));
  306. m_rgwchVrootPath[mi.cchMatchingPath] = L'\0';
  307. }
  308. }
  309. // ------------------------------------------------------------------------
  310. //
  311. // CEcbBaseImpl::GetMapExInfo60Before()
  312. //
  313. template<class _T>
  314. VOID CEcbBaseImpl<_T>::GetMapExInfo60Before() const
  315. {
  316. if ( !m_rgchVroot )
  317. {
  318. HSE_URL_MAPEX_INFO mi;
  319. // No cached wide vroot data. Get mapings for the request URL.
  320. //
  321. // We can get the virtual root by translating the path and using
  322. // the count of matched characters in the URL
  323. //
  324. // NOTE: ServerSupportFunction(HSE_REQ_MAP_UNICODE_URL_TO_PATH_EX)
  325. // does not require the count of bytes available for the path.
  326. // So we just pass in NULL and it will figure out the available size
  327. // itself - it knows the form of HSE_URL_MAPEX_INFO too, and that
  328. // it gives MAX_PATH butes for the translated path.
  329. //
  330. if ( !m_pecb->ServerSupportFunction( m_pecb->ConnID,
  331. HSE_REQ_MAP_URL_TO_PATH_EX,
  332. const_cast<LPSTR>(LpszRequestUrl()),
  333. NULL,
  334. reinterpret_cast<DWORD *>(&mi) ))
  335. {
  336. // Function does not allow to return failures, so the only option
  337. // is to throw. We cannot proceed if we did not get the data anyway.
  338. // If this function succeeds once, subsequent calls to it are non
  339. // failing.
  340. //
  341. DebugTrace ("CEcbBaseImpl<_T>::GetMapExInfo60Before() - ServerSupportFunction(HSE_REQ_MAP_URL_TO_PATH_EX) failed 0x%08lX\n", GetLastError());
  342. throw CLastErrorException();
  343. }
  344. EcbTrace ("Dav: caching request URI maping info (path for pre IIS 6.0):\n"
  345. " URL \"%hs\" maps to \"%hs\"\n"
  346. " dwFlags = 0x%08x\n"
  347. " cchMatchingPath = %d\n"
  348. " cchMatchingURL = %d\n",
  349. LpszRequestUrl(),
  350. mi.lpszPath,
  351. mi.dwFlags,
  352. mi.cchMatchingPath,
  353. mi.cchMatchingURL);
  354. // Adjust the matching URL ...
  355. //
  356. if ( mi.cchMatchingURL )
  357. {
  358. LPCSTR psz = LpszRequestUrl() + mi.cchMatchingURL - 1;
  359. // ... do not include the trailing slash, if any...
  360. //
  361. if ( '/' == *psz )
  362. {
  363. //$ RAID: NT:359868
  364. //
  365. // This is the first of many places where we need to be very
  366. // careful with our usage of single character checks. Namely,
  367. // in DBCS land, we need to check for lead bytes before treating
  368. // the last char as if it is a slash.
  369. //
  370. if (!FIsDBCSTrailingByte (psz, mi.cchMatchingURL))
  371. mi.cchMatchingURL -= 1;
  372. //
  373. //$ RAID: end.
  374. }
  375. // ... also we found a case (INDEX on the vroot) where the
  376. // cchMatching... points to the '\0' (where a trailing slash
  377. // would be IF DAV methods required a trailing slash). So,
  378. // also chop off any trailing '\0' here! --BeckyAn 21Aug1997
  379. //
  380. else if ( '\0' == *psz )
  381. {
  382. mi.cchMatchingURL -= 1;
  383. }
  384. }
  385. // Cache the vroot data.
  386. // Corollary: m_cchVrootW should always be > 0 when we have data.
  387. //
  388. m_cchVroot = mi.cchMatchingURL + 1;
  389. m_rgchVroot = reinterpret_cast<LPSTR>(m_sb.Alloc(m_cchVroot));
  390. memcpy (m_rgchVroot, LpszRequestUrl(), m_cchVroot);
  391. m_rgchVroot[m_cchVroot - 1] = '\0';
  392. // Adjust the matching path the same way as we did maching URL
  393. //
  394. if ( mi.cchMatchingPath )
  395. {
  396. LPCSTR psz = mi.lpszPath + mi.cchMatchingPath - 1;
  397. if ( '\\' == *psz )
  398. {
  399. //$ RAID: NT:359868
  400. //
  401. // This is the second of many places where we need to be very
  402. // careful with our usage of single character checks. Namely,
  403. // in DBCS land, we need to check for lead bytes before treating
  404. // the last char as if it is a backslash.
  405. //
  406. while ((0 < mi.cchMatchingPath) &&
  407. ('\\' == *psz) &&
  408. (!FIsDBCSTrailingByte (psz, mi.cchMatchingPath)) &&
  409. (!FIsDriveTrailingChar (psz, mi.cchMatchingPath)))
  410. {
  411. mi.cchMatchingPath--;
  412. psz--;
  413. }
  414. //
  415. //$ RAID: end.
  416. }
  417. else if ( '\0' == *psz )
  418. {
  419. mi.cchMatchingPath--;
  420. }
  421. }
  422. // Cache the matching path data.
  423. // Corollary: m_cchVrootPath should always be > 0 when we have data.
  424. //
  425. m_cchVrootPath = mi.cchMatchingPath + 1;
  426. m_rgchVrootPath = reinterpret_cast<LPSTR>(m_sb.Alloc(m_cchVrootPath));
  427. memcpy (m_rgchVrootPath, mi.lpszPath, mi.cchMatchingPath);
  428. m_rgchVrootPath[mi.cchMatchingPath] = '\0';
  429. }
  430. }
  431. // ------------------------------------------------------------------------
  432. //
  433. // CEcbBaseImpl::GetMapExInfo()
  434. //
  435. template<class _T>
  436. VOID CEcbBaseImpl<_T>::GetMapExInfo() const
  437. {
  438. if ( m_pecb->dwVersion >= IIS_VERSION_6_0 )
  439. {
  440. GetMapExInfo60After();
  441. }
  442. else
  443. {
  444. GetMapExInfo60Before();
  445. }
  446. }
  447. // ------------------------------------------------------------------------
  448. //
  449. // FGetServerVariable()
  450. //
  451. // Get the value of an ECB variable (e.g. "SERVER_NAME")
  452. //
  453. template<class _T>
  454. BOOL
  455. CEcbBaseImpl<_T>::FGetServerVariable( LPCSTR pszName, LPSTR pszValue,
  456. DWORD * pcbValue ) const
  457. {
  458. BOOL fResult = FALSE;
  459. Assert( m_pecb );
  460. Assert( !IsBadWritePtr( pcbValue, sizeof(DWORD) ) );
  461. Assert( *pcbValue > 0 );
  462. Assert( !IsBadWritePtr( pszValue, *pcbValue ) );
  463. if ( m_pecb->GetServerVariable( m_pecb->ConnID,
  464. const_cast<LPSTR>(pszName),
  465. pszValue,
  466. pcbValue ) )
  467. {
  468. fResult = TRUE;
  469. }
  470. else if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
  471. {
  472. AssertSz( GetLastError() == ERROR_INVALID_INDEX, "Unexpected last error from GetServerVariable()\n" );
  473. *pcbValue = 0;
  474. }
  475. return fResult;
  476. }
  477. template<class _T>
  478. BOOL
  479. CEcbBaseImpl<_T>::FGetServerVariable( LPCSTR pszName, LPWSTR pwszValue,
  480. DWORD * pcchValue ) const
  481. {
  482. BOOL fResult = FALSE;
  483. CStackBuffer<CHAR> pszValue;
  484. DWORD cbValue;
  485. UINT cch;
  486. Assert( m_pecb );
  487. Assert( !IsBadWritePtr( pcchValue, sizeof(DWORD) ) );
  488. Assert( *pcchValue > 0 );
  489. Assert( !IsBadWritePtr( pwszValue, *pcchValue * sizeof(WCHAR) ) );
  490. // Assume that 1 wide character can be made of 3 skinny ones,
  491. // which may be true in the case codepage is CP_UTF8
  492. //
  493. cbValue = *pcchValue * 3;
  494. if (NULL != pszValue.resize(cbValue))
  495. {
  496. if ( m_pecb->GetServerVariable( m_pecb->ConnID,
  497. const_cast<LPSTR>(pszName),
  498. pszValue.get(),
  499. &cbValue ) )
  500. {
  501. fResult = TRUE;
  502. }
  503. else if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
  504. {
  505. if (NULL != pszValue.resize(cbValue))
  506. {
  507. if ( m_pecb->GetServerVariable( m_pecb->ConnID,
  508. const_cast<LPSTR>(pszName),
  509. pszValue.get(),
  510. &cbValue ) )
  511. {
  512. fResult = TRUE;
  513. }
  514. }
  515. }
  516. }
  517. // By now we should be succesfull in geting data as the buffer provided
  518. // was big enough
  519. //
  520. if (FALSE == fResult)
  521. {
  522. EcbTrace( "Dav: CEcbBaseImpl<_T>::FGetServerVariable(). Error 0x%08lX from GetServerVariable()\n", GetLastError() );
  523. *pcchValue = 0;
  524. goto ret;
  525. }
  526. // We have the data, need to convert it to wide version, assume we will fail
  527. //
  528. fResult = FALSE;
  529. cch = MultiByteToWideChar(CP_ACP,
  530. MB_ERR_INVALID_CHARS,
  531. pszValue.get(),
  532. cbValue,
  533. pwszValue,
  534. *pcchValue);
  535. if (0 == cch)
  536. {
  537. // The function failed...
  538. //
  539. if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
  540. {
  541. // ... figure out the necessary size for the buffer
  542. //
  543. cch = MultiByteToWideChar(CP_ACP,
  544. MB_ERR_INVALID_CHARS,
  545. pszValue.get(),
  546. cbValue,
  547. NULL,
  548. 0);
  549. if (0 == cch)
  550. {
  551. // We still failed
  552. //
  553. AssertSz( ERROR_INSUFFICIENT_BUFFER != GetLastError(), "We should not fail with ERROR_INSUFFICIENT BUFFER here.\n" );
  554. EcbTrace( "Dav: CEcbBaseImpl<_T>::FGetServerVariable(). Error 0x%08lX from MultiByteToWideChar() "
  555. "while trying to find sufficient length for conversion.\n", GetLastError() );
  556. *pcchValue = 0;
  557. goto ret;
  558. }
  559. }
  560. else
  561. {
  562. // ... failure was fatal
  563. //
  564. EcbTrace( "Dav: CEcbBaseImpl<_T>::FGetServerVariable(), Error 0x%08lX from MultiByteToWideChar() "
  565. "while trying to convert.\n", GetLastError() );
  566. *pcchValue = 0;
  567. goto ret;
  568. }
  569. }
  570. *pcchValue = cch;
  571. fResult = TRUE;
  572. ret:
  573. return fResult;
  574. }
  575. // ------------------------------------------------------------------------
  576. //
  577. // CEcbBaseImpl::CchGetVirtualRoot
  578. //
  579. // Fetch and cache the vroot information.
  580. //
  581. template<class _T>
  582. UINT CEcbBaseImpl<_T>::CchGetVirtualRoot( LPCSTR * ppszVroot ) const
  583. {
  584. // Tidiness. If we fail, want to return a NULL.
  585. // (We return zero for the cch if we fail.)
  586. // Pre-set it here.
  587. //
  588. Assert( ppszVroot );
  589. *ppszVroot = NULL;
  590. // Check if we have cached vroot data.
  591. //
  592. GetMapExInfo();
  593. // If the skinny version of vroot is not available generate
  594. // an cache one
  595. //
  596. if (NULL == m_rgchVroot)
  597. {
  598. // We got the maping info, so if the skinny version of the
  599. // vroot was not available, then at least wide will be there.
  600. // That would meen that we are on IIS 6.0 or later. Also we
  601. // should have 0 bytes for skinny vroot at this point.
  602. //
  603. Assert(m_rgwchVroot);
  604. Assert(m_pecb->dwVersion >= IIS_VERSION_6_0);
  605. Assert(0 == m_cchVroot);
  606. UINT cb = m_cchVrootW * 3;
  607. m_rgchVroot = reinterpret_cast<LPSTR>(m_sb.Alloc(cb));
  608. m_cchVroot = WideCharToMultiByte ( CP_ACP,
  609. 0,
  610. m_rgwchVroot,
  611. m_cchVrootW,
  612. m_rgchVroot,
  613. cb,
  614. 0,
  615. 0 );
  616. if (0 == m_cchVroot)
  617. {
  618. DebugTrace ("Dav: CEcbBaseImpl::CchGetVirtualRoot failed(%ld)\n", GetLastError());
  619. throw CLastErrorException();
  620. }
  621. }
  622. // Give the data back to the caller from our cache.
  623. //
  624. *ppszVroot = m_rgchVroot;
  625. return m_cchVroot - 1;
  626. }
  627. // ------------------------------------------------------------------------
  628. //
  629. // CEcbBaseImpl::CchGetVirtualRootW
  630. //
  631. // Fetch and cache the vroot information.
  632. //
  633. template<class _T>
  634. UINT CEcbBaseImpl<_T>::CchGetVirtualRootW( LPCWSTR * ppwszVroot ) const
  635. {
  636. // Tidiness. If we fail, want to return a NULL.
  637. // (We return zero for the cch if we fail.)
  638. // Pre-set it here.
  639. //
  640. Assert( ppwszVroot );
  641. *ppwszVroot = NULL;
  642. // Check if we have cached vroot data.
  643. //
  644. GetMapExInfo();
  645. // If the wide version of vroot is not available generate
  646. // an cache one
  647. //
  648. if (NULL == m_rgwchVroot)
  649. {
  650. // We got the maping info, so if the wide version of the
  651. // vroot was not available, then at least wide will be there.
  652. // That would meen that we are on pre IIS 6.0 version. Also we
  653. // should have 0 bytes for wide vroot at this point.
  654. //
  655. Assert(m_rgchVroot);
  656. Assert(m_pecb->dwVersion < IIS_VERSION_6_0);
  657. Assert(0 == m_cchVrootW);
  658. UINT cb = m_cchVroot * sizeof(WCHAR);
  659. m_rgwchVroot = reinterpret_cast<LPWSTR>(m_sb.Alloc(cb));
  660. m_cchVrootW = MultiByteToWideChar ( CP_ACP,
  661. MB_ERR_INVALID_CHARS,
  662. m_rgchVroot,
  663. m_cchVroot,
  664. m_rgwchVroot,
  665. m_cchVroot);
  666. if (0 == m_cchVrootW)
  667. {
  668. DebugTrace ("Dav: CEcbBaseImpl::CchGetVirtualRootW failed(%ld)\n", GetLastError());
  669. throw CLastErrorException();
  670. }
  671. }
  672. // Give the data back to the caller from our cache.
  673. //
  674. *ppwszVroot = m_rgwchVroot;
  675. return m_cchVrootW - 1;
  676. }
  677. // ------------------------------------------------------------------------
  678. //
  679. // CEcbBaseImpl::CchGetMatchingPathW
  680. //
  681. // Fetch and cache the matching path information.
  682. //
  683. template<class _T>
  684. UINT CEcbBaseImpl<_T>::CchGetMatchingPathW( LPCWSTR * ppwszPath ) const
  685. {
  686. // Tidiness. If we fail, want to return a NULL.
  687. // (We return zero for the cch if we fail.)
  688. // Pre-set it here.
  689. //
  690. Assert( ppwszPath );
  691. *ppwszPath = NULL;
  692. // Check if we have cached vroot data.
  693. //
  694. GetMapExInfo();
  695. // Give the data back to the caller from our cache.
  696. //
  697. if (NULL == m_rgwchVrootPath)
  698. {
  699. // We got the maping info, so if the wide version of the
  700. // matching path was not available, then at least skinny will
  701. // be there. That would meen that we are on pre IIS 6.0 version.
  702. // Also we should have 0 bytes for wide matching path at
  703. // this point.
  704. //
  705. Assert(m_rgchVrootPath);
  706. Assert(m_pecb->dwVersion < IIS_VERSION_6_0);
  707. Assert(0 == m_cchVrootPathW);
  708. UINT cb = m_cchVrootPath * sizeof(WCHAR);
  709. m_rgwchVrootPath = reinterpret_cast<LPWSTR>(m_sb.Alloc(cb));
  710. m_cchVrootPathW = MultiByteToWideChar ( CP_ACP,
  711. MB_ERR_INVALID_CHARS,
  712. m_rgchVrootPath,
  713. m_cchVrootPath,
  714. m_rgwchVrootPath,
  715. m_cchVrootPath);
  716. if (0 == m_cchVrootPathW)
  717. {
  718. DebugTrace ("Dav: CEcbBaseImpl::CchGetMatchingPathW failed(%ld)\n", GetLastError());
  719. throw CLastErrorException();
  720. }
  721. }
  722. // Give the data back to the caller from our cache.
  723. //
  724. *ppwszPath = m_rgwchVrootPath;
  725. return m_cchVrootPathW - 1;
  726. }
  727. // ------------------------------------------------------------------------
  728. //
  729. // CEcbBaseImpl::CchGetServerName
  730. //
  731. // Fetch and cache the server name, including port number
  732. //
  733. template<class _T>
  734. UINT
  735. CEcbBaseImpl<_T>::CchGetServerName( LPCSTR* ppszServer ) const
  736. {
  737. if ( !m_lpszServerName )
  738. {
  739. DWORD cbName;
  740. DWORD cbPort;
  741. CStackBuffer<CHAR> lpszName;
  742. CStackBuffer<CHAR> lpszPort;
  743. cbName = lpszName.celems();
  744. for ( ;; )
  745. {
  746. DWORD cbCur = cbName;
  747. lpszName.resize(cbName);
  748. if ( FGetServerVariable( gc_szServer_Name,
  749. lpszName.get(),
  750. &cbName ) )
  751. {
  752. break;
  753. }
  754. if ( cbName == 0 )
  755. {
  756. lpszName[0] = '\0';
  757. ++cbName;
  758. break;
  759. }
  760. // If the size was big enough but we still failed, we
  761. // are probably out of memory.
  762. //
  763. if (cbName && (cbCur >= cbName))
  764. {
  765. throw CHresultException(E_OUTOFMEMORY);
  766. }
  767. }
  768. cbPort = lpszPort.celems();
  769. for ( ;; )
  770. {
  771. lpszPort.resize(cbPort);
  772. if ( FGetServerVariable( gc_szServer_Port,
  773. lpszPort.get(),
  774. &cbPort ) )
  775. {
  776. break;
  777. }
  778. if ( cbPort == 0 )
  779. {
  780. lpszPort[0] = '\0';
  781. ++cbPort;
  782. break;
  783. }
  784. else
  785. {
  786. // We should not have "PORT" values greater
  787. // than 4 digits anyway....
  788. //
  789. throw CHresultException(E_INVALIDARG);
  790. }
  791. }
  792. // Limit the servname/port combination to 256 (including NULL)
  793. //
  794. if (256 < (cbName + cbPort))
  795. {
  796. throw CHresultException(E_INVALIDARG);
  797. }
  798. // Allocate enough space for the server name and port plus
  799. // a ':' separator. Note that the ':' replaces the '\0' at
  800. // the end of the name, so we don't need to add 1 for it here.
  801. //
  802. m_lpszServerName = reinterpret_cast<LPSTR>(m_sb.Alloc(cbName + cbPort));
  803. // Format the whole thing as "<name>[:<port>]" where
  804. // :<port> is only included if the port is not the default
  805. // port for the connection (:443 for SSL or :80 for standard)
  806. //
  807. CopyMemory( m_lpszServerName,
  808. lpszName.get(),
  809. cbName );
  810. // If we are secure and the port is "443", or if the port is
  811. // the default one, then there is no need to append the port
  812. // number to the server name.
  813. //
  814. if (( FSsl() && !strcmp( lpszPort.get(), gc_sz443 )) ||
  815. !strcmp( lpszPort.get(), gc_sz80 ))
  816. {
  817. // It was easier to write the conditional this way and
  818. // have the real work done in the "else" clause.
  819. //
  820. }
  821. else
  822. {
  823. // Append the port to the server name
  824. //
  825. m_lpszServerName[cbName-1] = ':';
  826. CopyMemory( m_lpszServerName + cbName,
  827. lpszPort.get(),
  828. cbPort );
  829. }
  830. m_cchServerName = static_cast<UINT>(strlen(m_lpszServerName));
  831. }
  832. *ppszServer = m_lpszServerName;
  833. return m_cchServerName;
  834. }
  835. // ------------------------------------------------------------------------
  836. //
  837. // CEcbBaseImpl::CchGetServerNameW
  838. //
  839. // Fetch and cache the server name, including port number
  840. //
  841. template<class _T>
  842. UINT
  843. CEcbBaseImpl<_T>::CchGetServerNameW( LPCWSTR* ppwszServer ) const
  844. {
  845. if ( !m_pwszServerName )
  846. {
  847. // Fetch the server name and include 0 termination in its length
  848. //
  849. LPCSTR pszServerName = NULL;
  850. UINT cbServerName = CchGetServerName(&pszServerName) + 1;
  851. // We are looking for the wide server name for the first time.
  852. // The character count should be zero at that point.
  853. //
  854. Assert(!m_cchServerNameW);
  855. UINT cb = cbServerName * sizeof(WCHAR);
  856. m_pwszServerName = reinterpret_cast<LPWSTR>(m_sb.Alloc(cb));
  857. m_cchServerNameW = MultiByteToWideChar ( CP_ACP,
  858. MB_ERR_INVALID_CHARS,
  859. pszServerName,
  860. cbServerName,
  861. m_pwszServerName,
  862. cbServerName);
  863. if (0 == m_cchServerNameW)
  864. {
  865. DebugTrace ("Dav: CEcbBaseImpl::CchGetServerNameW failed(%ld)\n", GetLastError());
  866. throw CLastErrorException();
  867. }
  868. // Subtract 0 termination so that we would behave the same way as
  869. // the skinny version of the function
  870. //
  871. m_cchServerNameW--;
  872. }
  873. *ppwszServer = m_pwszServerName;
  874. return m_cchServerNameW;
  875. }
  876. // ------------------------------------------------------------------------
  877. //
  878. // CEcbBaseImpl::LpszUrlPrefix
  879. //
  880. // Fetch and cache the url prefix
  881. //
  882. extern const __declspec(selectany) CHAR gsc_szHTTPS[] = "HTTPS";
  883. extern const __declspec(selectany) CHAR gsc_szFrontEndHTTPS[] = "HTTP_FRONT_END_HTTPS";
  884. extern const __declspec(selectany) CHAR gsc_szOn[] = "on";
  885. template<class _T>
  886. BOOL
  887. CEcbBaseImpl<_T>::FSsl() const
  888. {
  889. if (m_secure == HTTPS_UNKNOWN)
  890. {
  891. // Start out believing that we are not in a secure environment
  892. //
  893. m_secure = NORMAL;
  894. // We want to ask the ECB for the server variables that indicate
  895. // whether or not the constructed urls should be secured or not.
  896. //
  897. // In the case of a FE/BE topology, the FE will include a header
  898. // "Front-End-HTTPS" that indicates whether or not the FE/BE was
  899. // secured via SSL. In the absence of the header, we should try
  900. // to fallback to the IIS "HTTPS" header. For either header, we
  901. // we check its value -- in both cases, it should either be "on"
  902. // or "off"
  903. //
  904. // IMPORTANT: you have to check for the FE entry first! That is
  905. // the overriding value for this configuration.
  906. //
  907. CHAR szHttps[8];
  908. ULONG cb = sizeof(szHttps);
  909. ULONG cbFE = sizeof(szHttps);
  910. m_fFESecured = FGetServerVariable (gsc_szFrontEndHTTPS, szHttps, &cbFE);
  911. if (m_fFESecured || FGetServerVariable (gsc_szHTTPS, szHttps, &cb))
  912. {
  913. if (!_stricmp(szHttps, gsc_szOn))
  914. m_secure = SECURE;
  915. }
  916. }
  917. return (SECURE == m_secure);
  918. }
  919. template<class _T>
  920. LPCSTR
  921. CEcbBaseImpl<_T>::LpszUrlPrefix() const
  922. {
  923. return (FSsl() ? gc_szUrl_Prefix_Secure : gc_szUrl_Prefix);
  924. }
  925. template<class _T>
  926. LPCWSTR
  927. CEcbBaseImpl<_T>::LpwszUrlPrefix() const
  928. {
  929. return (FSsl() ? gc_wszUrl_Prefix_Secure : gc_wszUrl_Prefix);
  930. }
  931. template<class _T>
  932. UINT
  933. CEcbBaseImpl<_T>::CchUrlPrefix( LPCSTR * ppszPrefix ) const
  934. {
  935. // Make sure that we know which prefix we are working with...
  936. //
  937. LPCSTR psz = LpszUrlPrefix();
  938. // If the caller wants the pointer, too, give it to 'em
  939. //
  940. if (ppszPrefix)
  941. *ppszPrefix = psz;
  942. // Return the appropriate size
  943. //
  944. return ((m_secure == SECURE)
  945. ? gc_cchszUrl_Prefix_Secure
  946. : gc_cchszUrl_Prefix);
  947. }
  948. template<class _T>
  949. UINT
  950. CEcbBaseImpl<_T>::CchUrlPrefixW( LPCWSTR * ppwszPrefix ) const
  951. {
  952. // Make sure that we know which prefix we are working with...
  953. //
  954. LPCWSTR pwsz = LpwszUrlPrefix();
  955. // If the caller wants the pointer, too, give it to 'em
  956. //
  957. if (ppwszPrefix)
  958. *ppwszPrefix = pwsz;
  959. // Return the appropriate size
  960. //
  961. return ((m_secure == SECURE)
  962. ? gc_cchszUrl_Prefix_Secure
  963. : gc_cchszUrl_Prefix);
  964. }
  965. // ------------------------------------------------------------------------
  966. //
  967. // CEcbBaseImpl::LpszRequestUrl()
  968. //
  969. template<class _T>
  970. LPCWSTR
  971. CEcbBaseImpl<_T>::LpwszRequestUrl() const
  972. {
  973. if (!m_pwszRequestUrl)
  974. {
  975. SCODE sc;
  976. CStackBuffer<CHAR> pszAcceptLanguage;
  977. CStackBuffer<CHAR> pszRawUrlCopy;
  978. LPCSTR pszQueryStringStart = NULL;
  979. LPCSTR pszRawUrl = NULL;
  980. UINT cbRawUrl = 0;
  981. UINT cchRequestUrl = 0;
  982. // Grab the raw URL.
  983. //
  984. cbRawUrl = CbGetRawURL(&pszRawUrl);
  985. // We also need to cut off the URL at the beginning of the query
  986. // string, if there is one.
  987. //
  988. pszQueryStringStart = strchr(pszRawUrl, '?');
  989. if (pszQueryStringStart)
  990. {
  991. // If there is a query string we need to make a copy of
  992. // the raw URL to work with.
  993. //
  994. cbRawUrl = static_cast<UINT>(pszQueryStringStart - pszRawUrl);
  995. // Allocate a buffer, and copy it in!
  996. //
  997. pszRawUrlCopy.resize(cbRawUrl + 1);
  998. memcpy(pszRawUrlCopy.get(), pszRawUrl, cbRawUrl);
  999. pszRawUrlCopy[cbRawUrl] = '\0';
  1000. // Now set up to normalize from this copy. Do not forget
  1001. // to increment cbRawUrl to include '\0' termination.
  1002. //
  1003. cbRawUrl++;
  1004. pszRawUrl = pszRawUrlCopy.get();
  1005. }
  1006. // Before normalizing the url, get the Accept-Language: header
  1007. // to pass it in. This will be used in figuring out the correct
  1008. // code page to use to decode non-UTF8 urls.
  1009. //
  1010. for ( DWORD cbValue = 256; cbValue > 0; )
  1011. {
  1012. DWORD cbCur = cbValue;
  1013. pszAcceptLanguage.resize(cbValue);
  1014. // Zero the string.
  1015. //
  1016. pszAcceptLanguage[0] = '\0';
  1017. // Get the header value
  1018. //
  1019. if ( FGetServerVariable( "HTTP_ACCEPT_LANGUAGE",
  1020. pszAcceptLanguage.get(),
  1021. &cbValue ) )
  1022. {
  1023. break;
  1024. }
  1025. // If the size needed is the same as the size returned, but the
  1026. // header retrieval still fails, then we are probably out of memory.
  1027. //
  1028. if (cbValue && (cbCur >= cbValue))
  1029. {
  1030. throw CHresultException(E_OUTOFMEMORY);
  1031. }
  1032. }
  1033. // Now, normalize the URL and we're done
  1034. //
  1035. cchRequestUrl = cbRawUrl;
  1036. m_pwszRequestUrl = reinterpret_cast<LPWSTR>(m_sb.Alloc (cchRequestUrl * sizeof(WCHAR)));
  1037. sc = ScNormalizeUrl(pszRawUrl,
  1038. &cchRequestUrl,
  1039. m_pwszRequestUrl,
  1040. pszAcceptLanguage.get());
  1041. if (S_OK != sc)
  1042. {
  1043. // We should never get S_FALSE here, since we've passed enough buffer space.
  1044. // Most often callers of this function assume that it cannot return NULL,
  1045. // and on the other hand we cannot do anything without request URL. Thus
  1046. // throw the last error exception.
  1047. //
  1048. Assert(S_FALSE != sc);
  1049. DebugTrace("CEcbBaseImpl::LpwszRequestUrl() - ScNormalizeUrl() failed with error 0x%08lX\n", sc);
  1050. SetLastError(sc);
  1051. throw CLastErrorException();
  1052. }
  1053. // Store the pointer to stripped request URL
  1054. //
  1055. m_pwszRequestUrl = const_cast<LPWSTR>(PwszUrlStrippedOfPrefix(m_pwszRequestUrl));
  1056. }
  1057. return m_pwszRequestUrl;
  1058. }
  1059. // ------------------------------------------------------------------------
  1060. //
  1061. // CEcbBaseImpl::LpszRequestUrl()
  1062. //
  1063. template<class _T>
  1064. LPCSTR
  1065. CEcbBaseImpl<_T>::LpszRequestUrl() const
  1066. {
  1067. if (!m_pszRequestUrl)
  1068. {
  1069. LPCWSTR pwszRequestUrl;
  1070. UINT cbRequestUrl;
  1071. UINT cchRequestUrl;
  1072. pwszRequestUrl = LpwszRequestUrl();
  1073. cchRequestUrl = static_cast<UINT>(wcslen(pwszRequestUrl));
  1074. cbRequestUrl = cchRequestUrl * 3;
  1075. m_pszRequestUrl = reinterpret_cast<LPSTR>(m_sb.Alloc (cbRequestUrl + 1));
  1076. // The reason for choosing CP_ACP codepage here is that it matches
  1077. // the old behaviour.
  1078. //
  1079. cbRequestUrl = WideCharToMultiByte(CP_ACP,
  1080. 0,
  1081. pwszRequestUrl,
  1082. cchRequestUrl + 1,
  1083. m_pszRequestUrl,
  1084. cbRequestUrl + 1,
  1085. NULL,
  1086. NULL);
  1087. if (0 == cbRequestUrl)
  1088. {
  1089. DebugTrace( "CEcbBaseImpl::LpszRequestUrl() - WideCharToMultiByte() failed 0x%08lX\n",
  1090. HRESULT_FROM_WIN32(GetLastError()) );
  1091. throw CLastErrorException();
  1092. }
  1093. }
  1094. return m_pszRequestUrl;
  1095. }
  1096. // ------------------------------------------------------------------------
  1097. //
  1098. // CEcbBaseImpl::LpwszRequestUrl()
  1099. //
  1100. template<class _T>
  1101. LPCWSTR
  1102. CEcbBaseImpl<_T>::LpwszMethod() const
  1103. {
  1104. if (!m_pwszMethod)
  1105. {
  1106. LPCSTR pszMethod;
  1107. UINT cbMethod;
  1108. UINT cchMethod;
  1109. pszMethod = LpszMethod();
  1110. cbMethod = static_cast<UINT>(strlen(pszMethod));
  1111. m_pwszMethod = reinterpret_cast<LPWSTR>(
  1112. m_sb.Alloc (CbSizeWsz(cbMethod)));
  1113. cchMethod = MultiByteToWideChar(CP_ACP,
  1114. MB_ERR_INVALID_CHARS,
  1115. pszMethod,
  1116. cbMethod + 1,
  1117. m_pwszMethod,
  1118. cbMethod + 1);
  1119. if (0 == cchMethod)
  1120. {
  1121. DebugTrace( "CEcbBaseImpl::LpwszRequestUrl() - MultiByteToWideChar() failed 0x%08lX\n",
  1122. HRESULT_FROM_WIN32(GetLastError()) );
  1123. throw CLastErrorException();
  1124. }
  1125. }
  1126. return m_pwszMethod;
  1127. }
  1128. // ------------------------------------------------------------------------
  1129. //
  1130. // CEcbBaseImpl::LpwszPathTranslated()
  1131. //
  1132. template<class _T>
  1133. LPCWSTR
  1134. CEcbBaseImpl<_T>::LpwszPathTranslated() const
  1135. {
  1136. // Cache the path info in the first call
  1137. //
  1138. if (!m_pwszPathTranslated)
  1139. {
  1140. LPCWSTR pwszRequestUrl;
  1141. UINT cchRequestUrl;
  1142. LPCWSTR pwszMatching;
  1143. UINT cchMatching;
  1144. LPCWSTR pwszVroot;
  1145. UINT cchVroot;
  1146. UINT cchPathTranslated;
  1147. // Grab the request URL.
  1148. //
  1149. pwszRequestUrl = LpwszRequestUrl();
  1150. cchRequestUrl = static_cast<UINT>(wcslen(pwszRequestUrl));
  1151. // Grab the matching path information.
  1152. //
  1153. pwszMatching = NULL;
  1154. cchMatching = CchGetMatchingPathW(&pwszMatching);
  1155. // Grab the virtual root information.
  1156. //
  1157. pwszVroot = NULL;
  1158. cchVroot = CchGetVirtualRootW(&pwszVroot);
  1159. // Move the request URL pointer over to snip off the virtual root.
  1160. //
  1161. pwszRequestUrl += cchVroot;
  1162. cchRequestUrl -= cchVroot;
  1163. // Allocate enough space for the matching path and the request URL, and
  1164. // copy the pieces in.
  1165. //
  1166. m_pwszPathTranslated = reinterpret_cast<LPWSTR>(
  1167. m_sb.Alloc (CbSizeWsz(cchMatching + cchRequestUrl)));
  1168. // Copy the matching path.
  1169. //
  1170. memcpy (m_pwszPathTranslated, pwszMatching, cchMatching * sizeof(WCHAR));
  1171. // Copy the request URL after the vroot, including '\0' termination
  1172. //
  1173. memcpy (m_pwszPathTranslated + cchMatching, pwszRequestUrl, (cchRequestUrl + 1) * sizeof(WCHAR));
  1174. // Change all '/' that came from URL to '\\'
  1175. //
  1176. for (LPWSTR pwch = m_pwszPathTranslated + cchMatching; *pwch; pwch++)
  1177. {
  1178. if (L'/' == *pwch)
  1179. {
  1180. *pwch = L'\\';
  1181. }
  1182. }
  1183. // We must remove all trailing slashes, in case the path is not empty string
  1184. //
  1185. cchPathTranslated = cchMatching + cchRequestUrl;
  1186. if (0 < cchPathTranslated)
  1187. {
  1188. LPWSTR pwszTrailing = m_pwszPathTranslated + cchPathTranslated - 1;
  1189. // Since URL is normalized there may be not more than one trailing slash.
  1190. // We check only for backslash, as we already changed all forward slashes
  1191. // to backslashes. Also do not remove trailing slash for the root of the
  1192. // drive.
  1193. //
  1194. if ((L'\\' == *pwszTrailing) &&
  1195. (!FIsDriveTrailingChar(pwszTrailing, cchPathTranslated)))
  1196. {
  1197. cchPathTranslated--;
  1198. *pwszTrailing = L'\0';
  1199. }
  1200. }
  1201. }
  1202. return m_pwszPathTranslated;
  1203. }
  1204. // ------------------------------------------------------------------------
  1205. //
  1206. // CEcbBaseImpl::CbGetRawURL
  1207. //
  1208. // Fetch and cache the raw URL
  1209. //
  1210. template<class _T>
  1211. UINT
  1212. CEcbBaseImpl<_T>::CbGetRawURL (LPCSTR* ppszRawURL) const
  1213. {
  1214. if (!m_pszRawURL)
  1215. {
  1216. DWORD cbRawURL;
  1217. CStackBuffer<CHAR,MAX_PATH> pszRawURL;
  1218. cbRawURL = pszRawURL.size();
  1219. for ( ;; )
  1220. {
  1221. DWORD cbCur = cbRawURL;
  1222. pszRawURL.resize(cbRawURL);
  1223. if (FGetServerVariable ("UNENCODED_URL",
  1224. pszRawURL.get(),
  1225. &cbRawURL))
  1226. {
  1227. break;
  1228. }
  1229. if (cbRawURL == 0)
  1230. {
  1231. pszRawURL[0] = '\0';
  1232. cbRawURL++;
  1233. break;
  1234. }
  1235. // If the size needed is the same as the size returned, but the
  1236. // header retrieval still fails, then we are probably out of memory.
  1237. //
  1238. if (cbCur >= cbRawURL)
  1239. {
  1240. throw CHresultException(E_OUTOFMEMORY);
  1241. }
  1242. }
  1243. Assert ('\0' == pszRawURL[cbRawURL - 1]);
  1244. UrlTrace("CEcbBaseImpl::CbGetRawURL(): Raw URL = %s\n", pszRawURL.get());
  1245. // Copy the data to our object.
  1246. //
  1247. m_pszRawURL = reinterpret_cast<LPSTR>(m_sb.Alloc (cbRawURL));
  1248. memcpy (m_pszRawURL, pszRawURL.get(), cbRawURL);
  1249. m_cbRawURL = cbRawURL;
  1250. }
  1251. // Return the cached values to the caller.
  1252. //
  1253. *ppszRawURL = m_pszRawURL;
  1254. return m_cbRawURL;
  1255. }
  1256. // ------------------------------------------------------------------------
  1257. //
  1258. // CEcbBaseImpl::LcidAccepted()
  1259. //
  1260. // Fetch, cache, and return the LCID of the accepted language
  1261. // based on the value of the Accept-Language header (if any).
  1262. //
  1263. // The default LCID is the special constant referring to the
  1264. // default system locale.
  1265. //
  1266. //$REVIEW$
  1267. // The const at the end of the function needs to be removed
  1268. //
  1269. template<class _T>
  1270. ULONG
  1271. CEcbBaseImpl<_T>::LcidAccepted() const
  1272. {
  1273. if ( !m_lcid )
  1274. {
  1275. LPCSTR psz = m_pszAcceptLanguage.get();
  1276. HDRITER hdri(psz);
  1277. ULONG lcid;
  1278. m_lcid = LOCALE_NEUTRAL; // must be the same as lcidDefault in mdbeif.hxx
  1279. for (psz = hdri.PszNext(); psz; psz = hdri.PszNext())
  1280. if (FLookupLCID(psz, &lcid))
  1281. {
  1282. m_lcid = LANGIDFROMLCID(lcid);
  1283. break;
  1284. }
  1285. }
  1286. return m_lcid;
  1287. }
  1288. // ------------------------------------------------------------------------
  1289. //
  1290. // CEcbBaseImpl::SetLcidAccepted
  1291. //
  1292. // Sets the LCID for the request. We call this function when
  1293. // we dont have an AcceptLang header and we want to override
  1294. // the default LCID (with the LCID in the Cookie)
  1295. //
  1296. //$REVIEW$
  1297. // After RTM, this function should be merged with the LcidAccepted() function.
  1298. // The LcidAccepted() should check AcceptLang header and if it is not present,
  1299. // should check the lcid in the cookie.
  1300. //
  1301. template<class _T>
  1302. VOID
  1303. CEcbBaseImpl<_T>::SetLcidAccepted(LCID lcid)
  1304. {
  1305. m_lcid = lcid;
  1306. }
  1307. #endif