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.

802 lines
21 KiB

  1. /*
  2. * U R L . C P P
  3. *
  4. * Url normalization/canonicalization
  5. *
  6. * Stolen from the IIS5 project 'iis5\svcs\iisrlt\string.cxx' and
  7. * cleaned up to fit in with the DAV sources.
  8. *
  9. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  10. */
  11. #include "_davprs.h"
  12. #include "xemit.h"
  13. // URI Escaping --------------------------------------------------------------
  14. //
  15. // gc_mpbchCharToHalfByte - map a ASCII-encoded char representing a single hex
  16. // digit to a half-byte value. Used to convert hex represented strings into a
  17. // binary representation.
  18. //
  19. // Reference values:
  20. //
  21. // '0' = 49, 0x31;
  22. // 'A' = 65, 0x41;
  23. // 'a' = 97, 0x61;
  24. //
  25. DEC_CONST BYTE gc_mpbchCharToHalfByte[] =
  26. {
  27. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  28. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  29. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  30. 0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7, 0x8,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
  31. 0x0,0xa,0xb,0xc,0xd,0xe,0xf,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // Caps here.
  32. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  33. 0x0,0xa,0xb,0xc,0xd,0xe,0xf,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // Lowercase here.
  34. 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
  35. };
  36. // Switches a wide char to a half-byte hex value. The incoming char
  37. // MUST be in the "ASCII-encoded hex digit" range: 0-9, A-F, a-f.
  38. //
  39. inline BYTE
  40. BCharToHalfByte(WCHAR wch)
  41. {
  42. AssertSz (!(wch & 0xFF00), "BCharToHalfByte: char upper bits non-zero");
  43. AssertSz (iswxdigit(wch), "BCharToHalfByte: Char out of hex digit range.");
  44. return gc_mpbchCharToHalfByte[wch];
  45. };
  46. // gc_mpwchhbHalfByteToChar - map a half-byte (low nibble) value to the
  47. // correspoding ASCII-encoded wide char. Used to convert a single byte
  48. // into a hex string representation.
  49. //
  50. const WCHAR gc_mpwchhbHalfByteToChar[] =
  51. {
  52. L'0', L'1', L'2', L'3',
  53. L'4', L'5', L'6', L'7',
  54. L'8', L'9', L'A', L'B',
  55. L'C', L'D', L'E', L'F',
  56. };
  57. // Switches a half-byte to an ACSII-encoded wide char.
  58. // NOTE: The caller must mask out the "other half" of the byte!
  59. //
  60. inline WCHAR WchHalfByteToWideChar(BYTE b)
  61. {
  62. AssertSz (!(b & 0xF0), "WchHalfByteToWideChar: byte upper bits non-zero.");
  63. return gc_mpwchhbHalfByteToChar[b];
  64. };
  65. // gc_mpchhbHalfByteToChar - map a half-byte (low nibble) value to the
  66. // correspoding ASCII-encoded wide char. Used to convert a single byte
  67. // into a hex string representation.
  68. //
  69. const CHAR gc_mpchhbHalfByteToChar[] =
  70. {
  71. '0', '1', '2', '3',
  72. '4', '5', '6', '7',
  73. '8', '9', 'A', 'B',
  74. 'C', 'D', 'E', 'F',
  75. };
  76. // Switches a half-byte to an ACSII-encoded wide char.
  77. // NOTE: The caller must mask out the "other half" of the byte!
  78. //
  79. inline CHAR ChHalfByteToWideChar(BYTE b)
  80. {
  81. AssertSz (!(b & 0xF0), "ChHalfByteToWideChar: byte upper bits non-zero.");
  82. return gc_mpchhbHalfByteToChar[b];
  83. };
  84. // Note on HttpUriEscape and HttpUriUnescape
  85. //
  86. // These functions do the HTTP URL escaping and Unescaping equivalent to
  87. // the one done by IIS. DAVEX URLs are escaped and unescaped thru a different
  88. // sets of routines in _urlesc subsystem. The rule is whenever we sent out
  89. // an Exchange HTTP wire URL, you should go thru the function in the
  90. // _urlesc. Right now old UrlEscape and UrlUnescape routines are routed
  91. // through those. However there exist cases where we need to do the
  92. // IIS style escape and unescape. One scenario is when we forward the
  93. // URLs to ISAPIs, where we use the HttpUriUnescape and HttpUriEscape functions.
  94. // File system DAV also uses HttpUriEscape and HttpUriUnescape.
  95. //
  96. // HttpUriEscape()
  97. //
  98. // This function is immigrated from iis5\svcs\w3\server\dirlist.cpp's
  99. // We should do the same URL escaping as IIS does.
  100. //
  101. // Replaces all "bad" characters with their ASCII hex equivalent
  102. //
  103. VOID __fastcall HttpUriEscape (
  104. /* [in] */ LPCSTR pszSrc,
  105. /* [out] */ auto_heap_ptr<CHAR>& pszDst)
  106. {
  107. enum { URL_BUF_INCREMENT = 16 };
  108. // It is important that we operate on unsigned character, as otherwise
  109. // checks below simply do not work correctly. E.g. UTF-8 characters will
  110. // not get escaped, etc.
  111. //
  112. UCHAR uch;
  113. UINT cbDst;
  114. UINT cbSrc;
  115. UINT ibDst;
  116. UINT ibSrc;
  117. // Set cbSrc to account for the string length of
  118. // the url including the NULL
  119. //
  120. Assert(pszSrc);
  121. cbSrc = static_cast<UINT>(strlen (pszSrc) + 1);
  122. // Allocate enough space for the expanded url -- and
  123. // lets be a bit optimistic
  124. //
  125. cbDst = max (cbSrc + URL_BUF_INCREMENT, MAX_PATH);
  126. pszDst = static_cast<LPSTR>(g_heap.Alloc(cbDst));
  127. for (ibSrc = 0, ibDst = 0; ibSrc < cbSrc; ibSrc++)
  128. {
  129. uch = pszSrc[ibSrc];
  130. // Make sure we always have space to expand this character.
  131. // Since we have allocated extra space to begin with, we should
  132. // never have the scenario where we do a realloc just for the
  133. // last char.
  134. //
  135. if (ibDst + 2 >= cbDst) // enough space for three more chars
  136. {
  137. // Destiniation buffer is not large enough, reallocate
  138. // to get more space.
  139. //
  140. cbDst += URL_BUF_INCREMENT;
  141. pszDst.realloc (cbDst);
  142. }
  143. // Escape characters that are in the non-printable range
  144. // but ignore CR and LF.
  145. //
  146. // The inclusive ranges escaped are...
  147. //
  148. // 0x01 - 0x20 /* First non-printable range */
  149. // 0x80 - 0xBF /* Trailing bytes of UTF8 sequence */
  150. // 0xC0 - 0xDF /* Leading byte of UTF8 two byte sequence */
  151. // 0xE0 - 0xEF /* Leading byte of UTF8 three byte sequence */
  152. //
  153. if ((((uch >= 0x01) && (uch <= 0x20)) /* First non-printable range */ ||
  154. ((uch >= 0x80) && (uch <= 0xEF)) /* UTF8 sequence bytes */ ||
  155. (uch == '%') ||
  156. (uch == '?') ||
  157. (uch == '+') ||
  158. (uch == '&') ||
  159. (uch == '#')) &&
  160. !(uch == '\n' || uch == '\r'))
  161. {
  162. // Insert the escape character
  163. //
  164. pszDst[ibDst + 0] = '%';
  165. // Convert the low then the high character to hex
  166. //
  167. BYTE bDigit = static_cast<BYTE>(uch % 16);
  168. pszDst[ibDst + 2] = ChHalfByteToWideChar (bDigit);
  169. bDigit = static_cast<BYTE>((uch/16) % 16);
  170. pszDst[ibDst + 1] = ChHalfByteToWideChar (bDigit);
  171. // Adjust for the two extra characters for this sequence
  172. //
  173. ibDst += 3;
  174. }
  175. else
  176. {
  177. pszDst[ibDst] = uch;
  178. ibDst += 1;
  179. }
  180. }
  181. UrlTrace ("Url: UriEscape(): escaped url: %hs\n", pszDst.get());
  182. return;
  183. }
  184. // HttpUriUnescape()
  185. //
  186. // This function is immigrated from iis5\svcs\w3\server\dirlist.cpp's
  187. // We should do the same URL unescaping as IIS does.
  188. //
  189. // Replaces all escaped characters with their byte equivalent
  190. //
  191. //
  192. VOID __fastcall HttpUriUnescape (
  193. /* [in] */ const LPCSTR pszUrl,
  194. /* [out] */ LPSTR pszUnescaped)
  195. {
  196. LPCSTR pch;
  197. LPSTR pchNew;
  198. Assert (pszUrl);
  199. Assert (pszUnescaped);
  200. pch = pszUrl;
  201. pchNew = pszUnescaped;
  202. while (*pch)
  203. {
  204. // If this is a valid byte-stuffed character, unpack it. For us
  205. // to really unpack it, we need the sequence to be valid.
  206. //
  207. // NOTE: we stole this code from IIS at one point, so we are
  208. // pretty sure this is consistant with their behavior.
  209. //
  210. if (('%' == pch[0]) &&
  211. ('\0' != pch[1]) &&
  212. ('\0' != pch[2]) &&
  213. isxdigit(pch[1]) &&
  214. isxdigit(pch[2]))
  215. {
  216. #pragma warning(disable:4244)
  217. // IMPORTANT: when we do this processing, there is no specific
  218. // machine/byte ordering assumed. The HEX digit is represented
  219. // as a %xx, and the first char is multiplied by sixteen and
  220. // then second char is added in.
  221. //
  222. UrlTrace ("HttpUriEscape () - unescaping: %hc%hc%hc\n", pch[0], pch[1], pch[2]);
  223. *pchNew = (BCharToHalfByte(pch[1]) * 16) + BCharToHalfByte(pch[2]);
  224. pch += 3;
  225. #pragma warning(default:4244)
  226. }
  227. else
  228. {
  229. *pchNew = *pch++;
  230. }
  231. // If a NULL character was byte-stuffed, then that is the end of
  232. // the url and we can stop processing now. Otherwise, path modifications
  233. // could be used to bypass a NULL.
  234. //
  235. if ('\0' == *pchNew)
  236. {
  237. break;
  238. }
  239. pchNew++;
  240. }
  241. // Close the new URI
  242. //
  243. *pchNew = '\0';
  244. UrlTrace ("HttpUriEscape() - resulting destination: \"%hs\"\n", pszUnescaped);
  245. }
  246. // Prefix stripping ----------------------------------------------------------
  247. //
  248. SCODE __fastcall
  249. ScStripAndCheckHttpPrefix (
  250. /* [in] */ const IEcb& ecb,
  251. /* [in/out] */ LPCWSTR * ppwszRequest)
  252. {
  253. SCODE sc = S_OK;
  254. Assert (ppwszRequest);
  255. Assert (*ppwszRequest);
  256. LPCWSTR pwszRequest = *ppwszRequest;
  257. // See if the servername matches
  258. //
  259. LPCWSTR pwsz;
  260. UINT cch;
  261. // If the forward request URI is fully qualified, strip it to
  262. // an absolute URI
  263. //
  264. cch = ecb.CchUrlPrefixW (&pwsz);
  265. if (!_wcsnicmp (pwsz, pwszRequest, cch))
  266. {
  267. pwszRequest += cch;
  268. cch = ecb.CchGetServerNameW (&pwsz);
  269. if (_wcsnicmp (pwsz, pwszRequest, cch))
  270. {
  271. sc = E_DAV_BAD_DESTINATION;
  272. DebugTrace ("ScStripAndCheckHttpPrefix(): server does not match 0x%08lX\n", sc);
  273. goto ret;
  274. }
  275. // If the server name matched, make sure that if the
  276. // next thing is a port number that it is ":80".
  277. //
  278. pwszRequest += cch;
  279. if (*pwszRequest == L':')
  280. {
  281. cch = ecb.CchUrlPortW (&pwsz);
  282. if (_wcsnicmp (pwsz, pwszRequest, cch))
  283. {
  284. sc = E_DAV_BAD_DESTINATION;
  285. DebugTrace ("ScStripAndCheckHttpPrefix(): port does not match 0x%08lX\n", sc);
  286. goto ret;
  287. }
  288. pwszRequest += cch;
  289. }
  290. }
  291. *ppwszRequest = pwszRequest;
  292. ret:
  293. return sc;
  294. }
  295. LPCWSTR __fastcall
  296. PwszUrlStrippedOfPrefix (
  297. /* [in] */ LPCWSTR pwszUrl)
  298. {
  299. Assert (pwszUrl);
  300. // Skip past the "http://" of the url
  301. //
  302. if (L'/' != *pwszUrl)
  303. {
  304. // If the first slash occurance is a double slash, then
  305. // move past the end of it.
  306. //
  307. LPWSTR pwszSlash = wcschr (pwszUrl, L'/');
  308. while (pwszSlash && (L'/' == pwszSlash[1]))
  309. {
  310. // Skip past the host/server name
  311. //
  312. pwszSlash += 2;
  313. while (NULL != (pwszSlash = wcschr (pwszSlash, L'/')))
  314. {
  315. UrlTrace ("Url: PwszUrlStrippedOfPrefix(): normalizing: "
  316. "skipping %d chars of '%S'\n",
  317. pwszSlash - pwszUrl,
  318. pwszUrl);
  319. pwszUrl = pwszSlash;
  320. break;
  321. }
  322. break;
  323. }
  324. }
  325. return pwszUrl;
  326. }
  327. // Storage path to UTF8 url translation --------------------------------------
  328. //
  329. SCODE __fastcall
  330. ScUTF8UrlFromStoragePath (
  331. /* [in] */ const IEcbBase & ecb,
  332. /* [in] */ LPCWSTR pwszPath,
  333. /* [out] */ LPSTR pszUrl,
  334. /* [in/out] */ UINT * pcbUrl,
  335. /* [in] */ LPCWSTR pwszServer)
  336. {
  337. CStackBuffer<WCHAR,MAX_PATH> pwszUrl;
  338. SCODE sc = S_OK;
  339. UINT cbUrl;
  340. UINT cchUrl;
  341. // Assume one skinny character will be represented by one wide character,
  342. // Note that callers are indicating available space including 0 termination.
  343. //
  344. cchUrl = *pcbUrl;
  345. if (!pwszUrl.resize(cchUrl * sizeof(WCHAR)))
  346. return E_OUTOFMEMORY;
  347. sc = ScUrlFromStoragePath (ecb,
  348. pwszPath,
  349. pwszUrl.get(),
  350. &cchUrl,
  351. pwszServer);
  352. if (S_FALSE == sc)
  353. {
  354. if (!pwszUrl.resize(cchUrl * sizeof(WCHAR)))
  355. return E_OUTOFMEMORY;
  356. sc = ScUrlFromStoragePath (ecb,
  357. pwszPath,
  358. pwszUrl.get(),
  359. &cchUrl,
  360. pwszServer);
  361. }
  362. if (S_OK != sc)
  363. {
  364. // There is no reason to fail because for being short of buffer - we gave as
  365. // much as we were asked for
  366. //
  367. Assert(S_FALSE != sc);
  368. DebugTrace( "ScUrlFromStoragePath() - ScUrlFromStoragePath() failed 0x%08lX\n", sc );
  369. goto ret;
  370. }
  371. // Find out the length of buffer needed for the UTF-8
  372. // version of the URL. Functions above return the length
  373. // including '\0' termination, so number of charasters
  374. // to convert will always be more than zero.
  375. //
  376. Assert(0 < cchUrl);
  377. cbUrl = WideCharToMultiByte(CP_UTF8,
  378. 0,
  379. pwszUrl.get(),
  380. cchUrl,
  381. NULL,
  382. 0,
  383. NULL,
  384. NULL);
  385. if (0 == cbUrl)
  386. {
  387. sc = HRESULT_FROM_WIN32(GetLastError());
  388. DebugTrace( "ScUTF8UrlFromStoragePath() - WideCharToMultiByte() failed 0x%08lX\n", sc );
  389. goto ret;
  390. }
  391. if (*pcbUrl < cbUrl)
  392. {
  393. sc = S_FALSE;
  394. *pcbUrl = cbUrl;
  395. goto ret;
  396. }
  397. else
  398. {
  399. // Convert the URL to skinny including 0 termination
  400. //
  401. cbUrl = WideCharToMultiByte( CP_UTF8,
  402. 0,
  403. pwszUrl.get(),
  404. cchUrl,
  405. pszUrl,
  406. cbUrl,
  407. NULL,
  408. NULL);
  409. if (0 == cbUrl)
  410. {
  411. sc = HRESULT_FROM_WIN32(GetLastError());
  412. DebugTrace( "ScUrlFromStoragePath() - WideCharToMultiByte() failed 0x%08lX\n", sc );
  413. goto ret;
  414. }
  415. *pcbUrl = cbUrl;
  416. }
  417. ret:
  418. if (FAILED(sc))
  419. {
  420. // Zero out the return in the case of failure
  421. //
  422. *pcbUrl = 0;
  423. }
  424. return sc;
  425. }
  426. // Redirect url construction -------------------------------------------------
  427. //
  428. SCODE __fastcall
  429. ScConstructRedirectUrl (
  430. /* [in] */ const IEcb& ecb,
  431. /* [in] */ BOOL fNeedSlash,
  432. /* [out] */ LPSTR * ppszUrl,
  433. /* [in] */ LPCWSTR pwszServer )
  434. {
  435. SCODE sc;
  436. auto_heap_ptr<CHAR> pszEscapedUrl; // We will need to escape the url we construct, so we will store it there
  437. CStackBuffer<CHAR,MAX_PATH> pszLocation;
  438. LPCSTR pszQueryString;
  439. UINT cchQueryString;
  440. LPCWSTR pwsz;
  441. UINT cch;
  442. // This request needs to be redirected. Allocate
  443. // enough space for the URI and an extra trailing
  444. // slash and a null terminator.
  445. //
  446. pwsz = ecb.LpwszPathTranslated();
  447. pszQueryString = ecb.LpszQueryString();
  448. cchQueryString = static_cast<UINT>(strlen(pszQueryString));
  449. // Make a best guess. We allow for additional trailing '/'
  450. // here (thus we show one character less than we actually
  451. // have to the functions bellow).
  452. //
  453. cch = pszLocation.celems() - 1;
  454. sc = ::ScUTF8UrlFromStoragePath (ecb,
  455. pwsz,
  456. pszLocation.get(),
  457. &cch,
  458. pwszServer);
  459. if (S_FALSE == sc)
  460. {
  461. // Try again. Also do not forget that we may
  462. // add trailing '/' later, thus allow space for
  463. // it too.
  464. //
  465. if (!pszLocation.resize(cch + 1))
  466. return E_OUTOFMEMORY;
  467. sc = ::ScUTF8UrlFromStoragePath (ecb,
  468. pwsz,
  469. pszLocation.get(),
  470. &cch,
  471. pwszServer);
  472. }
  473. if (S_OK != sc)
  474. {
  475. // We gave sufficient space, we must not be asked for more
  476. //
  477. Assert(S_FALSE != sc);
  478. DebugTrace("ScConstructRedirectUrl() - ScUTF8UrlFromStoragePath() failed with error 0x%08lX\n", sc);
  479. goto ret;
  480. }
  481. // The translation above results in a URI that does not
  482. // have a trailing slash. So if one is required, do that
  483. // here.
  484. //
  485. // The value of cch at this point includes the
  486. // null-termination character. So we need to look
  487. // back two characters instead of one.
  488. //
  489. //$ DBCS: Since we are always spitting back UTF8, I don't think
  490. // forward-slash characters are likely to be an issue here. So
  491. // there should be no need for a DBCS lead byte check to determine
  492. // if a slash is required.
  493. //
  494. Assert (0 == pszLocation[cch - 1]);
  495. if (fNeedSlash && ('/' != pszLocation[cch - 2]))
  496. {
  497. pszLocation[cch - 1] = '/';
  498. pszLocation[cch] = '\0';
  499. }
  500. //
  501. //$ DBCS: end.
  502. // Escape the URL
  503. //
  504. HttpUriEscape (pszLocation.get(), pszEscapedUrl);
  505. // Copy the query string if we have got one
  506. //
  507. if (cchQueryString)
  508. {
  509. cch = static_cast<UINT>(strlen(pszEscapedUrl.get()));
  510. pszEscapedUrl.realloc(cch + cchQueryString + 2); // One for the '?' and one for zero termination.
  511. pszEscapedUrl[cch] = '?';
  512. memcpy(pszEscapedUrl.get() + cch + 1, pszQueryString, cchQueryString);
  513. pszEscapedUrl[cch + 1 + cchQueryString] = '\0';
  514. }
  515. *ppszUrl = pszEscapedUrl.relinquish();
  516. ret:
  517. return sc;
  518. }
  519. // Virtual roots -------------------------------------------------------------
  520. //
  521. /*
  522. * FIsVRoot()
  523. *
  524. * Purpose:
  525. *
  526. * Returns TRUE iif the specified URI is the VRoot
  527. *
  528. * Parameters:
  529. *
  530. * pmu [in] method utility function
  531. * pszURI [in] URI to check
  532. */
  533. BOOL __fastcall
  534. CMethUtil::FIsVRoot (LPCWSTR pwszURI)
  535. {
  536. LPCWSTR pwsz;
  537. LPCWSTR pwszUnused;
  538. Assert(pwszURI);
  539. UINT cch = static_cast<UINT>(wcslen (pwszURI));
  540. // The virtual root as determined by CchGetVirtualRoot(),
  541. // will truncate the trailing slash, if any.
  542. //
  543. pwsz = pwszURI + (cch ? cch - 1 : 0);
  544. if (L'/' == *pwsz)
  545. {
  546. cch -= 1;
  547. }
  548. return (cch == CchGetVirtualRootW(&pwszUnused));
  549. }
  550. // Path conflicts ------------------------------------------------------------
  551. //
  552. BOOL __fastcall
  553. FSizedPathConflict (
  554. /* [in] */ LPCWSTR pwszSrc,
  555. /* [in] */ UINT cchSrc,
  556. /* [in] */ LPCWSTR pwszDst,
  557. /* [in] */ UINT cchDst)
  558. {
  559. // For which ever path is shorter, see if it is
  560. // a proper subdir of the longer.
  561. //
  562. if ((0 == cchSrc) || (0 == cchDst))
  563. {
  564. DebugTrace ("Dav: Url: FSizedPathConflict(): zero length path is "
  565. "always in conflict!\n");
  566. return TRUE;
  567. }
  568. if (cchDst < cchSrc)
  569. {
  570. // When the destination is shorter, if the paths
  571. // match up to the full length of the destination
  572. // and the last character or the one immediately
  573. // following the destination is a backslash, then
  574. // the paths are conflicting.
  575. //
  576. if (!_wcsnicmp (pwszSrc, pwszDst, cchDst))
  577. {
  578. if ((L'\\' == *(pwszDst + cchDst - 1)) ||
  579. (L'\\' == *(pwszSrc + cchDst)) ||
  580. //$$DAVEX BUG: We could get here in a case where we have:
  581. // pwszSrc = \\.\ExchangeIfs\Private Folders/this/is/my/path
  582. // pwszDest = \\.\ExchangeIfs\Private Folders
  583. // The two comparisons above balk on this. Add the two
  584. // comparisons below to handle this case properly.
  585. (L'/' == *(pwszDst + cchDst - 1)) ||
  586. (L'/' == *(pwszSrc + cchDst)))
  587. {
  588. DebugTrace ("Dav: Url: FSizedPathConflict(): destination is "
  589. "parent to source\n");
  590. return TRUE;
  591. }
  592. }
  593. }
  594. else if (cchSrc < cchDst)
  595. {
  596. // When the source is shorter, if the paths
  597. // match up to the full length of the source
  598. // and the last character or the one immediately
  599. // following the source is a backslash, then
  600. // the paths are conflicting.
  601. //
  602. if (!_wcsnicmp (pwszSrc, pwszDst, cchSrc))
  603. {
  604. if ((L'\\' == *(pwszSrc + cchSrc - 1)) ||
  605. (L'\\' == *(pwszDst + cchSrc)) ||
  606. //$$DAVEX BUG: We could get here in a case where we have:
  607. // pwszSrc = \\.\ExchangeIfs\Private Folders/this/is/my/path
  608. // pwszDest = \\.\ExchangeIfs\Private Folders
  609. // The two comparisons above balk on this. Add the two
  610. // comparisons below to handle this case properly.
  611. (L'/' == *(pwszSrc + cchSrc - 1)) ||
  612. (L'/' == *(pwszDst + cchSrc)))
  613. {
  614. DebugTrace ("Dav: Url: FSizedPathConflict(): source is parent "
  615. "to destination\n");
  616. return TRUE;
  617. }
  618. }
  619. }
  620. else
  621. {
  622. // If the paths are the same length, and are infact
  623. // equal, why do anything?
  624. //
  625. if (!_wcsicmp (pwszSrc, pwszDst))
  626. {
  627. DebugTrace ("Dav: Url: FSizedPathConflict(): source and "
  628. "destination refer to same\n");
  629. return TRUE;
  630. }
  631. }
  632. return FALSE;
  633. }
  634. BOOL __fastcall
  635. FPathConflict (
  636. /* [in] */ LPCWSTR pwszSrc,
  637. /* [in] */ LPCWSTR pwszDst)
  638. {
  639. Assert (pwszSrc);
  640. Assert (pwszDst);
  641. UINT cchSrc = static_cast<UINT>(wcslen (pwszSrc));
  642. UINT cchDst = static_cast<UINT>(wcslen (pwszDst));
  643. return FSizedPathConflict (pwszSrc, cchSrc, pwszDst, cchDst);
  644. }
  645. BOOL __fastcall
  646. FIsImmediateParentUrl (LPCWSTR pwszParent, LPCWSTR pwszChild)
  647. {
  648. LPCWSTR pwsz;
  649. Assert(pwszChild);
  650. UINT cchChild = static_cast<UINT>(wcslen (pwszChild));
  651. UINT cchMatch;
  652. // Skip back from the end of the child until the last
  653. // path segment has been reached
  654. //
  655. pwsz = pwszChild + cchChild - 1;
  656. // Child may terminate in a slash, trim it if need be
  657. //
  658. if (*pwsz == L'/')
  659. {
  660. --pwsz;
  661. }
  662. // Ok, now we can try and isolate the last segment
  663. //
  664. for (; pwsz > pwszChild; --pwsz)
  665. {
  666. if (*pwsz == L'/')
  667. {
  668. break;
  669. }
  670. }
  671. // See if the parent and child match up to this point
  672. //
  673. cchMatch = static_cast<UINT>(pwsz - pwszChild);
  674. if (!_wcsnicmp (pwszParent, pwszChild, cchMatch))
  675. {
  676. // Make sure that the parent doesn't trail off onto another
  677. // branch of the tree, and yes these asserts are DBCS correct.
  678. //
  679. Assert ((*(pwszParent + cchMatch) == L'\0') ||
  680. ((*(pwszParent + cchMatch) == L'/') &&
  681. (*(pwszParent + cchMatch + 1) == L'\0')));
  682. return TRUE;
  683. }
  684. return FALSE;
  685. }
  686. SCODE
  687. ScAddTitledHref (CEmitterNode& enParent,
  688. IMethUtil * pmu,
  689. LPCWSTR pwszTag,
  690. LPCWSTR pwszPath,
  691. BOOL fCollection,
  692. CVRoot* pcvrTranslate)
  693. {
  694. auto_heap_ptr<CHAR> pszUriEscaped;
  695. CEmitterNode en;
  696. SCODE sc = S_OK;
  697. // Just see if we have the path and tag to process
  698. //
  699. Assert(pwszTag);
  700. Assert(pwszPath);
  701. sc = ScWireUrlFromStoragePath (pmu,
  702. pwszPath,
  703. fCollection,
  704. pcvrTranslate,
  705. pszUriEscaped);
  706. if (FAILED (sc))
  707. goto ret;
  708. sc = enParent.ScAddUTF8Node (pwszTag, en, pszUriEscaped.get());
  709. if (FAILED (sc))
  710. goto ret;
  711. ret:
  712. return sc;
  713. }