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.

598 lines
16 KiB

  1. /*
  2. * F S B A S E . 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 "_fsmvcpy.h"
  10. // DAV-Base Implementation ---------------------------------------------------
  11. //
  12. /*
  13. * DAVOptions()
  14. *
  15. * Purpose:
  16. *
  17. * Win32 file system implementation of the DAV OPTIONS method. The
  18. * OPTIONS method responds with a comma separated list of supported
  19. * methods by the server.
  20. *
  21. * Parameters:
  22. *
  23. * pmu [in] pointer to the method utility object
  24. */
  25. const CHAR gc_szHttpBase[] = "OPTIONS, TRACE, GET, HEAD";
  26. const CHAR gc_szHttpDelete[] = ", DELETE";
  27. const CHAR gc_szHttpPut[] = ", PUT";
  28. const CHAR gc_szHttpPost[] = ", POST";
  29. const CHAR gc_szDavCopy[] = ", COPY";
  30. const CHAR gc_szDavMove[] = ", MOVE";
  31. const CHAR gc_szDavMkCol[] = ", MKCOL";
  32. const CHAR gc_szDavPropfind[] = ", PROPFIND";
  33. const CHAR gc_szDavProppatch[] = ", PROPPATCH";
  34. const CHAR gc_szDavLocks[] = ", LOCK, UNLOCK";
  35. const CHAR gc_szDavSearch[] = ", SEARCH";
  36. const CHAR gc_szDavNotif[] = ""; // no notification on httpext
  37. const CHAR gc_szDavBatchDelete[] = ""; // no batch methods on httpext
  38. const CHAR gc_szDavBatchCopy[] = ""; // no batch methods on httpext
  39. const CHAR gc_szDavBatchMove[] = ""; // no batch methods on httpext
  40. const CHAR gc_szDavBatchProppatch[] = ""; // no batch methods on httpext
  41. const CHAR gc_szDavBatchPropfind[] = ""; // no batch methods on httpext
  42. const CHAR gc_szDavPublic[] =
  43. "OPTIONS, TRACE, GET, HEAD, DELETE"
  44. ", PUT"
  45. ", POST"
  46. ", COPY, MOVE"
  47. ", MKCOL"
  48. ", PROPFIND, PROPPATCH"
  49. ", LOCK, UNLOCK"
  50. ", SEARCH";
  51. const UINT gc_cbszDavPublic = sizeof(gc_szDavPublic);
  52. const CHAR gc_szCompliance[] = "1, 2";
  53. void
  54. DAVOptions (LPMETHUTIL pmu)
  55. {
  56. CResourceInfo cri;
  57. RESOURCE_TYPE rt = RT_NULL;
  58. SCODE sc = S_OK;
  59. UINT uiErrorDetail = 0;
  60. BOOL fFrontPageWeb = FALSE;
  61. // According to spec, If the request URI is '*', the OPTIONS request
  62. // is intended to apply to the server in general rather than to the
  63. // specific resource. Since a Server's communication options typically
  64. // depend on the resource, the '*' request is only useful as a "ping"
  65. // or "no-op" type of method; it does nothing beyong allowing client
  66. // to test the capabilities of the server.
  67. // So here we choose to return all the methods can ever be accepted
  68. // by this server.
  69. // NOTE: if the request URI is '*', WININET will convert it to '/*'.
  70. // Handle this case also so that WININET clients aren't left in the dust.
  71. //
  72. if (!wcscmp(pmu->LpwszRequestUrl(), L"*") ||
  73. !wcscmp(pmu->LpwszRequestUrl(), L"/*"))
  74. {
  75. // So we simply allow all methods as defined in public
  76. //
  77. pmu->SetResponseHeader (gc_szAllow, gc_szDavPublic);
  78. pmu->SetResponseHeader (gc_szAccept_Ranges, gc_szBytes);
  79. // Set the rest of common headers
  80. //
  81. goto ret;
  82. }
  83. // Do ISAPI application and IIS access bits checking
  84. //
  85. //$ REVIEW - Do we really need read access?
  86. //
  87. sc = pmu->ScIISCheck (pmu->LpwszRequestUrl(), MD_ACCESS_READ);
  88. if (FAILED(sc) && (sc != E_DAV_NO_IIS_READ_ACCESS))
  89. {
  90. // Either the request has been forwarded, or some bad error occurred.
  91. // In either case, quit here and map the error!
  92. //
  93. goto ret;
  94. }
  95. // We can retrieve the file information if only we have MD_ACCESS_READ
  96. // access. otherwise, we better not to try and treat it as non-existing
  97. // resource.
  98. //
  99. if (SUCCEEDED(sc))
  100. {
  101. // Get the file information for this resource
  102. //
  103. sc = cri.ScGetResourceInfo (pmu->LpwszPathTranslated());
  104. if (!FAILED (sc))
  105. {
  106. // If the resource exists, adjust the resource type
  107. // to the one that applies, and check to see if the URL
  108. // and the resource type jibe.
  109. //
  110. rt = cri.FCollection() ? RT_COLLECTION : RT_DOCUMENT;
  111. }
  112. // OPTIONS is allowed to return non-error responses for non-existing
  113. // resources. The response should indicate what a caller could do to
  114. // create a resource at that location. Any other error is an error.
  115. //
  116. else if ((sc != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) &&
  117. (sc != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)))
  118. {
  119. goto ret;
  120. }
  121. // Check state headers here.
  122. //
  123. sc = HrCheckStateHeaders (pmu, pmu->LpwszPathTranslated(), FALSE);
  124. if (FAILED (sc))
  125. {
  126. DebugTrace ("DavFS: If-State checking failed.\n");
  127. goto ret;
  128. }
  129. }
  130. else
  131. {
  132. // Treat E_DAV_NO_IIS_READ_ACCESS as resource not exist
  133. //
  134. Assert (sc == E_DAV_NO_IIS_READ_ACCESS);
  135. sc = S_OK;
  136. }
  137. // BIG NOTE ABOUT LOCKING
  138. //
  139. // Locktoken checking is omitted here because it can't possibly
  140. // make any difference. The "loose" interpretation of lock tokens
  141. // means that we try a method anyway if an invalid lock token
  142. // is provided. Since the calls in this method impl. are
  143. // UNAFFECTED by locks (GetFileAttributesEx, used by
  144. // ScCheckForLocationCorrectness doesn't fail for WRITE locks)
  145. // this method can't fail and the values in the locktoken header
  146. // are irrelevant.
  147. //
  148. // NOTE: We still have to consider if-state-match headers,
  149. // but that is done elsewhere (above -- HrCheckIfStateHeader).
  150. //
  151. // Pass back the "allow" header
  152. //
  153. pmu->SetAllowHeader (rt);
  154. // Pass back the "accept-ranges"
  155. //
  156. pmu->SetResponseHeader (gc_szAccept_Ranges,
  157. (rt == RT_COLLECTION)
  158. ? gc_szNone
  159. : gc_szBytes);
  160. //
  161. // Emit an appropriate "MS_Author_Via" header. If MD_FRONTPAGE_WEB
  162. // was set at the vroot, then use frontpage. Otherwise use "DAV".
  163. //
  164. // MD_FRONTPAGE_WEB must be checked only at the virtual root.
  165. // It is inherited, so you have to be careful in how you check it.
  166. //
  167. // We don't care if this fails: default to author via "DAV"
  168. //
  169. (void) pmu->HrMDIsAuthorViaFrontPageNeeded(&fFrontPageWeb);
  170. // Pass back the "MS_Author_Via" header
  171. //
  172. pmu->SetResponseHeader (gc_szMS_Author_Via,
  173. fFrontPageWeb ? gc_szMS_Author_Via_Dav_Fp : gc_szMS_Author_Via_Dav);
  174. ret:
  175. if (SUCCEEDED(sc))
  176. {
  177. // Supported query languages
  178. //
  179. pmu->SetResponseHeader (gc_szDasl, gc_szSqlQuery);
  180. // Public methods
  181. //
  182. pmu->SetResponseHeader (gc_szPublic, gc_szDavPublic);
  183. // Do the canned bit to the response
  184. //
  185. #ifdef DBG
  186. if (DEBUG_TRACE_TEST(HttpExtDbgHeaders))
  187. {
  188. pmu->SetResponseHeader (gc_szX_MS_DEBUG_DAV, gc_szVersion);
  189. pmu->SetResponseHeader (gc_szX_MS_DEBUG_DAV_Signature, gc_szSignature);
  190. }
  191. #endif
  192. pmu->SetResponseHeader (gc_szDavCompliance, gc_szCompliance);
  193. pmu->SetResponseHeader (gc_szCache_Control, gc_szCache_Control_Private);
  194. }
  195. pmu->SetResponseCode (HscFromHresult(sc), NULL, uiErrorDetail, CSEFromHresult(sc));
  196. }
  197. /*
  198. * DAVMkCol()
  199. *
  200. * Purpose:
  201. *
  202. * Win32 file system implementation of the DAV MKCOL method. The
  203. * MKCOL method creates a collection in the DAV name space and
  204. * optionally populates the collection with the data found in the
  205. * passed in request. The response created indicates the success of
  206. * the call.
  207. *
  208. * Parameters:
  209. *
  210. * pmu [in] pointer to the method utility object
  211. */
  212. void
  213. DAVMkCol (LPMETHUTIL pmu)
  214. {
  215. LPCWSTR pwszPath = pmu->LpwszPathTranslated();
  216. SCODE sc = S_OK;
  217. UINT uiErrorDetail = 0;
  218. LPCWSTR pwsz;
  219. DavTrace ("Dav: creating collection/directory '%ws'\n", pwszPath);
  220. // Do ISAPI application and IIS access bits checking
  221. //
  222. sc = pmu->ScIISCheck (pmu->LpwszRequestUrl(), MD_ACCESS_WRITE);
  223. if (FAILED(sc))
  224. {
  225. // Either the request has been forwarded, or some bad error occurred.
  226. // In either case, quit here and map the error!
  227. //
  228. goto ret;
  229. }
  230. // Check the content-type of the request.
  231. // To quote from the DAV spec:
  232. // A MKCOL request message MAY contain a message body. ...
  233. // If the server receives a MKCOL request entity type it does
  234. // not support or understand it MUST respond with a 415 Unsupported
  235. // Media Type status code.
  236. //
  237. // Since we don't yet support ANY media types, check for ANY
  238. // Content-Type header, or ANY body of any length ('cause no Content-Type
  239. // could still have a valid body of type application/octet-stream),
  240. // and FAIL if found!
  241. //
  242. pwsz = pmu->LpwszGetRequestHeader (gc_szContent_Length, FALSE);
  243. if (pwsz && wcscmp(pwsz, gc_wsz0) ||
  244. pmu->LpwszGetRequestHeader (gc_szContent_Type, FALSE))
  245. {
  246. DebugTrace ("DavFS: Found a body on MKCOL -- 415");
  247. sc = E_DAV_UNKNOWN_CONTENT;
  248. goto ret;
  249. }
  250. // This method is gated by If-xxx headers
  251. //
  252. sc = ScCheckIfHeaders (pmu, pwszPath, FALSE);
  253. if (FAILED (sc))
  254. {
  255. DebugTrace ("Dav: If-xxx failed their check\n");
  256. goto ret;
  257. }
  258. // Check state headers here.
  259. //
  260. sc = HrCheckStateHeaders (pmu, pwszPath, FALSE);
  261. if (FAILED (sc))
  262. {
  263. DebugTrace ("DavFS: If-State checking failed.\n");
  264. goto ret;
  265. }
  266. // BIG NOTE ABOUT LOCKING
  267. //
  268. // Since DAVFS does not yet support locks on directories,
  269. // (and since MKCOL does not have an Overwrite: header)
  270. // this method cannot be affected by passed-in locktokens.
  271. // So, for now, on DAVFS, don't bother to check locktokens.
  272. //
  273. // NOTE: We still have to consider if-state-match headers,
  274. // but that is done elsewhere (above -- HrCheckIfStateHeader).
  275. //
  276. // Create the structured resource, ie. directory
  277. //
  278. if (!DavCreateDirectory (pwszPath, NULL))
  279. {
  280. DWORD dwError = GetLastError();
  281. // If the failure was caused by non-exist path, then
  282. // fail with 403
  283. //
  284. if (ERROR_PATH_NOT_FOUND == dwError)
  285. {
  286. DebugTrace ("Dav: intermediate directories do not exist\n");
  287. sc = E_DAV_NONEXISTING_PARENT;
  288. }
  289. else
  290. {
  291. if ((ERROR_FILE_EXISTS == dwError) || (ERROR_ALREADY_EXISTS == dwError))
  292. sc = E_DAV_COLLECTION_EXISTS;
  293. else
  294. sc = HRESULT_FROM_WIN32 (dwError);
  295. }
  296. goto ret;
  297. }
  298. // Emit the location
  299. //
  300. pmu->EmitLocation (gc_szLocation, pmu->LpwszRequestUrl(), TRUE);
  301. sc = W_DAV_CREATED;
  302. ret:
  303. // Return the response code
  304. //
  305. pmu->SetResponseCode (HscFromHresult(sc), NULL, uiErrorDetail, CSEFromHresult(sc));
  306. }
  307. /*
  308. * DAVDelete()
  309. *
  310. * Purpose:
  311. *
  312. * Win32 file system implementation of the DAV DELETE method. The
  313. * DELETE method responds with a status line and possibly an XML
  314. * web collection of failed deletes.
  315. *
  316. * Parameters:
  317. *
  318. * pmu [in] pointer to the method utility object
  319. */
  320. void
  321. DAVDelete (LPMETHUTIL pmu)
  322. {
  323. CResourceInfo cri;
  324. LPCWSTR pwsz;
  325. LPCWSTR pwszPath = pmu->LpwszPathTranslated();
  326. SCODE sc = S_OK;
  327. UINT uiErrorDetail = 0;
  328. auto_ref_ptr<CXMLEmitter> pxml;
  329. auto_ptr<CParseLockTokenHeader> plth;
  330. auto_ref_ptr<CXMLBody> pxb;
  331. CStackBuffer<WCHAR> pwszMBPath;
  332. // We don't know if we'll have chunked XML response, defer response anyway
  333. //
  334. pmu->DeferResponse();
  335. // Do ISAPI application and IIS access bits checking
  336. //
  337. sc = pmu->ScIISCheck (pmu->LpwszRequestUrl(), MD_ACCESS_WRITE);
  338. if (FAILED(sc))
  339. {
  340. // Either the request has been forwarded, or some bad error occurred.
  341. // In either case, quit here and map the error!
  342. //
  343. goto ret;
  344. }
  345. // Setup the access checking mechanism for deep operations
  346. //
  347. if (NULL == pwszMBPath.resize(pmu->CbMDPathW(pmu->LpwszRequestUrl())))
  348. {
  349. sc = E_OUTOFMEMORY;
  350. goto ret;
  351. }
  352. pmu->MDPathFromUrlW (pmu->LpwszRequestUrl(), pwszMBPath.get());
  353. // Get the resource information
  354. //
  355. sc = cri.ScGetResourceInfo (pwszPath);
  356. if (FAILED (sc))
  357. goto ret;
  358. // Check to see that the location is correct
  359. //
  360. sc = ScCheckForLocationCorrectness (pmu, cri, NO_REDIRECT);
  361. if (FAILED (sc))
  362. goto ret;
  363. // This method is gated ny the "if-xxx" headers
  364. //
  365. sc = ScCheckIfHeaders (pmu, cri.PftLastModified(), FALSE);
  366. if (FAILED (sc))
  367. goto ret;
  368. // Check state headers here.
  369. //
  370. sc = HrCheckStateHeaders (pmu, pwszPath, FALSE);
  371. if (FAILED (sc))
  372. {
  373. DebugTrace ("DavFS: If-State checking failed.\n");
  374. goto ret;
  375. }
  376. // If there are locktokens, feed them to a parser object.
  377. //
  378. pwsz = pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  379. if (pwsz)
  380. {
  381. plth = new CParseLockTokenHeader (pmu, pwsz);
  382. Assert(plth.get());
  383. plth->SetPaths (pwszPath, NULL);
  384. }
  385. // If the resource is a collection, iterate through
  386. // and do a recursive delete
  387. //
  388. if (cri.FCollection())
  389. {
  390. CAuthMetaOp moAuth(pmu, pwszMBPath.get(), pmu->MetaData().DwAuthorization());
  391. CAccessMetaOp moAccess(pmu, pwszMBPath.get(), MD_ACCESS_READ|MD_ACCESS_WRITE);
  392. CIPRestrictionMetaOp moIP(pmu, pwszMBPath.get());
  393. BOOL fDeleted = FALSE;
  394. DWORD dwAcc = 0;
  395. LONG lDepth = pmu->LDepth(DEPTH_INFINITY);
  396. // The client must not submit a depth header with any value
  397. // but Infinity
  398. //
  399. if ((DEPTH_INFINITY != lDepth) &&
  400. (DEPTH_INFINITY_NOROOT != lDepth))
  401. {
  402. sc = E_DAV_INVALID_HEADER;
  403. goto ret;
  404. }
  405. // Make sure we have access. The access will come back out and we
  406. // can then pass it into the call to delete.
  407. //
  408. (void) pmu->ScIISAccess (pmu->LpwszRequestUrl(),
  409. MD_ACCESS_READ|MD_ACCESS_WRITE,
  410. &dwAcc);
  411. // Check for deep operation access blocking
  412. //
  413. sc = moAccess.ScMetaOp();
  414. if (FAILED (sc))
  415. goto ret;
  416. sc = moAuth.ScMetaOp();
  417. if (FAILED (sc))
  418. goto ret;
  419. sc = moIP.ScMetaOp();
  420. if (FAILED (sc))
  421. goto ret;
  422. // Create an XML doc, NOT chunked
  423. //
  424. pxb.take_ownership (new CXMLBody (pmu));
  425. pxml.take_ownership(new CXMLEmitter(pxb.get()));
  426. // Must set all the headers before XML emitting start
  427. //
  428. pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
  429. pmu->SetResponseCode (HscFromHresult(W_DAV_PARTIAL_SUCCESS),
  430. NULL,
  431. 0,
  432. CSEFromHresult(W_DAV_PARTIAL_SUCCESS));
  433. // Delete the directory
  434. //
  435. DavTrace ("Dav: deleting '%ws'\n", pwszPath);
  436. sc = ScDeleteDirectoryAndChildren (pmu,
  437. pmu->LpwszRequestUrl(),
  438. pwszPath,
  439. moAccess.FAccessBlocked() || moAuth.FAccessBlocked() || moIP.FAccessBlocked(),
  440. dwAcc,
  441. lDepth,
  442. *pxml,
  443. NULL, // translations are pmu based
  444. &fDeleted,
  445. plth.get(),
  446. TRUE); // drop locks
  447. }
  448. else
  449. {
  450. // If we have a locktoken for this file, drop the lock before
  451. // trying the delete.
  452. //
  453. if (plth.get())
  454. {
  455. LARGE_INTEGER liLockID;
  456. sc = plth->HrGetLockIdForPath (pwszPath, GENERIC_WRITE, &liLockID);
  457. if (SUCCEEDED(sc))
  458. {
  459. // Drop the lock
  460. //
  461. sc = CSharedLockMgr::Instance().HrDeleteLock(pmu->HitUser(),
  462. liLockID);
  463. if (FAILED(sc))
  464. {
  465. goto ret;
  466. }
  467. }
  468. else if (E_DAV_LOCK_NOT_FOUND != sc)
  469. {
  470. goto ret;
  471. }
  472. }
  473. // Delete the file that is referred to by the URI
  474. //
  475. DavTrace ("Dav: deleting '%ws'\n", pwszPath);
  476. if (!DavDeleteFile (pwszPath))
  477. {
  478. DebugTrace ("Dav: failed to delete file\n");
  479. sc = HRESULT_FROM_WIN32 (GetLastError());
  480. // Special work for 416 Locked responses -- fetch the
  481. // comment & set that as the response body.
  482. //
  483. if (FLockViolation (pmu,
  484. GetLastError(),
  485. pwszPath,
  486. GENERIC_READ | GENERIC_WRITE))
  487. {
  488. sc = E_DAV_LOCKED;
  489. }
  490. }
  491. }
  492. if (SUCCEEDED (sc))
  493. {
  494. // Delete the content-types
  495. //
  496. //$REVIEW I don't believe we need to do this any longer because
  497. //$REVIEW MOVE and COPY both unconditionally blow away the destination
  498. //$REVIEW metadata before copying over the source, so there is no
  499. //$REVIEW chance that the resulting content type will be wrong.
  500. //
  501. CContentTypeMetaOp amoContent(pmu, pwszMBPath.get(), NULL, TRUE);
  502. (void) amoContent.ScMetaOp();
  503. }
  504. // Only continue on complete success
  505. //
  506. if (sc != S_OK)
  507. goto ret;
  508. ret:
  509. if (pxml.get() && pxml->PxnRoot())
  510. {
  511. pxml->Done();
  512. // Note we must not emit any headers after XML chunking starts
  513. }
  514. else
  515. pmu->SetResponseCode (HscFromHresult(sc), NULL, uiErrorDetail, CSEFromHresult(sc));
  516. pmu->SendCompleteResponse();
  517. }
  518. /*
  519. * DAVPost()
  520. *
  521. * Purpose:
  522. *
  523. * Win32 file system implementation of the DAV POST method. The
  524. * POST method creates a file in the DAV name space and populates
  525. * the file with the data found in the passed in request. The
  526. * response created indicates the success of the call.
  527. *
  528. * Parameters:
  529. *
  530. * pmu [in] pointer to the method utility object
  531. */
  532. void
  533. DAVPost (LPMETHUTIL pmu)
  534. {
  535. // DAVPost() is really an unknown/unsupported method
  536. // at this point...
  537. //
  538. DAVUnsupported (pmu);
  539. }