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.

1444 lines
38 KiB

  1. // ========================================================================
  2. // H T T P E X T \ U R L M A P . C P P
  3. //
  4. // Copyright Microsoft Corporation 1997-1999.
  5. //
  6. // This file contains all necessary routines to deal with IIS URLs
  7. // properly. This file is part of HTTPEXT, as in HTTPEXT, we need to
  8. // handle URLs the same way IIS would.
  9. //
  10. // ========================================================================
  11. #include <_davfs.h>
  12. #include <langtocpid.h>
  13. //$ REVIEW: BUG:NT5:196814
  14. //
  15. // <string.hxx> is an IIS header file that exposes the CanonURL() api.
  16. // It is exported from IISRTL.DLL and we should be able to call it
  17. // instead of us stealing their code.
  18. //
  19. //$ HACK:
  20. //
  21. // <string.hxx> includes <buffer.hxx> which includes <nt.h> and all of
  22. // its minions. DAV has already included all of the <winnt.h> and its
  23. // minions. The <nt.h> and <winnt.h> are at odds, so we are defining
  24. // NT_INCLUDED, _NTRTL_, _NTURTL_, DBG_ASSERT(), IntializeListHead(),
  25. // and RemoveEntryList() to disable those conflicts.
  26. //
  27. #define NT_INCLUDED
  28. #define _NTRTL_
  29. #define _NTURTL_
  30. #define InitializeListHead(_p)
  31. #define RemoveEntryList(_p)
  32. #define DBG_ASSERT Assert
  33. #pragma warning (disable:4390)
  34. #include <string.hxx>
  35. #pragma warning (default:4390)
  36. //
  37. //$ HACK: end
  38. //$ REVIEW: end
  39. //
  40. // Private constants.
  41. //
  42. enum {
  43. ACTION_NOTHING = 0x00000000,
  44. ACTION_EMIT_CH = 0x00010000,
  45. ACTION_EMIT_DOT_CH = 0x00020000,
  46. ACTION_EMIT_DOT_DOT_CH = 0x00030000,
  47. ACTION_BACKUP = 0x00040000,
  48. ACTION_MASK = 0xFFFF0000
  49. };
  50. // States and State translations ---------------------------------------------
  51. //
  52. const UINT gc_rguStateTable[16] = {
  53. // State 0
  54. //
  55. 0 , // other
  56. 0 , // "."
  57. 4 , // EOS
  58. 1 , // "\"
  59. // State 1
  60. //
  61. 0 , // other
  62. 2 , // "."
  63. 4 , // EOS
  64. 1 , // "\"
  65. // State 2
  66. //
  67. 0 , // other
  68. 3 , // "."
  69. 4 , // EOS
  70. 1 , // "\"
  71. // State 3
  72. //
  73. 0 , // other
  74. 0 , // "."
  75. 4 , // EOS
  76. 1 // "\"
  77. };
  78. const UINT gc_rguActionTable[16] = {
  79. // State 0
  80. //
  81. ACTION_EMIT_CH, // other
  82. ACTION_EMIT_CH, // "."
  83. ACTION_EMIT_CH, // EOS
  84. ACTION_EMIT_CH, // "\"
  85. // State 1
  86. //
  87. ACTION_EMIT_CH, // other
  88. ACTION_NOTHING, // "."
  89. ACTION_EMIT_CH, // EOS
  90. ACTION_NOTHING, // "\"
  91. // State 2
  92. //
  93. ACTION_EMIT_DOT_CH, // other
  94. ACTION_NOTHING, // "."
  95. ACTION_EMIT_CH, // EOS
  96. ACTION_NOTHING, // "\"
  97. // State 3
  98. //
  99. ACTION_EMIT_DOT_DOT_CH, // other
  100. ACTION_EMIT_DOT_DOT_CH, // "."
  101. ACTION_BACKUP, // EOS
  102. ACTION_BACKUP // "\"
  103. };
  104. // The following table provides the index for various ISA Latin1 characters
  105. // in the incoming URL.
  106. //
  107. // It assumes that the URL is ISO Latin1 == ASCII
  108. //
  109. const UINT gc_rguIndexForChar[] = {
  110. 2, // null char
  111. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 thru 10
  112. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 11 thru 20
  113. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21 thru 30
  114. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 31 thru 40
  115. 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, // 41 thru 50 46 = '.' 47 = '/'
  116. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 51 thru 60
  117. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 61 thru 70
  118. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 71 thru 80
  119. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 81 thru 90
  120. 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, // 91 thru 100 92 = '\\'
  121. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 101 thru 110
  122. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 111 thru 120
  123. 0, 0, 0, 0, 0, 0, 0, 0 // 121 thru 128
  124. };
  125. // FIsUTF8TrailingByte -------------------------------------------------------
  126. //
  127. // Function returns TRUE if the given character is UTF-8 trailing byte
  128. //
  129. inline BOOL FIsUTF8TrailingByte (CHAR ch)
  130. {
  131. return (0x80 == (ch & 0xc0));
  132. }
  133. // FIsUTF8Url ----------------------------------------------------------------
  134. //
  135. // Function returns TRUE if the given string can be treated as UTF-8
  136. //
  137. BOOL __fastcall
  138. FIsUTF8Url (/* [in] */ LPCSTR pszUrl)
  139. {
  140. CHAR ch;
  141. while (0 != (ch = *pszUrl++))
  142. {
  143. // Sniff for a lead-byte
  144. //
  145. if (ch & 0x80)
  146. {
  147. CHAR chT1;
  148. CHAR chT2;
  149. // Pick off the trailing bytes
  150. //
  151. chT1 = *pszUrl++;
  152. if (chT1)
  153. chT2 = *pszUrl;
  154. else
  155. chT2 = 0;
  156. // Handle the three byte case
  157. // 1110xxxx 10xxxxxx 10xxxxxx
  158. //
  159. if (((ch & 0xF0) == 0xE0) &&
  160. FIsUTF8TrailingByte (chT1) &&
  161. FIsUTF8TrailingByte (chT2))
  162. {
  163. // We found a UTF-8 character. Keep going.
  164. //
  165. pszUrl++;
  166. continue;
  167. }
  168. // Also watch for the two byte case
  169. // 110xxxxx 10xxxxxx
  170. //
  171. else if (((ch & 0xE0) == 0xC0) && FIsUTF8TrailingByte (chT1))
  172. {
  173. // We found a UTF-8 character. Keep going.
  174. //
  175. continue;
  176. }
  177. else
  178. {
  179. // If we had a lead-byte but no UTF trailing bytes, then
  180. // this cannot be a UTF8 url.
  181. //
  182. DebugTrace ("FIsUTF8Url(): url contains UTF8 lead byte with no trailing\n");
  183. return FALSE;
  184. }
  185. }
  186. }
  187. // Hey, we made it through without any non-singlebyte chars, so we can
  188. // operate as if this is a UTF8 url.
  189. //
  190. DebugTrace ("FIsUTF8Url(): url contains only UTF8 characters\n");
  191. return TRUE;
  192. }
  193. // ScCanonicalizeURL ---------------------------------------------------------
  194. //
  195. // Wide version of the CanonURL() function, which lives in iisrtl.lib
  196. //
  197. // PURPOSE: Sanitizes a path by removing bogus path elements.
  198. //
  199. // As expected, "/./" entries are simply removed, and
  200. // "/../" entries are removed along with the previous
  201. // path element.
  202. //
  203. // To maintain compatibility with URL path semantics
  204. // additional transformations are required. All backward
  205. // slashes "\\" are converted to forward slashes. Any
  206. // repeated forward slashes (such as "///") are mapped to
  207. // single backslashes.
  208. //
  209. // A state table (see the p_StateTable global at the
  210. // beginning of this file) is used to perform most of
  211. // the transformations. The table's rows are indexed
  212. // by current state, and the columns are indexed by
  213. // the current character's "class" (either slash, dot,
  214. // NULL, or other). Each entry in the table consists
  215. // of the new state tagged with an action to perform.
  216. // See the ACTION_* constants for the valid action
  217. // codes.
  218. //
  219. // PARAMETERS:
  220. //
  221. // pwszSrc - url to canonicalize
  222. // pwszDest - buffer to fill
  223. // pcch - number of characters written into the buffer
  224. // (which includes '\0' termination)
  225. //
  226. // RETURN CODES:
  227. //
  228. // S_OK.
  229. //
  230. // NOTE: This function assumes that destination buffer is
  231. // equal or biger than the source.
  232. //
  233. SCODE __fastcall
  234. ScCanonicalizeURL( /* [in] */ LPCWSTR pwszSrc,
  235. /* [in/out] */ LPWSTR pwszDest,
  236. /* [out] */ UINT * pcch )
  237. {
  238. LPCWSTR pwszPath;
  239. UINT uiCh;
  240. UINT uiIndex = 0; // State = 0
  241. Assert( pwszSrc );
  242. Assert( pwszDest );
  243. Assert( pcch );
  244. // Zero out return
  245. //
  246. *pcch = 0;
  247. // Remember start of the buffer into which we will canonicalize
  248. //
  249. pwszPath = pwszDest;
  250. // Loop until we enter state 4 (the final, accepting state).
  251. //
  252. do {
  253. // Grab the next character from the path and compute its
  254. // next state. While we're at it, map any forward
  255. // slashes to backward slashes.
  256. //
  257. uiIndex = gc_rguStateTable[uiIndex] * 4; // 4 = # states
  258. uiCh = *pwszSrc++;
  259. uiIndex += ((uiCh >= 0x80) ? 0 : gc_rguIndexForChar[uiCh]);
  260. // Perform the action associated with the state.
  261. //
  262. switch( gc_rguActionTable[uiIndex] )
  263. {
  264. case ACTION_EMIT_DOT_DOT_CH :
  265. *pwszDest++ = L'.';
  266. /* fall through */
  267. case ACTION_EMIT_DOT_CH :
  268. *pwszDest++ = L'.';
  269. /* fall through */
  270. case ACTION_EMIT_CH :
  271. *pwszDest++ = static_cast<WCHAR>(uiCh);
  272. /* fall through */
  273. case ACTION_NOTHING :
  274. break;
  275. case ACTION_BACKUP :
  276. if ( (pwszDest > (pwszPath + 1) ) && (*pwszPath == L'/'))
  277. {
  278. pwszDest--;
  279. Assert( *pwszDest == L'/' );
  280. *pwszDest = L'\0';
  281. pwszDest = wcsrchr( pwszPath, L'/') + 1;
  282. }
  283. *pwszDest = L'\0';
  284. break;
  285. default :
  286. TrapSz("Invalid action code in state table!");
  287. uiIndex = 2; // move to invalid state
  288. Assert( 4 == gc_rguStateTable[uiIndex] );
  289. *pwszDest++ = L'\0';
  290. break;
  291. }
  292. } while( gc_rguStateTable[uiIndex] != 4 );
  293. // Point to terminating nul
  294. //
  295. if (ACTION_EMIT_CH == gc_rguActionTable[uiIndex])
  296. {
  297. pwszDest--;
  298. }
  299. Assert((L'\0' == *pwszDest) && (pwszDest >= pwszPath));
  300. // Return number of characters written
  301. //
  302. *pcch = static_cast<UINT>(pwszDest - pwszPath + 1);
  303. return S_OK;
  304. }
  305. SCODE __fastcall
  306. ScCanonicalizePrefixedURL( /* [in] */ LPCWSTR pwszSrc,
  307. /* [in] */ LPWSTR pwszDest,
  308. /* [out] */ UINT * pcch )
  309. {
  310. SCODE sc = S_OK;
  311. LPCWSTR pwszStripped;
  312. UINT cchStripped;
  313. UINT cch = 0;
  314. Assert(pwszSrc);
  315. Assert(pwszDest);
  316. Assert(pcch);
  317. // Zero out return
  318. //
  319. *pcch = 0;
  320. pwszStripped = PwszUrlStrippedOfPrefix(pwszSrc);
  321. cchStripped = static_cast<UINT>(pwszStripped - pwszSrc);
  322. // Copy the prefix over to the destination. I do not use
  323. // memcpy here as source and destination may overlap,
  324. // and in such case those functions are not recomended.
  325. //
  326. for (UINT ui = 0; ui < cchStripped; ui++)
  327. {
  328. pwszDest[ui] = pwszSrc[ui];
  329. }
  330. // Canonicalize the remainder of te URL
  331. //
  332. sc = ScCanonicalizeURL(pwszStripped,
  333. pwszDest + cchStripped,
  334. &cch);
  335. if (S_OK != sc)
  336. {
  337. Assert(S_FALSE != sc);
  338. DebugTrace("ScCanonicalizePrefixedURL() - ScCanonicalizeUrl() failed 0x%08lX\n", sc);
  339. goto ret;
  340. }
  341. // Return the number of characters written
  342. //
  343. *pcch = cchStripped + cch;
  344. ret:
  345. return sc;
  346. }
  347. // ScConvertToWide -----------------------------------------------------------
  348. //
  349. SCODE __fastcall
  350. ScConvertToWide(/* [in] */ LPCSTR pszSource,
  351. /* [in/out] */ UINT * pcchDest,
  352. /* [out] */ LPWSTR pwszDest,
  353. /* [in] */ LPCSTR pszAcceptLang,
  354. /* [in] */ BOOL fUrlConversion)
  355. {
  356. SCODE sc = S_OK;
  357. CStackBuffer<CHAR, MAX_PATH> pszToConvert;
  358. UINT cpid = CP_UTF8;
  359. UINT cb;
  360. UINT cch;
  361. Assert(pszSource);
  362. Assert(pcchDest);
  363. Assert(pwszDest);
  364. if (fUrlConversion)
  365. {
  366. // Allocate the space to escape URL into.
  367. //
  368. cb = static_cast<UINT>(strlen(pszSource));
  369. if (NULL == pszToConvert.resize(cb + 1))
  370. {
  371. sc = E_OUTOFMEMORY;
  372. DebugTrace("ScConvertToWide() - Error while allocating memory 0x%08lX\n", sc);
  373. goto ret;
  374. }
  375. // Unescape to the new buffer. Unescaping can only shrink the size,
  376. // so we have enough buffer allocated.
  377. //
  378. HttpUriUnescape(pszSource, pszToConvert.get());
  379. // Perform a quick pass over the url looking for non-UTF8 characters.
  380. // Remember if we need to continue to scan for UTF8 characters.
  381. //
  382. if (!FIsUTF8Url(pszToConvert.get()))
  383. {
  384. // ... cannot do CP_UTF8, assume CP_ACP.
  385. //
  386. cpid = CP_ACP;
  387. }
  388. // If the URL cannot be treated as UTF8 then find out the code page for it
  389. //
  390. if (CP_UTF8 != cpid)
  391. {
  392. if (pszAcceptLang)
  393. {
  394. HDRITER hdri(pszAcceptLang);
  395. LPCSTR psz;
  396. // Let us try guessing the cpid from the language string
  397. // Try all the languages in the header. We stop at the
  398. // first language for which we have a cpid mapping. If
  399. // none of the languages specified in the header have cpid
  400. // mappings, then we will end up with the default cpid
  401. // CP_ACP
  402. //
  403. for (psz = hdri.PszNext(); psz; psz = hdri.PszNext())
  404. {
  405. if (CLangToCpidCache::FFindCpid(psz, &cpid))
  406. break;
  407. }
  408. }
  409. }
  410. // Swap the pointer and recalculate the size
  411. //
  412. pszSource = pszToConvert.get();
  413. }
  414. // Find out the length of the string we will convert
  415. //
  416. cb = static_cast<UINT>(strlen(pszSource));
  417. // Translate to unicode including '\0' termination
  418. //
  419. cch = MultiByteToWideChar(cpid,
  420. (CP_UTF8 != cpid) ? MB_ERR_INVALID_CHARS : 0,
  421. pszSource,
  422. cb + 1,
  423. pwszDest,
  424. *pcchDest);
  425. if (0 == cch)
  426. {
  427. // If buffer was not sufficient
  428. //
  429. if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
  430. {
  431. // Find out the size needed
  432. //
  433. cch = MultiByteToWideChar(cpid,
  434. (CP_UTF8 != cpid) ? MB_ERR_INVALID_CHARS : 0,
  435. pszSource,
  436. cb + 1,
  437. NULL,
  438. 0);
  439. if (0 == cch)
  440. {
  441. sc = HRESULT_FROM_WIN32(GetLastError());
  442. DebugTrace("ScConvertToWide() - MultiByteToWideChar() failed to fetch size 0x%08lX - CPID: %d\n", sc, cpid);
  443. goto ret;
  444. }
  445. // Return the size and warning back
  446. //
  447. *pcchDest = cch;
  448. sc = S_FALSE;
  449. goto ret;
  450. }
  451. else
  452. {
  453. sc = HRESULT_FROM_WIN32(GetLastError());
  454. DebugTrace("ScConvertToWide() - MultiByteToWideChar() failed 0x%08lX - CPID: %d\n", sc, cpid);
  455. goto ret;
  456. }
  457. }
  458. *pcchDest = cch;
  459. ret:
  460. return sc;
  461. }
  462. // ScNormalizeUrl ------------------------------------------------------------
  463. //
  464. // PURPOSE: Normalization of a url.
  465. //
  466. // Has two components to the operation:
  467. //
  468. // 1) All sequences of %xx are replaced by a single character that
  469. // has a value that is equal to the hex representation of the
  470. // following two characters.
  471. //
  472. // 2) All path modification sequences are stripped out and the url
  473. // is adjusted accordingly. The set of path modification sequences
  474. // that we recognize are as follows:
  475. //
  476. // "//" reduces to "/"
  477. // "/./" reduces to "/"
  478. // "/../" strips off the last path segment
  479. //
  480. // It is important to note that the unescaping happens first!
  481. //
  482. // NOTE: this function does NOT currently normalize the path separators
  483. // All '\' are NOT replaced with '/' in this function or vice versa.
  484. // The code is implemented such that slashes replaced due to a double
  485. // slash such as "//", "\\", "\/", or "/\" are defaulted to forward
  486. // slashes '/'
  487. //
  488. // A state table (see the gc_rguStateTable global at the beginning
  489. // of this file) is used to perform most of the transformations. The
  490. // table's rows are indexed by current state, and the columns are indexed
  491. // by the current character's "class" (either slash, dot, NULL, or other).
  492. // Each entry in the table consists of the new state tagged with an action
  493. // to perform. See the ACTION_* constants for the valid action codes.//
  494. //
  495. // PARAMETERS:
  496. //
  497. // pwszSourceUrl -- the URL to be normalized
  498. // pcchNormalizedUrl -- the amount of characters available in buffer
  499. // pointed by pwszNormalizedUrl
  500. // pwszNormalizedUrl -- the place to put the normalized URL
  501. //
  502. // RETURN CODES:
  503. //
  504. // S_OK: Everything went well, the URL was normalized into pwszNormalizedUrl.
  505. // S_FALSE: Buffer was not sufficient. Required size is in *pcchNormalizedUrl.
  506. // E_OUTOFMEMORY: Memory alocation failure
  507. // ...other errors that we could get from the conversion routines
  508. //
  509. SCODE __fastcall
  510. ScNormalizeUrl (
  511. /* [in] */ LPCWSTR pwszSourceUrl,
  512. /* [in/out] */ UINT * pcchNormalizedUrl,
  513. /* [out] */ LPWSTR pwszNormalizedUrl,
  514. /* [in] */ LPCSTR pszAcceptLang)
  515. {
  516. SCODE sc = S_OK;
  517. CStackBuffer<CHAR, MAX_PATH> pszSourceUrl;
  518. UINT cchSourceUrl;
  519. UINT cbSourceUrl;
  520. Assert(pwszSourceUrl);
  521. Assert(pcchNormalizedUrl);
  522. Assert(pwszNormalizedUrl);
  523. // We are given the wide version of the URL, so someone who
  524. // converted it already should have done that correctly. So
  525. // we will convert it to CP_UTF8
  526. //
  527. cchSourceUrl = static_cast<UINT>(wcslen(pwszSourceUrl));
  528. cbSourceUrl = cchSourceUrl * 3;
  529. if (NULL == pszSourceUrl.resize(cbSourceUrl + 1))
  530. {
  531. sc = E_OUTOFMEMORY;
  532. DebugTrace("ScNormalizeUrl() - Error while allocating memory 0x%08lX\n", sc);
  533. goto ret;
  534. }
  535. cbSourceUrl = WideCharToMultiByte(CP_UTF8,
  536. 0,
  537. pwszSourceUrl,
  538. cchSourceUrl + 1,
  539. pszSourceUrl.get(),
  540. cbSourceUrl + 1,
  541. NULL,
  542. NULL);
  543. if (0 == cbSourceUrl)
  544. {
  545. sc = HRESULT_FROM_WIN32(GetLastError());
  546. DebugTrace("ScNormalizeUrl() - WideCharToMultiByte() failed 0x%08lX\n", sc);
  547. goto ret;
  548. }
  549. sc = ScNormalizeUrl(pszSourceUrl.get(),
  550. pcchNormalizedUrl,
  551. pwszNormalizedUrl,
  552. pszAcceptLang);
  553. if (FAILED(sc))
  554. {
  555. DebugTrace("ScNormalizeUrl() - ScNormalizeUrl() failed 0x%08lX\n", sc);
  556. goto ret;
  557. }
  558. ret:
  559. return sc;
  560. }
  561. SCODE __fastcall
  562. ScNormalizeUrl (
  563. /* [in] */ LPCSTR pszSourceUrl,
  564. /* [in/out] */ UINT * pcchNormalizedUrl,
  565. /* [out] */ LPWSTR pwszNormalizedUrl,
  566. /* [in] */ LPCSTR pszAcceptLang)
  567. {
  568. SCODE sc = S_OK;
  569. Assert(pszSourceUrl);
  570. Assert(pcchNormalizedUrl);
  571. Assert(pwszNormalizedUrl);
  572. // Convert the URL to UNICODE into the given buffer.
  573. // Function may return S_FALSE, so make sure we
  574. // check the return code correctly - against S_OK
  575. //
  576. sc = ScConvertToWide(pszSourceUrl,
  577. pcchNormalizedUrl,
  578. pwszNormalizedUrl,
  579. pszAcceptLang,
  580. TRUE);
  581. if (S_OK != sc)
  582. {
  583. DebugTrace("ScNormalizeUrl() - ScConvertToWide() returned 0x%08lX\n", sc);
  584. goto ret;
  585. }
  586. // Canonicalize in place, take into account that URL may be fully
  587. // qualified.
  588. //
  589. sc = ScCanonicalizePrefixedURL(pwszNormalizedUrl,
  590. pwszNormalizedUrl,
  591. pcchNormalizedUrl);
  592. if (FAILED(sc))
  593. {
  594. DebugTrace("ScNormalizeUrl() - ScCanonicalizePrefixedURL() failed 0x%08lX\n", sc);
  595. goto ret;
  596. }
  597. ret:
  598. return sc;
  599. }
  600. // ScStoragePathFromUrl ------------------------------------------------------
  601. //
  602. // PURPOSE: Url to storage path translation.
  603. //
  604. SCODE __fastcall
  605. ScStoragePathFromUrl (
  606. /* [in] */ const IEcb & ecb,
  607. /* [in] */ LPCWSTR pwszUrl,
  608. /* [out] */ LPWSTR wszStgID,
  609. /* [in/out] */ UINT * pcch,
  610. /* [out] */ CVRoot ** ppcvr)
  611. {
  612. Assert (pwszUrl);
  613. Assert (wszStgID);
  614. Assert (pcch);
  615. SCODE sc = S_OK;
  616. HSE_UNICODE_URL_MAPEX_INFO mi;
  617. LPCWSTR pwszVRoot;
  618. UINT cchVRoot;
  619. UINT cch = 0;
  620. UINT cchUrl = 0;
  621. #undef ALLOW_RELATIVE_URL_TRANSLATION
  622. #ifdef ALLOW_RELATIVE_URL_TRANSLATION
  623. CStackBuffer<WCHAR,256> pwszNew;
  624. #endif // ALLOW_RELATIVE_URL_TRANSLATION
  625. // Lets make sure this funcion is never called with a
  626. // prefixed url.
  627. //
  628. sc = ScStripAndCheckHttpPrefix (ecb, &pwszUrl);
  629. if (FAILED (sc))
  630. return sc;
  631. // Make sure that the url is absolute
  632. //
  633. if (L'/' != *pwszUrl)
  634. {
  635. #ifdef ALLOW_RELATIVE_URL_TRANSLATION
  636. //$ REVIEW:
  637. //
  638. // This code is here should we ever decide we need
  639. // to support relative url processing.
  640. //
  641. // Construct an absolute url from the relative one
  642. //
  643. UINT cchRequestUrl = wcslen(ecb.LpwszRequestUrl());
  644. UINT cchUrl = static_cast<UINT>(wcslen(pwszUrl));
  645. if (NULL == pwszNew.resize(CbSizeWsz(cchRequestUrl + cchUrl)))
  646. {
  647. sc = E_OUTOFMEMORY;
  648. DebugTrace("ScStoragePathFromUrl() - CStackBuffer::resize() failed 0x%08lX\n", sc);
  649. return sc;
  650. }
  651. memcpy (pwszNew.get(), ecb.LpwszRequestUrl(), cchRequestUrl * sizeof(WCHAR));
  652. memcpy (pwszNew.get(), pwszUrl, (cchUrl + 1) * sizeof(WCHAR));
  653. // Now pszURI points to the generated absolute URI
  654. //
  655. pwszUrl = pwszNew.get();
  656. //
  657. //$ REVIEW: end
  658. #else
  659. DebugTrace ("ScStoragePathFromUrl(): cannot translate relative URIs\n");
  660. return E_DAV_BAD_DESTINATION;
  661. #endif // ALLOW_RELATIVE_URL_TRANSLATION
  662. }
  663. // OK, here is where virtual root spanning needs to be supported...
  664. //
  665. // When the virtual root of the request url does not match the
  666. // the virtual root for the url being translated, extra work
  667. // needs to be done.
  668. //
  669. // There are two ways to do this.
  670. //
  671. // 1) Call back to IIS and have it do the translation for us
  672. // 2) Use our metabase cache to rip through each virtual root
  673. // and find the longest matching virtual root.
  674. //
  675. // At first thought, the second method seems efficient. However,
  676. // the changes being made to the metabase cache do not make this
  677. // an easy matter. The cache no longer will be containing just
  678. // virtual roots, so the lookup will not be as cheap.
  679. //
  680. //$ REVIEW: In fact, I believe that we must do the virtual lookup
  681. // via IIS for all translations. The sub-virtual root thing keeps
  682. // gnawing at me.
  683. //
  684. cchUrl = static_cast<UINT>(wcslen(pwszUrl));
  685. sc = ecb.ScReqMapUrlToPathEx(pwszUrl, &mi);
  686. if (FAILED(sc))
  687. {
  688. DebugTrace("ScStoragePathFromUrl() - IEcb::SSFReqMapUrlPathEx() failed 0x%08lX\n", sc);
  689. return sc;
  690. }
  691. // Try and figure out if the url spanned a virtual root at all.
  692. //
  693. cchVRoot = ecb.CchGetVirtualRootW(&pwszVRoot);
  694. if (cchVRoot != mi.cchMatchingURL)
  695. {
  696. // This case is not so cut-n-dry..
  697. //
  698. // Since CchGetVirtualRoot() should always return a url
  699. // that does not have a trailing slash, the matching count
  700. // could be off by one and the root may actually be the
  701. // same!
  702. //
  703. // Assuming "/vroot" is the Virtual Root in question, this if
  704. // statement protects against the following:
  705. // 1. catches a two completely different sized vroots.
  706. // disqualifies matches that are too short or
  707. // too long "/vr", but allows "/vroot/" because need to
  708. // handle IIS bug (NT:432359).
  709. // 2. checks to make sure the URL is slash terminated. This
  710. // allows "/vroot/" (again because of NT:432359), but
  711. // disqualifies vroots such as "/vrootA"
  712. // 3. allows "/vroot" to pass if mi.cchMatchingURL is off by
  713. // one (again because of NT:432359).
  714. //
  715. if ((cchVRoot + 1 != mi.cchMatchingURL) || // 1
  716. ((L'/' != pwszUrl[cchVRoot]) && // 2
  717. (L'\0' != pwszUrl[cchVRoot]))) // 3
  718. {
  719. // If we're here the virtual root of the URL does not match
  720. // the current virtual root...
  721. //
  722. DebugTrace ("ScStoragePathFromUrl() - urls do not "
  723. "share common virtual root\n"
  724. "-- pwszUrl: %ls\n"
  725. "-- pwszVirtualRoot: %ls\n"
  726. "-- cchVirtualRoot: %ld\n",
  727. pwszUrl,
  728. pwszVRoot,
  729. cchVRoot);
  730. // Tell the caller that the virtual root is spanned. This allows
  731. // the call to succeed, but the caller to fail the call if spanning
  732. // is not allowed.
  733. //
  734. sc = W_DAV_SPANS_VIRTUAL_ROOTS;
  735. }
  736. else
  737. {
  738. // If we're here we know that the current virtual root matches
  739. // the virtual root of the URL, and the following character in
  740. // the URL is a slash or a NULL termination. cchMatchingURL is
  741. // EXACTLY 1 greater than the number of characters in the virtual
  742. // root (cchVRoot) due to the IIS bug (NT:432359).
  743. //
  744. // Theoretically, if cchMatchingURL matches and matches
  745. // one more than the number of characters in the
  746. // vroot, the characters will match! Thus we should assert this case.
  747. //
  748. Assert (!_wcsnicmp(pwszVRoot, pwszUrl, cchVRoot));
  749. // In this case, mi.cchMatchingURL actually _includes_ the
  750. // slash. Below, when we copy in the trailing part of the
  751. // URL, we skip mi.cchMatchingURL characters in the URL
  752. // before copying in the trailing URL. This has the
  753. // unfortunate side effect in this case of missing the
  754. // slash that is at the beginning of the URL after the
  755. // virtual root, so you could end up with a path that looks
  756. // like:
  757. // \\.\BackOfficeStorage\mydom.extest.microsoft.com\MBXuser1/Inbox
  758. // rather than:
  759. // \\.\BackOfficeStorage\mydom.extest.microsoft.com\MBX/user1/Inbox
  760. //
  761. // So decrement miw.cchMatchingURL here to handle this.
  762. //
  763. DebugTrace ("ScStoragePathFromUrl() : mi.cchMatchingURL included a slash!\n");
  764. mi.cchMatchingURL--;
  765. }
  766. }
  767. // If we are hitting this conditional if statement, we know that
  768. // the mi.cchMatchingURL is the same as the number of characters
  769. // in the vroot.
  770. // 1. We already checked for difference in the vroot lengts above
  771. // and if length of the vroot was 0 then they actually matched
  772. // 2. We know that due to an IIS bug (NT:432359), cchMatchingURL
  773. // could be 1 character too long. This lines checks for that
  774. // case. If that is the case, we know that the VRoot is one
  775. // character longer than the virtual root of the URL -- ie
  776. // we are spanning virtual roots.
  777. // 3. If the strings aren't in fact the same then we know that
  778. // cchMatchingURL matched to a different virtual root than
  779. // pszVRoot.
  780. //
  781. else if ((0 != cchVRoot) && // 1
  782. ((L'\0' == pwszUrl[cchVRoot - 1]) || // 2
  783. _wcsnicmp(pwszVRoot, pwszUrl, cchVRoot))) // 3
  784. {
  785. DebugTrace ("ScStoragePathFromUrl(): urls do not "
  786. "share common virtual root\n"
  787. "-- pwszUrl: %ls\n"
  788. "-- pwszVirtualRoot: %ls\n"
  789. "-- cchVirtualRoot: %ld\n",
  790. pwszUrl,
  791. pwszVRoot,
  792. cchVRoot);
  793. // Tell the caller that the virtual root is spanned. This allows
  794. // the call to succeed, but the caller to fail the call if spanning
  795. // is not allowed.
  796. //
  797. sc = W_DAV_SPANS_VIRTUAL_ROOTS;
  798. }
  799. // If we span, and the caller wants it, look up the vroot
  800. // for them.
  801. //
  802. if ((W_DAV_SPANS_VIRTUAL_ROOTS == sc) && ppcvr)
  803. {
  804. auto_ref_ptr<CVRoot> arp;
  805. CStackBuffer<WCHAR, MAX_PATH> pwsz;
  806. CStackBuffer<WCHAR, MAX_PATH> pwszMetaPath;
  807. if (NULL == pwsz.resize((mi.cchMatchingURL + 1) * sizeof(WCHAR)))
  808. {
  809. sc = E_OUTOFMEMORY;
  810. DebugTrace("ScStoragePathFromUrl() - CStackBuffer::resize() failed 0x%08lX\n", sc);
  811. return sc;
  812. }
  813. memcpy(pwsz.get(), pwszUrl, mi.cchMatchingURL * sizeof(WCHAR));
  814. pwsz[mi.cchMatchingURL] = L'\0';
  815. if (NULL == pwszMetaPath.resize(::CbMDPathW(ecb, pwsz.get())))
  816. {
  817. sc = E_OUTOFMEMORY;
  818. DebugTrace("ScStoragePathFromUrl() - CStackBuffer::resize() failed 0x%08lX\n", sc);
  819. return sc;
  820. }
  821. MDPathFromURIW (ecb, pwsz.get(), pwszMetaPath.get());
  822. _wcslwr (pwszMetaPath.get());
  823. // Find the vroot
  824. //
  825. if (!CChildVRCache::FFindVroot (ecb, pwszMetaPath.get(), arp))
  826. {
  827. DebugTrace ("ScStoragePathFromUrl(): spanned virtual root not available\n");
  828. return E_DAV_BAD_DESTINATION;
  829. }
  830. *ppcvr = arp.relinquish();
  831. }
  832. // Adjust the matching path the same way as we did matching URL
  833. //
  834. if ( mi.cchMatchingPath )
  835. {
  836. LPCWSTR pwsz = mi.lpszPath + mi.cchMatchingPath - 1;
  837. if ( L'\\' == *pwsz )
  838. {
  839. while ((0 < mi.cchMatchingPath) &&
  840. (L'\\' == *pwsz) &&
  841. (!FIsDriveTrailingChar(pwsz, mi.cchMatchingPath)))
  842. {
  843. mi.cchMatchingPath--;
  844. pwsz--;
  845. }
  846. }
  847. else if ( L'\0' == *pwsz )
  848. {
  849. mi.cchMatchingPath--;
  850. }
  851. }
  852. // If there is not enough space in the buffer provided, a return
  853. // of S_FALSE tells the caller to realloc and try again!
  854. //
  855. Assert (*pcch);
  856. cch = mi.cchMatchingPath + cchUrl - mi.cchMatchingURL + 1;
  857. if (*pcch < cch)
  858. {
  859. DebugTrace ("ScStoragePathFromUrl (IIS URL Version): buffer too "
  860. "small for url translation\n");
  861. *pcch = cch;
  862. //$ REVIEW: take ownership of the abandoned ref if one was abandoned
  863. //
  864. if (ppcvr)
  865. {
  866. auto_ref_ptr<CVRoot> arp;
  867. arp.take_ownership(*ppcvr);
  868. *ppcvr = NULL;
  869. }
  870. //
  871. //$ REVIEW: end.
  872. return S_FALSE;
  873. }
  874. // Copy the Matching Path to the beginning of rgwchStgID
  875. //
  876. memcpy(wszStgID, mi.lpszPath, mi.cchMatchingPath * sizeof(WCHAR));
  877. // Copy the request URL after the vroot, including '\0' termination
  878. //
  879. Assert (cchUrl >= mi.cchMatchingURL);
  880. memcpy (wszStgID + mi.cchMatchingPath,
  881. pwszUrl + mi.cchMatchingURL,
  882. (cchUrl - mi.cchMatchingURL + 1) * sizeof(WCHAR));
  883. // Change all '/' that came from URL to '\\'
  884. //
  885. for (LPWSTR pwch = wszStgID + mi.cchMatchingPath; *pwch; pwch++)
  886. if (L'/' == *pwch) *pwch = L'\\';
  887. // At this point, cch is the actual number of chars in the destination
  888. // -- including the null
  889. //
  890. *pcch = cch;
  891. Assert (L'\0' == wszStgID[cch - 1]);
  892. Assert (L'\0' != wszStgID[cch - 2]);
  893. return sc;
  894. }
  895. // Storage path to url translation -------------------------------------------
  896. //
  897. SCODE __fastcall
  898. ScUrlFromStoragePath (
  899. /* [in] */ const IEcbBase & ecb,
  900. /* [in] */ LPCWSTR pwszPath,
  901. /* [out] */ LPWSTR pwszUrl,
  902. /* [in/out] */ UINT * pcch,
  903. /* [in] */ LPCWSTR pwszServer)
  904. {
  905. WCHAR * pwch;
  906. LPCWSTR pwszPrefix;
  907. LPCWSTR pwszVroot;
  908. LPCWSTR pwszVrPath;
  909. UINT cch;
  910. UINT cchPath;
  911. UINT cchMatching;
  912. UINT cchAdjust;
  913. UINT cchPrefix;
  914. UINT cchServer;
  915. UINT cchVroot;
  916. UINT cchTrailing;
  917. // Find the number of path characters that match the
  918. // virtual root
  919. //
  920. cchVroot = ecb.CchGetVirtualRootW (&pwszVroot);
  921. // We always return fully qualified Urls -- so we need to know
  922. // the server name and the prefix.
  923. //
  924. cchPrefix = ecb.CchUrlPrefixW (&pwszPrefix);
  925. // If server name is not given yet take default one
  926. //
  927. if (!pwszServer)
  928. {
  929. cchServer = ecb.CchGetServerNameW (&pwszServer);
  930. }
  931. else
  932. {
  933. cchServer = static_cast<UINT>(wcslen(pwszServer));
  934. }
  935. // The number of characters to be skiped needs to include the physical
  936. // vroot path.
  937. //
  938. cchMatching = ecb.CchGetMatchingPathW (&pwszVrPath);
  939. // If the matching path is ending with '\\' we need to ajust accordingly
  940. // as that symbol in the matching path is "overlapping" with the start
  941. // of trailing URL part. To construct the URL correctly we need to make
  942. // sure that we do not skip that separator. Also handle it the best way
  943. // we can if someone is trying to commit suicide by putting '/' at the
  944. // end of the matching path.
  945. //
  946. if ((0 != cchMatching) &&
  947. (L'\\' == pwszVrPath[cchMatching - 1] || L'/' == pwszVrPath[cchMatching - 1]) )
  948. {
  949. cchAdjust = 1;
  950. }
  951. else
  952. {
  953. cchAdjust = 0;
  954. }
  955. // So, at this point, the length of the resulting url is the length
  956. // of the servername, virtual root and trailing path all put together.
  957. //
  958. cchPath = static_cast<UINT>(wcslen(pwszPath));
  959. // We assume that the path we are passed in is always fully qualified
  960. // with the vroot. Assert that. Calculate the length of trailing
  961. // portion including '\0' termination.
  962. //
  963. Assert (cchPath + cchAdjust >= cchMatching);
  964. cchTrailing = cchPath - cchMatching + cchAdjust + 1;
  965. cch = cchPrefix + cchServer + cchVroot + cchTrailing;
  966. // If there is not enough room, a return value of S_FALSE will
  967. // properly instruct the caller to realloc and call again.
  968. //
  969. if (*pcch < cch)
  970. {
  971. DebugTrace ("ScUrlFromStoragePath(): buffer too small for path translation.\n");
  972. *pcch = cch;
  973. return S_FALSE;
  974. }
  975. // Start building the url by copying over the prefix and servername.
  976. //
  977. memcpy (pwszUrl, pwszPrefix, cchPrefix * sizeof(WCHAR));
  978. memcpy (pwszUrl + cchPrefix, pwszServer, cchServer * sizeof(WCHAR));
  979. cch = cchPrefix + cchServer;
  980. // Copy over the virtual root
  981. //
  982. memcpy (pwszUrl + cch, pwszVroot, cchVroot * sizeof(WCHAR));
  983. cch += cchVroot;
  984. //$ REVIEW: I don't know what happens here when we want to be able to
  985. // span virtual roots with MOVE/COPY and what not. However, it will
  986. // be up to the caller to fail this if that is the case.
  987. //
  988. if (!FSizedPathConflict (pwszPath,
  989. cchPath,
  990. pwszVrPath,
  991. cchMatching))
  992. {
  993. DebugTrace ("ScUrlFromStoragePath (IIS URL Version): translation not "
  994. "scoped by current virtual root\n");
  995. return E_DAV_BAD_DESTINATION;
  996. }
  997. //
  998. //$ REVIEW: end
  999. // While copying make sure that we are not skiping the '\' separator
  1000. // at the beginning of the trailing URL. That is what cchAdjust stands for.
  1001. //
  1002. memcpy( pwszUrl + cch, pwszPath + cchMatching - cchAdjust, cchTrailing * sizeof(WCHAR));
  1003. // Lastly, swap all '\\' to '/'
  1004. //
  1005. for (pwch = pwszUrl + cch;
  1006. NULL != (pwch = wcschr (pwch, L'\\'));
  1007. )
  1008. {
  1009. *pwch++ = L'/';
  1010. }
  1011. // Pass back the length, cchTrailing includes the null-termination at this
  1012. // point.
  1013. //
  1014. *pcch = cch + cchTrailing;
  1015. Assert (0 == pwszUrl[cch + cchTrailing - 1]);
  1016. Assert (0 != pwszUrl[cch + cchTrailing - 2]);
  1017. DebugTrace ("ScUrlFromStoragePath(): translated path:\n"
  1018. "- path \"%ls\" maps to \"%ls\"\n"
  1019. "- cchMatchingPath = %d\n"
  1020. "- cchVroot = %d\n",
  1021. pwszPath,
  1022. pwszUrl,
  1023. cchMatching,
  1024. cchVroot);
  1025. return S_OK;
  1026. }
  1027. SCODE __fastcall
  1028. ScUrlFromSpannedStoragePath (
  1029. /* [in] */ LPCWSTR pwszPath,
  1030. /* [in] */ CVRoot & vr,
  1031. /* [in] */ LPWSTR pwszUrl,
  1032. /* [in/out] */ UINT * pcch)
  1033. {
  1034. WCHAR * pwch;
  1035. LPCWSTR pwszPort;
  1036. LPCWSTR pwszServer;
  1037. LPCWSTR pwszVRoot;
  1038. LPCWSTR pwszVRPath;
  1039. UINT cch;
  1040. UINT cchPort;
  1041. UINT cchServer;
  1042. UINT cchTotal;
  1043. UINT cchTrailing;
  1044. UINT cchVRoot;
  1045. // Make sure that the path and the virtual root context share a
  1046. // common base path!
  1047. //
  1048. cch = vr.CchGetVRPath(&pwszVRPath);
  1049. if (_wcsnicmp (pwszPath, pwszVRPath, cch))
  1050. {
  1051. DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): path "
  1052. "is not from virtual root\n");
  1053. return E_DAV_BAD_DESTINATION;
  1054. }
  1055. pwszPath += cch;
  1056. // If the next character is not a moniker separator, then this can't
  1057. // be a match
  1058. //
  1059. if (*pwszPath && (*pwszPath != L'\\'))
  1060. {
  1061. DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): path "
  1062. "is not from virtual root\n");
  1063. return E_DAV_BAD_DESTINATION;
  1064. }
  1065. // A concatination of the url prefix, server, port, vroot prefix and
  1066. // the remaining path gives us our URL.
  1067. //
  1068. cchTrailing = static_cast<UINT>(wcslen (pwszPath));
  1069. cchVRoot = vr.CchGetVRoot(&pwszVRoot);
  1070. cchServer = vr.CchGetServerName(&pwszServer);
  1071. cchPort = vr.CchGetPort(&pwszPort);
  1072. cch = cchTrailing +
  1073. cchVRoot +
  1074. cchPort +
  1075. cchServer +
  1076. CchConstString(gc_wszUrl_Prefix_Secure) + 1;
  1077. if (*pcch < cch)
  1078. {
  1079. DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): spanned "
  1080. "translation buffer too small\n");
  1081. *pcch = cch;
  1082. return S_FALSE;
  1083. }
  1084. // A small note about codepages....
  1085. //
  1086. // Start constructing the url by grabbing the appropriate prefix
  1087. //
  1088. if (vr.FSecure())
  1089. {
  1090. cchTotal = gc_cchszUrl_Prefix_Secure;
  1091. memcpy (pwszUrl, gc_wszUrl_Prefix_Secure, cchTotal * sizeof(WCHAR));
  1092. }
  1093. else
  1094. {
  1095. cchTotal = gc_cchszUrl_Prefix;
  1096. memcpy (pwszUrl, gc_wszUrl_Prefix, cchTotal * sizeof(WCHAR));
  1097. }
  1098. // Tack on the server name
  1099. //
  1100. memcpy (pwszUrl + cchTotal, pwszServer, cchServer * sizeof(WCHAR));
  1101. cchTotal += cchServer;
  1102. // Tack on the port if it is neither the default or a secure port
  1103. //
  1104. if (!vr.FDefaultPort() && !vr.FSecure())
  1105. {
  1106. memcpy (pwszUrl + cchTotal, pwszPort, cchPort * sizeof(WCHAR));
  1107. cchTotal += cchPort;
  1108. }
  1109. // Add the vroot
  1110. //
  1111. memcpy (pwszUrl + cchTotal, pwszVRoot, cchVRoot * sizeof(WCHAR));
  1112. cchTotal += cchVRoot;
  1113. // Add the trailing path.
  1114. //
  1115. // IMPORTANT: The resulting cch will include the NULL
  1116. // termination.
  1117. //
  1118. if (cch < cchTotal + cchTrailing + 1)
  1119. {
  1120. DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): spanned "
  1121. "translation buffer too small\n");
  1122. *pcch = cchTotal + cchTrailing + 1;
  1123. return S_FALSE;
  1124. }
  1125. else
  1126. {
  1127. memcpy (pwszUrl + cchTotal, pwszPath, (cchTrailing + 1) * sizeof(WCHAR));
  1128. }
  1129. Assert (L'\0' == pwszUrl[cchTotal + cchTrailing]);
  1130. Assert (L'\0' != pwszUrl[cchTotal + cchTrailing - 1]);
  1131. // Translate all '\\' to '/'
  1132. //
  1133. for (pwch = pwszUrl + cchTrailing + 1; *pwch; pwch++)
  1134. {
  1135. if (L'\\' == *pwch)
  1136. {
  1137. *pwch = L'/';
  1138. }
  1139. }
  1140. DebugTrace ("ScUrlFromSpannedStoragePath (IIS URL Version): spanned "
  1141. "storage path fixed as '%S'\n", pwszUrl);
  1142. *pcch = cchTotal + cchTrailing + 1;
  1143. return S_OK;
  1144. }
  1145. // Wire urls -----------------------------------------------------------------
  1146. //
  1147. SCODE __fastcall
  1148. ScWireUrlFromWideLocalUrl (
  1149. /* [in] */ UINT cchLocal,
  1150. /* [in] */ LPCWSTR pwszLocalUrl,
  1151. /* [in/out] */ auto_heap_ptr<CHAR>& pszWireUrl)
  1152. {
  1153. UINT ib = 0;
  1154. // Since the url is already wide, all we need to do is
  1155. // to reduce the url to a UTF8 entity.
  1156. //
  1157. // We could call the Win32 WideCharToMultiByte(), but we
  1158. // already know that production, and it would be best to
  1159. // skip the system call if possible.
  1160. //
  1161. // Allocate enough space as if every char had maximum expansion
  1162. //
  1163. CStackBuffer<CHAR,MAX_PATH> psz;
  1164. if (NULL == psz.resize((cchLocal * 3) + 1))
  1165. return E_OUTOFMEMORY;
  1166. if (cchLocal)
  1167. {
  1168. // Currently we get UTF-8 url-s onto the wire. Do we ever
  1169. // want to pipe out any other codepage?
  1170. //
  1171. ib = WideCharToUTF8(pwszLocalUrl,
  1172. cchLocal,
  1173. psz.get(),
  1174. (cchLocal * 3));
  1175. Assert(ib);
  1176. }
  1177. // Termination...
  1178. //
  1179. psz[ib] = 0;
  1180. // Escape it
  1181. //
  1182. HttpUriEscape (psz.get(), pszWireUrl);
  1183. return S_OK;
  1184. }
  1185. SCODE __fastcall
  1186. ScWireUrlFromStoragePath (
  1187. /* [in] */ IMethUtilBase * pmu,
  1188. /* [in] */ LPCWSTR pwszStoragePath,
  1189. /* [in] */ BOOL fCollection,
  1190. /* [in] */ CVRoot * pcvrTranslate,
  1191. /* [in/out] */ auto_heap_ptr<CHAR>& pszWireUrl)
  1192. {
  1193. Assert (pwszStoragePath);
  1194. Assert (NULL == pszWireUrl.get());
  1195. SCODE sc = S_OK;
  1196. // Take a best guess for size and try and convert
  1197. // NOTE: we allocate space allowing for the trailing
  1198. // slash on directories - thus for the calls filling
  1199. // the buffer we indicate that available space is one
  1200. // character less than actually allocated.
  1201. //
  1202. CStackBuffer<WCHAR,128> pwszUrl;
  1203. //$ REVIEW: WINRAID:462078: The "-1" below has to do
  1204. // with making sure that there is enough space to append
  1205. // a trailing slash at the end of the url for directories.
  1206. //
  1207. UINT cch = pwszUrl.celems() - 1;
  1208. //
  1209. //$ REVIEW: end.
  1210. if (pcvrTranslate == NULL)
  1211. {
  1212. sc = pmu->ScUrlFromStoragePath (pwszStoragePath, pwszUrl.get(), &cch);
  1213. if (S_FALSE == sc)
  1214. {
  1215. // Try again, but with a bigger size.
  1216. //
  1217. if (NULL == pwszUrl.resize(CbSizeWsz(cch)))
  1218. return E_OUTOFMEMORY;
  1219. sc = pmu->ScUrlFromStoragePath (pwszStoragePath, pwszUrl.get(), &cch);
  1220. }
  1221. if (S_OK != sc)
  1222. {
  1223. DebugTrace ("ScWireUrlFromStoragePath (IIS URL Version): "
  1224. "failed to translate path to href\n");
  1225. return sc;
  1226. }
  1227. }
  1228. else
  1229. {
  1230. sc = ScUrlFromSpannedStoragePath (pwszStoragePath,
  1231. *pcvrTranslate,
  1232. pwszUrl.get(),
  1233. &cch);
  1234. if (S_FALSE == sc)
  1235. {
  1236. // Try again, but with a bigger size.
  1237. //
  1238. if (NULL == pwszUrl.resize(CbSizeWsz(cch)))
  1239. return E_OUTOFMEMORY;
  1240. sc = ScUrlFromSpannedStoragePath (pwszStoragePath,
  1241. *pcvrTranslate,
  1242. pwszUrl.get(),
  1243. &cch);
  1244. }
  1245. if (S_OK != sc)
  1246. {
  1247. DebugTrace ("ScWireUrlFromStoragePath (IIS URL Version): "
  1248. "failed to translate path to href\n");
  1249. return sc;
  1250. }
  1251. }
  1252. // cch includes the termination char
  1253. //
  1254. Assert (cch);
  1255. Assert (L'\0' == pwszUrl[cch - 1]);
  1256. Assert (L'\0' != pwszUrl[cch - 2]);
  1257. // For directories, check the trailing slash
  1258. //
  1259. if (fCollection && (L'/' != pwszUrl[cch - 2]))
  1260. {
  1261. // Add the trailing '/'
  1262. //
  1263. // Remember we've added one extra bytes when allocating pwszUrl
  1264. //
  1265. pwszUrl[cch - 1] = L'/';
  1266. pwszUrl[cch] = L'\0';
  1267. cch += 1;
  1268. }
  1269. return ScWireUrlFromWideLocalUrl (cch - 1, pwszUrl.get(), pszWireUrl);
  1270. }