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.

758 lines
20 KiB

  1. /*
  2. * F S G E T . C P P
  3. *
  4. * Sources file system implementation of DAV-Base
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davfs.h"
  9. #include <htmlmap.h>
  10. #include <ex\rgiter.h>
  11. /*
  12. * ScEmitFile()
  13. *
  14. * Purpose:
  15. *
  16. * Helper function used to open and transmit a given
  17. * file from the local dav namespace.
  18. *
  19. * Parameters:
  20. *
  21. * pmu [in] pointer to the method util obj
  22. * pwszFile [in] name of file to emit
  23. * pwszContent [in] content type of the file, we need it if it is a multipart response
  24. *
  25. * Returns:
  26. *
  27. * SCODE.
  28. * S_OK (0) indicates success, and the WHOLE file was sent.
  29. * W_DAV_PARTIAL_CONTENT indicates success, but only PARTIAL content was
  30. * sent because of a Content-Range header.
  31. * An error (FAILED(sc)) means that the file was not setn.
  32. */
  33. SCODE
  34. ScEmitFile (LPMETHUTIL pmu,
  35. LPCWSTR pwszFile,
  36. LPCWSTR pwszContent)
  37. {
  38. auto_ref_handle hf;
  39. BOOL fMap = FALSE;
  40. BY_HANDLE_FILE_INFORMATION fi;
  41. CRangeParser riByteRange;
  42. LPCWSTR pwsz;
  43. SCODE sc = S_OK;
  44. UINT cch;
  45. // Check validity of input
  46. //
  47. Assert (pwszFile);
  48. Assert (pwszContent);
  49. // Check to see if we have a map file
  50. //
  51. cch = static_cast<UINT>(wcslen (pwszFile));
  52. if ((cch >= 4) && !_wcsicmp (L".map", pwszFile + cch - 4))
  53. fMap = TRUE;
  54. // If we have a locktoken, try to get the lock handle from the cache.
  55. // If this fails, fall through and do the normal processing.
  56. // DO NOT put LOCK handles into an auto-object!! The CACHE still owns it!!!
  57. //
  58. pwsz = pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  59. if (!pwsz ||
  60. !FGetLockHandle (pmu, pmu->LpwszPathTranslated(), GENERIC_READ, pwsz, &hf))
  61. {
  62. // Open the file and go to it
  63. //
  64. if (!hf.FCreate(
  65. DavCreateFile (pwszFile, // filename
  66. GENERIC_READ, // dwAccess
  67. FILE_SHARE_READ | FILE_SHARE_WRITE,
  68. NULL, // lpSecurityAttributes
  69. OPEN_EXISTING, // creation flags
  70. FILE_ATTRIBUTE_NORMAL |
  71. FILE_FLAG_SEQUENTIAL_SCAN |
  72. FILE_FLAG_OVERLAPPED, // attributes
  73. NULL))) // template
  74. {
  75. DWORD dwErr = GetLastError();
  76. sc = HRESULT_FROM_WIN32 (dwErr);
  77. // Special work for 416 Locked responses -- fetch the
  78. // comment & set that as the response body.
  79. //
  80. if (FLockViolation (pmu, dwErr, pwszFile, GENERIC_READ))
  81. {
  82. sc = E_DAV_LOCKED;
  83. }
  84. DebugTrace ("Dav: failed to open the file for retrieval\n");
  85. goto ret;
  86. }
  87. }
  88. // We better have a valid handle.
  89. Assert (hf.get() != INVALID_HANDLE_VALUE);
  90. // We are going to need the file size for both map files and
  91. // ordinary files. For map files, we read the entire file into
  92. // memory. For ordinary files, we need the file size to do
  93. // byte range validation.
  94. //
  95. if (!GetFileInformationByHandle(hf.get(), &fi))
  96. {
  97. sc = HRESULT_FROM_WIN32 (GetLastError());
  98. goto ret;
  99. }
  100. // Again, if it is a mapfile, we need to parse the map and
  101. // find the right URL to redirect to, otherwise, we can just
  102. // emit the file back to the client.
  103. //
  104. if (fMap && pmu->FTranslated())
  105. {
  106. auto_handle<HANDLE> hevt(CreateEvent(NULL, TRUE, FALSE, NULL));
  107. auto_heap_ptr<CHAR> pszBuf;
  108. BOOL fRedirect = FALSE;
  109. LPCSTR pszPrefix;
  110. CHAR pszRedirect[MAX_PATH];
  111. OVERLAPPED ov;
  112. ULONG cb;
  113. // The common case is that these map files are not very large.
  114. // We may want to put a physical upper bound on the size of the
  115. // file, but I don't see that as an imperitive thing at this
  116. // point.
  117. //
  118. // Since we are going to need to parse the whole thing, we are
  119. // going to read the whole thing into memory at once. Read the file
  120. // in, and then parse it out.
  121. //
  122. // Allocate space for the file.
  123. // Lets put an uper bound of 64K on it.
  124. //
  125. if ((fi.nFileSizeHigh != 0) || (fi.nFileSizeLow > (128 * 1024)))
  126. {
  127. // Mapping is too large for our tastes
  128. //
  129. DavTrace ("Dav: mapping file too large\n");
  130. sc = HRESULT_FROM_WIN32 (ERROR_MORE_DATA);
  131. goto ret;
  132. }
  133. pszBuf = (CHAR *)g_heap.Alloc (fi.nFileSizeLow + 1);
  134. // Read it in
  135. //
  136. ov.hEvent = hevt;
  137. ov.Offset = 0;
  138. ov.OffsetHigh = 0;
  139. if (!ReadFromOverlapped (hf.get(), pszBuf, fi.nFileSizeLow, &cb, &ov))
  140. {
  141. sc = HRESULT_FROM_WIN32 (GetLastError());
  142. goto ret;
  143. }
  144. Assert (cb == fi.nFileSizeLow);
  145. // Ensure the file data is NULL terminated
  146. //
  147. *(pszBuf + cb) = 0;
  148. // Check the map...
  149. //
  150. pmu->CchUrlPrefix(&pszPrefix);
  151. if (FIsMapProcessed (pmu->LpszQueryString(),
  152. pszPrefix,
  153. pmu->LpszServerName(),
  154. pszBuf.get(),
  155. &fRedirect,
  156. pszRedirect,
  157. MAX_PATH))
  158. {
  159. // Redirect the request
  160. //
  161. if (fRedirect)
  162. {
  163. sc = pmu->ScRedirect (pszRedirect);
  164. goto ret;
  165. }
  166. }
  167. // if not redirect, we should rewind the file pointer
  168. // back to the beginning.
  169. //
  170. if (INVALID_SET_FILE_POINTER == SetFilePointer (hf.get(), 0, NULL, FILE_BEGIN))
  171. {
  172. sc = HRESULT_FROM_WIN32 (GetLastError());
  173. goto ret;
  174. }
  175. }
  176. // Do any byte range (206 Partial Content) processing. The function will fail out if the
  177. // we are trying to do byte ranges on the file larger than 4GB.
  178. //
  179. sc = ScProcessByteRanges (pmu, pwszFile, fi.nFileSizeLow, fi.nFileSizeHigh, &riByteRange);
  180. // Tell the parser to transmit the file
  181. //
  182. // We need to transmit the entire file
  183. //
  184. if (S_OK == sc)
  185. {
  186. // Just add the file
  187. //
  188. pmu->AddResponseFile (hf);
  189. }
  190. else if (W_DAV_PARTIAL_CONTENT == sc)
  191. {
  192. // It is a byte range transmission. Tranmsit the ranges.
  193. //
  194. Assert(0 == fi.nFileSizeHigh);
  195. TransmitFileRanges(pmu, hf, fi.nFileSizeLow, &riByteRange, pwszContent);
  196. }
  197. ret:
  198. return sc;
  199. }
  200. /*
  201. * TransmitFileRanges()
  202. *
  203. * Purpose:
  204. *
  205. * Helper function used to transmit a byte range
  206. * file from the local dav namespace.
  207. *
  208. * Parameters:
  209. *
  210. * pmu [in] pointer to the method util obj
  211. * hf [in] handle of file to emit
  212. * dwSize [in] size of file
  213. * priRanges [in] the ranges
  214. * pszContent [in] content type of the file, we need it if it is a multipart response
  215. *
  216. */
  217. VOID
  218. TransmitFileRanges (LPMETHUTIL pmu,
  219. const auto_ref_handle& hf,
  220. DWORD dwSize,
  221. CRangeBase * priRanges,
  222. LPCWSTR pwszContent)
  223. {
  224. auto_heap_ptr<WCHAR> pwszPreamble;
  225. WCHAR rgwchBoundary[75];
  226. const RGITEM * prgi = NULL;
  227. DWORD dwTotalRanges;
  228. // Create a buffer for the preamble we tramsit before each part
  229. // of the response.
  230. //
  231. pwszPreamble = static_cast<LPWSTR>(g_heap.Alloc
  232. ((2 + CElems(rgwchBoundary) + 2 +
  233. gc_cchContent_Type + 2 + wcslen(pwszContent) + 2 +
  234. gc_cchContent_Range + 2 + gc_cchBytes + 40) * sizeof(WCHAR)));
  235. // Assert that we have a at least one range to transmit
  236. //
  237. Assert (priRanges);
  238. dwTotalRanges = priRanges->UlTotalRanges();
  239. Assert (dwTotalRanges > 0);
  240. // Assert that we have a content type.
  241. //
  242. Assert (pwszContent);
  243. // Rewind to the first range. This is only a precautionary measure.
  244. //
  245. priRanges->Rewind();
  246. prgi = priRanges->PrgiNextRange();
  247. // Is it a singlepart response
  248. //
  249. if ((1 == dwTotalRanges) && prgi && (RANGE_ROW == prgi->uRT))
  250. {
  251. // Set the content range header
  252. //
  253. wsprintfW(pwszPreamble, L"%ls %u-%u/%u",
  254. gc_wszBytes,
  255. prgi->dwrgi.dwFirst,
  256. prgi->dwrgi.dwLast,
  257. dwSize);
  258. pmu->SetResponseHeader (gc_szContent_Range, pwszPreamble);
  259. // Add the file
  260. //
  261. pmu->AddResponseFile (hf,
  262. prgi->dwrgi.dwFirst,
  263. prgi->dwrgi.dwLast - prgi->dwrgi.dwFirst + 1);
  264. }
  265. else
  266. {
  267. // We have multiple byte ranges, then we need to generate a
  268. // boundary and set the Content-Type header with the multipart
  269. // content type and boundary.
  270. //
  271. // Generate a boundary
  272. //
  273. GenerateBoundary (rgwchBoundary, CElems(rgwchBoundary));
  274. // Create the content type header with the boundary generated
  275. //
  276. wsprintfW(pwszPreamble, L"%ls; %ls=\"%ls\"",
  277. gc_wszMultipart_Byterange,
  278. gc_wszBoundary,
  279. rgwchBoundary);
  280. // Reset the content type header with the new content type
  281. //
  282. pmu->SetResponseHeader (gc_szContent_Type, pwszPreamble);
  283. do {
  284. if (RANGE_ROW == prgi->uRT)
  285. {
  286. // Create preamble.
  287. //
  288. wsprintfW(pwszPreamble, L"--%ls%ls%ls: %ls%ls%ls: %ls %u-%u/%u%ls%ls",
  289. rgwchBoundary,
  290. gc_wszCRLF,
  291. gc_wszContent_Type,
  292. pwszContent,
  293. gc_wszCRLF,
  294. gc_wszContent_Range,
  295. gc_wszBytes,
  296. prgi->dwrgi.dwFirst,
  297. prgi->dwrgi.dwLast,
  298. dwSize,
  299. gc_wszCRLF,
  300. gc_wszCRLF);
  301. pmu->AddResponseText (static_cast<UINT>(wcslen(pwszPreamble)), pwszPreamble);
  302. pmu->AddResponseFile (hf,
  303. prgi->dwrgi.dwFirst,
  304. prgi->dwrgi.dwLast - prgi->dwrgi.dwFirst + 1);
  305. // Add the CRLF
  306. //
  307. pmu->AddResponseText (gc_cchCRLF, gc_szCRLF);
  308. }
  309. prgi = priRanges->PrgiNextRange();
  310. } while (prgi);
  311. // Add the last end of response text
  312. //
  313. wsprintfW(pwszPreamble, L"--%ls--", rgwchBoundary);
  314. pmu->AddResponseText (static_cast<UINT>(wcslen(pwszPreamble)), pwszPreamble);
  315. }
  316. }
  317. SCODE
  318. ScGetFile (LPMETHUTIL pmu,
  319. LPWSTR pwszFile,
  320. LPCWSTR pwszURI)
  321. {
  322. SCODE sc;
  323. WCHAR rgwszContent[MAX_PATH];
  324. FILETIME ft;
  325. // Get the Content-Type of the file
  326. //
  327. UINT cchContent = CElems(rgwszContent);
  328. if (!pmu->FGetContentType(pwszURI, rgwszContent, &cchContent))
  329. {
  330. sc = E_FAIL;
  331. goto ret;
  332. }
  333. // This method is gated by If-xxx headers
  334. //
  335. sc = ScCheckIfHeaders (pmu, pwszFile, TRUE);
  336. if (FAILED (sc))
  337. {
  338. DebugTrace ("Dav: If-xxx failed their check\n");
  339. goto ret;
  340. }
  341. sc = HrCheckStateHeaders (pmu, // methutil
  342. pwszFile, // path
  343. TRUE); // fGetMeth
  344. if (FAILED (sc))
  345. {
  346. DebugTrace ("DavFS: If-State checking failed.\n");
  347. // 304 returns from get should really have an ETag....
  348. //SideAssert(FGetLastModTime (pmu, pwszFile, &ft));
  349. //hsc = HscEmitHeader (pmu, pszContent, &ft);
  350. goto ret;
  351. }
  352. // Emit the headers for the file
  353. //
  354. if (FGetLastModTime (pmu, pwszFile, &ft))
  355. {
  356. sc = pmu->ScEmitHeader (rgwszContent, pwszURI, &ft);
  357. if (sc != S_OK)
  358. {
  359. DebugTrace ("Dav: failed to emit headers\n");
  360. goto ret;
  361. }
  362. }
  363. // Emit the file
  364. //
  365. sc = ScEmitFile (pmu, pwszFile, rgwszContent);
  366. if ( (sc != S_OK) && (sc != W_DAV_PARTIAL_CONTENT) )
  367. {
  368. DebugTrace ("Dav: failed to emit file\n");
  369. goto ret;
  370. }
  371. ret:
  372. return sc;
  373. }
  374. void
  375. GetDirectory (LPMETHUTIL pmu, LPCWSTR pwszUrl)
  376. {
  377. auto_ref_ptr<IMDData> pMDData;
  378. ULONG ulDirBrowsing = 0;
  379. LPCWSTR pwszDftDocList = NULL;
  380. SCODE sc = S_OK;
  381. UINT cchUrl = static_cast<UINT>(wcslen(pwszUrl));
  382. // Before we decide to do anything, we need to check to see what
  383. // kind of default behavior is expected for a directory. Values
  384. // for this level of access are cached from the metabase. Look
  385. // them up and see what to do.
  386. // Get the metabase doc attributes
  387. //
  388. if (FAILED(pmu->HrMDGetData (pwszUrl, pMDData.load())))
  389. {
  390. //
  391. //$REVIEW HrMDGetData() can fail for timeout reasons,
  392. //$REVIEW shouldn't we just pass back the hr that it returns?
  393. //
  394. sc = E_DAV_NO_IIS_READ_ACCESS;
  395. goto ret;
  396. }
  397. ulDirBrowsing = pMDData->DwDirBrowsing();
  398. pwszDftDocList = pMDData->PwszDefaultDocList();
  399. // Try to load default file if allowed, do this only when translate: t
  400. //
  401. if ((ulDirBrowsing & MD_DIRBROW_LOADDEFAULT) && pwszDftDocList && pmu->FTranslated())
  402. {
  403. HDRITER_W hit(pwszDftDocList);
  404. LPCWSTR pwszDoc;
  405. while (NULL != (pwszDoc = hit.PszNext()))
  406. {
  407. auto_com_ptr<IStream> pstm;
  408. CStackBuffer<WCHAR> pwszDocUrl;
  409. CStackBuffer<WCHAR> pwszDocUrlNormalized;
  410. CStackBuffer<WCHAR,MAX_PATH> pwszDocPath;
  411. UINT cchDocUrlNormalized;
  412. UINT cchDoc;
  413. // What happens here is that for EVERY possible default
  414. // document, we are going to see if this document is something
  415. // we can legitimately process.
  416. //
  417. // So first, we need to extend our url and normalize it
  418. // in such a way that we can pin-point the document it uses.
  419. //
  420. cchDoc = static_cast<UINT>(wcslen(pwszDoc));
  421. pwszDocUrl.resize(CbSizeWsz(cchUrl + cchDoc));
  422. memcpy (pwszDocUrl.get(), pwszUrl, cchUrl * sizeof(WCHAR));
  423. memcpy (pwszDocUrl.get() + cchUrl, pwszDoc, (cchDoc + 1) * sizeof(WCHAR));
  424. // Now, someone could have been evil and stuffed path modifiers
  425. // or escaped characters in the default document name. So we
  426. // need to parse those out here -- do both in place. You can't
  427. // do this per-say in the MMC snap in, but MDUTIL does (maybe
  428. // ASDUTIL too).
  429. //
  430. cchDocUrlNormalized = cchUrl + cchDoc + 1;
  431. pwszDocUrlNormalized.resize(cchDocUrlNormalized * sizeof(WCHAR));
  432. sc = ScNormalizeUrl (pwszDocUrl.get(),
  433. &cchDocUrlNormalized,
  434. pwszDocUrlNormalized.get(),
  435. NULL);
  436. if (S_FALSE == sc)
  437. {
  438. pwszDocUrlNormalized.resize(cchDocUrlNormalized * sizeof(WCHAR));
  439. sc = ScNormalizeUrl (pwszDocUrl.get(),
  440. &cchDocUrlNormalized,
  441. pwszDocUrlNormalized.get(),
  442. NULL);
  443. // Since we've given ScNormalizeUrl() the space it asked for,
  444. // we should never get S_FALSE again. Assert this!
  445. //
  446. Assert(S_FALSE != sc);
  447. }
  448. if (FAILED (sc))
  449. continue;
  450. // Translate this into a local path. We should be able to
  451. //
  452. // At most we should go through the processing below twice, as the byte
  453. // count required is an out param.
  454. //
  455. do {
  456. pwszDocPath.resize(cchDocUrlNormalized * sizeof(WCHAR));
  457. sc = pmu->ScStoragePathFromUrl (pwszDocUrlNormalized.get(),
  458. pwszDocPath.get(),
  459. &cchDocUrlNormalized);
  460. } while (sc == S_FALSE);
  461. if (FAILED (sc) || (W_DAV_SPANS_VIRTUAL_ROOTS == sc))
  462. continue;
  463. //$ SECURITY:
  464. //
  465. // Check to see if the destination is really a short
  466. // filename.
  467. //
  468. sc = ScCheckIfShortFileName (pwszDocPath.get(), pmu->HitUser());
  469. if (FAILED (sc))
  470. continue;
  471. //$ SECURITY:
  472. //
  473. // Check to see if the destination is really the default
  474. // data stream via alternate file access.
  475. //
  476. sc = ScCheckForAltFileStream (pwszDocPath.get());
  477. if (FAILED (sc))
  478. continue;
  479. if (static_cast<DWORD>(-1) != GetFileAttributesW (pwszDocPath.get()))
  480. {
  481. DWORD dwAcc = 0;
  482. // See if we have the right access...
  483. //
  484. (void) pmu->ScIISAccess (pwszDocUrlNormalized.get(), MD_ACCESS_READ, &dwAcc);
  485. // Found the default doc, if a child ISAPI doesn't want it,
  486. // then we will handle it.
  487. //
  488. // NOTE: Pass in TRUE for fCheckISAPIAccess to tell this
  489. // function to do all the special access checking.
  490. //
  491. // NOTE: Also pass in FALSE to fKeepQueryString 'cause
  492. // we're re-routing this request to a whole new URI.
  493. //
  494. sc = pmu->ScApplyChildISAPI (pwszDocUrlNormalized.get(), dwAcc, TRUE, FALSE);
  495. if (FAILED(sc))
  496. {
  497. // Either the request has been forwarded, or some bad error occurred.
  498. // In either case, quit here and map the error!
  499. //
  500. goto ret;
  501. }
  502. // Emit the location of the default document
  503. //
  504. pmu->EmitLocation (gc_szContent_Location, pwszDocUrlNormalized.get(), FALSE);
  505. // If we don't have any read access, then there
  506. // is no point in continuing the request.
  507. //
  508. if (0 == (dwAcc & MD_ACCESS_READ))
  509. {
  510. sc = E_DAV_NO_IIS_READ_ACCESS;
  511. goto ret;
  512. }
  513. // Get the file
  514. //
  515. // NOTE: This function can give a WARNING that needs to be mapped
  516. // to get our 207 Partial Content in some cases.
  517. //
  518. sc = ScGetFile (pmu, pwszDocPath.get(), pwszDocUrlNormalized.get());
  519. goto ret;
  520. }
  521. }
  522. }
  523. // If we haven't emitted any other way, see if HTML
  524. // is allowed...
  525. //
  526. if (ulDirBrowsing & MD_DIRBROW_ENABLED)
  527. {
  528. // At one point in time we would generate our own HTML rendering
  529. // of the directories, but at the beginning of NT beta3, the change
  530. // was made to behave the same in HTTPExt as in DavEX, etc.
  531. //
  532. sc = W_DAV_NO_CONTENT;
  533. }
  534. else
  535. {
  536. // Otherwise, report forbidden
  537. // We weren't allowed to browse the dir and there was no
  538. // default doc. IIS maps this scenario to a specific
  539. // suberror (403.2).
  540. //
  541. sc = E_DAV_NO_IIS_READ_ACCESS;
  542. }
  543. ret:
  544. pmu->SetResponseCode (HscFromHresult(sc), NULL, 0, CSEFromHresult(sc));
  545. }
  546. /*
  547. * GetInt()
  548. *
  549. * Purpose:
  550. *
  551. * Win32 file system implementation of the DAV GET method. The
  552. * GET method returns a file from the DAV name space and populates
  553. * the headers with the info found in the file and its meta data. The
  554. * response created indicates the success of the call and contains
  555. * the data from the file.
  556. *
  557. * Parameters:
  558. *
  559. * pmu [in] pointer to the method utility object
  560. */
  561. void
  562. GetInt (LPMETHUTIL pmu)
  563. {
  564. CResourceInfo cri;
  565. LPCWSTR pwszUrl = pmu->LpwszRequestUrl();
  566. LPCWSTR pwszPath = pmu->LpwszPathTranslated();
  567. SCODE sc = S_OK;
  568. // Do ISAPI application and IIS access bits checking
  569. //
  570. sc = pmu->ScIISCheck (pwszUrl, MD_ACCESS_READ, TRUE);
  571. if (FAILED(sc))
  572. {
  573. // Either the request has been forwarded, or some bad error occurred.
  574. // In either case, quit here and map the error!
  575. //
  576. goto ret;
  577. }
  578. // If we have a directory, we are going to return
  579. // HTML, otherwise the extenstion of the file gives
  580. // us what we need.
  581. //
  582. sc = cri.ScGetResourceInfo (pwszPath);
  583. if (FAILED (sc))
  584. goto ret;
  585. // If this is a hidden object, fail with 404 Resource Not Found. This is to be
  586. // consistent with IIS (See NTRAID #247218). They do not allow GET on resources that
  587. // have the HIDDEN bit set.
  588. //
  589. if (cri.FHidden())
  590. {
  591. sc = E_DAV_HIDDEN_OBJECT;
  592. goto ret;
  593. }
  594. // If this is a directory, process it as such, otherwise
  595. // handle the request as if the resource was a file
  596. //
  597. if (cri.FCollection())
  598. {
  599. // GET allows for request url's that end in a trailing slash
  600. // when getting data from a directory. Otherwise it is a bad
  601. // request. If it does not have a trailing slash and refers
  602. // to a directory, then we want to redirect.
  603. //
  604. sc = ScCheckForLocationCorrectness (pmu, cri, REDIRECT);
  605. if (FAILED (sc))
  606. goto ret;
  607. // A return of S_FALSE from above means that a redirect happened
  608. // so we can forego trying to do a GET on the directory.
  609. //
  610. if (S_FALSE == sc)
  611. return;
  612. GetDirectory (pmu, pwszUrl);
  613. return;
  614. }
  615. // GET allows for request url's that end in a trailing slash
  616. // when getting data from a directory. Otherwise it is not found.
  617. // This matches IIS's behavior as of 9/28/98.
  618. //
  619. if (FTrailingSlash (pwszUrl))
  620. {
  621. // Trailing slash on a non-directory just doesn't work
  622. //
  623. sc = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  624. goto ret;
  625. }
  626. // Emit the file
  627. //
  628. sc = ScGetFile (pmu, const_cast<LPWSTR>(pwszPath), pwszUrl);
  629. ret:
  630. pmu->SetResponseCode (HscFromHresult(sc), NULL, 0, CSEFromHresult(sc));
  631. }
  632. /*
  633. * DAVGet()
  634. *
  635. * Purpose:
  636. *
  637. * Win32 file system implementation of the DAV GET method. The
  638. * GET method returns a file from the DAV name space and populates
  639. * the headers with the info found in the file and its meta data. The
  640. * response created indicates the success of the call and contains
  641. * the data from the file.
  642. *
  643. * Parameters:
  644. *
  645. * pmu [in] pointer to the method utility object
  646. */
  647. void
  648. DAVGet (LPMETHUTIL pmu)
  649. {
  650. GetInt (pmu);
  651. }
  652. /*
  653. * DAVHead()
  654. *
  655. * Purpose:
  656. *
  657. * Win32 file system implementation of the DAV HEAD method. The
  658. * HEAD method returns a file from the DAV name space and populates
  659. * the headers with the info found in the file and its meta data.
  660. * The response created indicates the success of the call and contains
  661. * the data from the file.
  662. *
  663. * Parameters:
  664. *
  665. * pmu [in] pointer to the method utility object
  666. */
  667. void
  668. DAVHead (LPMETHUTIL pmu)
  669. {
  670. // The HEAD method should never return any body what so ever...
  671. //
  672. pmu->SupressBody();
  673. // Otherwise, it is just the same as a GET
  674. //
  675. GetInt (pmu);
  676. }