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.

2884 lines
68 KiB

  1. /*
  2. * F S M E T A . C P P
  3. *
  4. * Sources file system implementation of DAV-Meta
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davfs.h"
  9. // CFSFind -------------------------------------------------------------------
  10. //
  11. SCODE
  12. CFSFind::ScAddProp (LPCWSTR, LPCWSTR pwszProp, BOOL)
  13. {
  14. enum { cProps = 8 };
  15. // If this is our first time in, we will need to allocate
  16. // space for all of the properties we are expecting to get
  17. // added over the coarse of this operation.
  18. //
  19. if (m_ft == FIND_NONE)
  20. {
  21. // Note that we have started requesting specific properties
  22. //
  23. m_ft = FIND_SPECIFIC;
  24. }
  25. else if (m_ft != FIND_SPECIFIC)
  26. {
  27. // If we are not finding specfic properties and somebody asked
  28. // for one, then bts (by the spec) this should consititute an
  29. // error.
  30. //
  31. return E_DAV_PROPFIND_TYPE_UNEXPECTED;
  32. }
  33. // See if there is room at the in...
  34. //
  35. if (m_cMaxProps == m_cProps)
  36. {
  37. UINT cb;
  38. // Allocate enough space for the next block of properties
  39. //
  40. m_cMaxProps = m_cProps + cProps;
  41. cb = m_cMaxProps * sizeof(PROPVARIANT);
  42. m_rgwszProps.realloc (cb);
  43. }
  44. // If this is the getcontenttype property, then we need to remember
  45. // its location for use when providing default values...
  46. //
  47. if (!wcscmp (pwszProp, gc_wszProp_iana_getcontenttype))
  48. m_ip_getcontenttype = m_cProps;
  49. // Set the property up as one to process.
  50. //
  51. Assert (m_cProps < m_cMaxProps);
  52. m_rgwszProps[m_cProps++] = AppendChainedSz (m_csb, pwszProp);
  53. return S_OK;
  54. }
  55. SCODE
  56. CFSFind::ScFind (CXMLEmitter& msr,
  57. IMethUtil * pmu,
  58. CFSProp& fpt)
  59. {
  60. SCODE scFind;
  61. SCODE sc = S_OK;
  62. // Setup the emitting of the response. This will construct
  63. // an XML node that looks like:
  64. //
  65. // <multistatus>
  66. // <response>
  67. // <href>http:/www....</>
  68. //
  69. //
  70. CEmitterNode enItem;
  71. CEmitterNode en;
  72. sc = msr.ScSetRoot (gc_wszMultiResponse);
  73. if (FAILED (sc))
  74. goto ret;
  75. sc = enItem.ScConstructNode (msr, msr.PxnRoot(), gc_wszResponse);
  76. if (FAILED (sc))
  77. goto ret;
  78. // If they havent asked for anything, then we should return an
  79. // error
  80. //
  81. if (m_ft == FIND_NONE)
  82. {
  83. //$REVIEW: is it really correct to NOT add the HREF node here? --BeckyAn 6July1999
  84. return E_DAV_EMPTY_FIND_REQUEST;
  85. }
  86. // If the request is an for a specific set of properties, then
  87. // this is pretty easy...
  88. //
  89. else if (m_ft == FIND_SPECIFIC)
  90. {
  91. Assert (m_cProps);
  92. Assert (m_rgwszProps);
  93. sc = ScAddHref (enItem,
  94. pmu,
  95. fpt.PwszPath(),
  96. fpt.FCollection(),
  97. fpt.PcvrTranslation());
  98. if (FAILED (sc))
  99. goto ret;
  100. // Get all the properties by name
  101. //
  102. scFind = fpt.ScGetSpecificProps (msr,
  103. enItem,
  104. m_cProps,
  105. (LPCWSTR*)m_rgwszProps.get(),
  106. m_ip_getcontenttype);
  107. if (FAILED (scFind))
  108. {
  109. (void) ScAddStatus (&enItem, HscFromHresult(scFind));
  110. goto ret;
  111. }
  112. }
  113. // If the request is an for all properties or all names, then again,
  114. // this is pretty easy...
  115. //
  116. else
  117. {
  118. Assert ((m_ft == FIND_ALL) || (m_ft == FIND_NAMES));
  119. // Get all props or all names
  120. //
  121. scFind = fpt.ScGetAllProps (msr, enItem, m_ft == FIND_ALL);
  122. if (FAILED (scFind) && (scFind != E_DAV_SMB_PROPERTY_ERROR))
  123. {
  124. (void) ScAddStatus (&enItem, HscFromHresult(scFind));
  125. goto ret;
  126. }
  127. }
  128. ret:
  129. return sc;
  130. }
  131. // IPreloadNamespaces
  132. //
  133. SCODE
  134. CFSFind::ScLoadNamespaces(CXMLEmitter * pmsr)
  135. {
  136. SCODE sc = S_OK;
  137. UINT iProp;
  138. // Load common namespaces
  139. //
  140. sc = pmsr->ScPreloadNamespace (gc_wszDav);
  141. if (FAILED(sc))
  142. goto ret;
  143. sc = pmsr->ScPreloadNamespace (gc_wszLexType);
  144. if (FAILED(sc))
  145. goto ret;
  146. sc = pmsr->ScPreloadNamespace (gc_wszXml_V);
  147. if (FAILED(sc))
  148. goto ret;
  149. // Add more namespaces
  150. switch (m_ft)
  151. {
  152. case FIND_SPECIFIC:
  153. for (iProp = 0; iProp < m_cProps; iProp++)
  154. {
  155. sc = pmsr->ScPreloadNamespace (m_rgwszProps[iProp]);
  156. if (FAILED(sc))
  157. goto ret;
  158. }
  159. break;
  160. case FIND_ALL:
  161. case FIND_NAMES:
  162. // Now that we don't have a way to predict what namespaces to
  163. // be used.
  164. // Per resource level namespaces will be added on <DAV:response>
  165. // node later
  166. break;
  167. default:
  168. AssertSz (FALSE, "Unknown propfind type");
  169. // fall through
  170. case FIND_NONE:
  171. sc = E_DAV_EMPTY_FIND_REQUEST;
  172. goto ret;
  173. }
  174. ret:
  175. return sc;
  176. }
  177. // CFSPatch ------------------------------------------------------------------
  178. //
  179. SCODE
  180. CFSPatch::ScDeleteProp (LPCWSTR, LPCWSTR pwszProp)
  181. {
  182. enum { cProps = 8 };
  183. UINT irp;
  184. // We cannot delete any reserved properties, so let's
  185. // just shortcut this here and now...
  186. //
  187. if (CFSProp::FReservedProperty (pwszProp,
  188. CFSProp::RESERVED_SET,
  189. &irp))
  190. {
  191. // Take ownership of the bstr as well
  192. //
  193. return m_csn.ScAddErrorStatus (HSC_FORBIDDEN, pwszProp);
  194. }
  195. // Make sure there is room at the inn...
  196. //
  197. if (m_cMaxDeleteProps == m_cDeleteProps)
  198. {
  199. UINT cb;
  200. // Allocate enough space for all the properties names
  201. // we want to delete.
  202. //
  203. m_cMaxDeleteProps = m_cDeleteProps + cProps;
  204. cb = m_cMaxDeleteProps * sizeof(BSTR);
  205. m_rgwszDeleteProps.realloc (cb);
  206. }
  207. // Set the property up as one to process.
  208. //
  209. Assert (m_cDeleteProps < m_cMaxDeleteProps);
  210. m_rgwszDeleteProps[m_cDeleteProps++] = AppendChainedSz(m_csb, pwszProp);
  211. return S_OK;
  212. }
  213. SCODE
  214. CFSPatch::ScSetProp (LPCWSTR,
  215. LPCWSTR pwszProp,
  216. auto_ref_ptr<CPropContext>& pPropCtx)
  217. {
  218. enum { cProps = 8 };
  219. UINT irp;
  220. // We cannot set any reserved properties, so let's
  221. // just shortcut this here and now...
  222. //
  223. if (CFSProp::FReservedProperty (pwszProp,
  224. CFSProp::RESERVED_SET,
  225. &irp))
  226. {
  227. // Take ownership of the bstr as well
  228. //
  229. return m_csn.ScAddErrorStatus (HSC_FORBIDDEN, pwszProp);
  230. }
  231. // Make sure there is room at the inn...
  232. //
  233. if (m_cMaxSetProps == m_cSetProps)
  234. {
  235. UINT cb;
  236. // Allocate enough space for all the properties we
  237. // might want to set
  238. //
  239. m_cMaxSetProps = m_cSetProps + cProps;
  240. cb = m_cMaxSetProps * sizeof(PROPVARIANT);
  241. m_rgvSetProps.realloc (cb);
  242. // Make sure the VARIANT are properly initialized
  243. // (only initialize the newly added space).
  244. //
  245. ZeroMemory (&m_rgvSetProps[m_cSetProps],
  246. sizeof(PROPVARIANT) * cProps);
  247. // ... and their names.
  248. //
  249. cb = m_cMaxSetProps * sizeof(LPCWSTR);
  250. m_rgwszSetProps.realloc (cb);
  251. }
  252. // Set the property up as one to process.
  253. //
  254. Assert (m_cSetProps < m_cMaxSetProps);
  255. m_rgwszSetProps[m_cSetProps] = AppendChainedSz(m_csb, pwszProp);
  256. pPropCtx = new CFSPropContext(&m_rgvSetProps[m_cSetProps]);
  257. m_cSetProps++;
  258. return S_OK;
  259. }
  260. SCODE
  261. CFSPatch::ScPatch (CXMLEmitter& msr,
  262. IMethUtil * pmu,
  263. CFSProp& fpt)
  264. {
  265. SCODE sc = S_OK;
  266. SCODE scSet = S_OK;
  267. SCODE scDelete = S_OK;
  268. CEmitterNode enItem;
  269. // If there are no properties at all, reserved or otherwise,
  270. // we want to fail the call with BAD_REQUEST
  271. //
  272. if ((m_cSetProps == 0) &&
  273. (m_cDeleteProps == 0) &&
  274. m_csn.FEmpty())
  275. {
  276. return E_DAV_EMPTY_PATCH_REQUEST;
  277. }
  278. // Setup the emitting of the response. This will construct
  279. // an XML node that looks like:
  280. //
  281. // <multistatus>
  282. // <response>
  283. // <href>http:/www....</>
  284. //
  285. //
  286. sc = msr.ScSetRoot (gc_wszMultiResponse);
  287. if (FAILED (sc))
  288. goto ret;
  289. sc = enItem.ScConstructNode (msr, msr.PxnRoot(), gc_wszResponse);
  290. if (FAILED (sc))
  291. goto ret;
  292. sc = ScAddHref (enItem,
  293. pmu,
  294. fpt.PwszPath(),
  295. fpt.FCollection(),
  296. fpt.PcvrTranslation());
  297. if (FAILED (sc))
  298. goto ret;
  299. // If the client requested any of the reserved properties, we know
  300. // that they will fail and we also know that everything else will fail
  301. // as well, so we might as well handle that here...
  302. //
  303. if (!m_csn.FEmpty())
  304. {
  305. //$ REVIEW:
  306. //
  307. // If the possibly successful properties need to be
  308. // marked as a failure as well (HSC_METHOD_FAILURE),
  309. // then that would happen here.
  310. //
  311. //NT242086: Now that we've got a reponse node, we should
  312. //added to the response.
  313. //
  314. sc = m_csn.ScEmitErrorStatus (enItem);
  315. goto ret;
  316. }
  317. // If there are no reserved properties we have a pretty good bet
  318. // at setting these props...
  319. //
  320. scSet = fpt.ScSetProps (m_csn,
  321. m_cSetProps,
  322. m_rgwszSetProps.get(),
  323. m_rgvSetProps);
  324. if (FAILED (scSet))
  325. {
  326. sc = scSet;
  327. goto ret;
  328. }
  329. // ... and deleting these props.
  330. //
  331. scDelete = fpt.ScDeleteProps (m_csn,
  332. m_cDeleteProps,
  333. m_rgwszDeleteProps.get());
  334. if (FAILED (scDelete))
  335. {
  336. sc = scDelete;
  337. goto ret;
  338. }
  339. // If the possibly successful properties need to be
  340. // marked as a failure as well (HSC_METHOD_FAILURE),
  341. // then that would happen here. Either way, if there
  342. // is a failure, then we do not want to commit the
  343. // changes.
  344. //
  345. if ((scSet == S_FALSE) || (scDelete == S_FALSE))
  346. goto ret;
  347. // Commit the changes to the property container
  348. //
  349. sc = fpt.ScPersist();
  350. if (FAILED (sc))
  351. goto ret;
  352. // Emit the response,
  353. //
  354. sc = m_csn.ScEmitErrorStatus (enItem);
  355. if (FAILED(sc))
  356. goto ret;
  357. ret:
  358. return sc;
  359. }
  360. CFSPatch::~CFSPatch()
  361. {
  362. // Make sure all the propvariants are cleaned up...
  363. //
  364. for (UINT i = 0; i < m_cSetProps; i++)
  365. PropVariantClear (&m_rgvSetProps[i]);
  366. }
  367. SCODE
  368. CFSPatch::ScLoadNamespaces (CXMLEmitter * pmsr)
  369. {
  370. SCODE sc = S_OK;
  371. UINT iProp;
  372. // Load common namespaces
  373. //
  374. sc = pmsr->ScPreloadNamespace (gc_wszDav);
  375. if (FAILED(sc))
  376. goto ret;
  377. // Add namespaces for set props
  378. //
  379. for (iProp = 0; iProp < m_cSetProps; iProp++)
  380. {
  381. sc = pmsr->ScPreloadNamespace (m_rgwszSetProps[iProp]);
  382. if (FAILED(sc))
  383. goto ret;
  384. }
  385. // And delete props
  386. //
  387. for (iProp = 0; iProp < m_cDeleteProps; iProp++)
  388. {
  389. sc = pmsr->ScPreloadNamespace (m_rgwszDeleteProps[iProp]);
  390. if (FAILED(sc))
  391. goto ret;
  392. }
  393. ret:
  394. return sc;
  395. }
  396. // CFSProp -------------------------------------------------------------------
  397. //
  398. SCODE
  399. CFSProp::ScGetPropsInternal (ULONG cProps,
  400. LPCWSTR* rgwszPropNames,
  401. PROPVARIANT* rgvar,
  402. LONG ip_getcontenttype)
  403. {
  404. SCODE sc = S_OK;
  405. // There really should only be one scenario where this could happen
  406. // -- and it is a cheap test, so it is worth doing. The case where
  407. // we might see an invalid pbag is when the document extisted, but
  408. // there was no existing property set to impose the pbag on. Other
  409. // than that, OLE is always giving us a property bag, regardless of
  410. // whether the target drive can support it.
  411. //
  412. if (FInvalidPbag())
  413. return sc;
  414. // We better be good to go...
  415. //
  416. sc = m_pbag->ReadMultiple (cProps,
  417. rgwszPropNames,
  418. rgvar,
  419. NULL);
  420. // If we succeeded, and the getcontenttype property was requested,
  421. // we may need to do some special processing
  422. //
  423. if (SUCCEEDED (sc) && (ip_getcontenttype != -1))
  424. {
  425. // We want to make sure that getcontenttype gets filled in
  426. //
  427. if (rgvar[ip_getcontenttype].vt == VT_EMPTY)
  428. {
  429. CStackBuffer<WCHAR> pwszT;
  430. LPWSTR pwszContentType;
  431. UINT cch = 40;
  432. // No content type was explicitly set in the props.
  433. // Fetch the default based on the file extension
  434. // (fetching from our metabase-content-type-cache).
  435. //
  436. do {
  437. if (NULL == pwszT.resize(CbSizeWsz(cch)))
  438. {
  439. sc = E_OUTOFMEMORY;
  440. goto ret;
  441. }
  442. } while (!m_pmu->FGetContentType (m_pwszURI, pwszT.get(), &cch));
  443. // Return the mapped content type
  444. //
  445. rgvar[ip_getcontenttype].vt = VT_LPWSTR;
  446. // Must use task memory, as it will be freed by PropVariantClear
  447. //
  448. pwszContentType = (LPWSTR) CoTaskMemAlloc (cch * sizeof(WCHAR));
  449. if (NULL == pwszContentType)
  450. {
  451. MCDTrace ("Dav: MCD: CFSProp::ScGetPropsInternal() - CoTaskMemAlloc() failed to allocate %d bytes\n", cch * sizeof(WCHAR));
  452. sc = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  453. goto ret;
  454. }
  455. rgvar[ip_getcontenttype].pwszVal = pwszContentType;
  456. memcpy(pwszContentType, pwszT.get(), cch * sizeof(WCHAR));
  457. // In the case where this was the only property requested, make
  458. // sure that our return code it correct.
  459. //
  460. if (cProps == 1)
  461. {
  462. Assert (ip_getcontenttype == 0);
  463. Assert (sc == S_FALSE);
  464. sc = S_OK;
  465. }
  466. }
  467. }
  468. else
  469. {
  470. // This is the common path for when we are trying to access
  471. // something over an SMB, but the host cannot support the
  472. // request (it is not an NT5 NTFS machine).
  473. //
  474. if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
  475. sc = E_DAV_SMB_PROPERTY_ERROR;
  476. }
  477. ret:
  478. return sc;
  479. }
  480. BOOL
  481. CFSProp::FReservedProperty (LPCWSTR pwszProp, RESERVED_TYPE rt, UINT* prp)
  482. {
  483. UINT irp;
  484. CRCWsz wsz(pwszProp);
  485. // Search for the property in the list of local
  486. // properties.
  487. //
  488. Assert (CElems(sc_rp) == sc_crp_set_reserved);
  489. for (irp = 0; irp < sc_crp_set_reserved; irp++)
  490. {
  491. // If the crc and the strings match...
  492. //
  493. if ((wsz.m_dwCRC == sc_rp[irp].dwCRC) &&
  494. !wcscmp (wsz.m_pwsz, sc_rp[irp].pwsz))
  495. {
  496. break;
  497. }
  498. }
  499. // Setup the return
  500. //
  501. Assert (sc_crp_set_reserved != iana_rp_content_type);
  502. *prp = irp;
  503. return (irp < static_cast<UINT>((rt == RESERVED_GET) ? sc_crp_get_reserved : sc_crp_set_reserved));
  504. }
  505. SCODE
  506. CFSProp::ScGetReservedProp (CXMLEmitter& xml,
  507. CEmitterNode& enParent,
  508. UINT irp,
  509. BOOL fGetValues)
  510. {
  511. CEmitterNode en;
  512. CStackBuffer<WCHAR> wszBuf;
  513. LARGE_INTEGER li;
  514. LPCWSTR pwszType = NULL;
  515. LPWSTR pwsz = NULL;
  516. SCODE sc = S_OK;
  517. SYSTEMTIME st;
  518. Assert (irp <= sc_crp_get_reserved);
  519. Assert (sc_crp_get_reserved == iana_rp_content_type);
  520. Assert (CElems(sc_rp) == sc_crp_set_reserved);
  521. // Only generate values if the caller wants them
  522. //
  523. if (fGetValues)
  524. {
  525. // Switch across the reserved properties generating
  526. // a value for the property
  527. //
  528. switch (irp)
  529. {
  530. case iana_rp_etag:
  531. Assert(m_pmu);
  532. if (FETagFromFiletime (m_cri.PftLastModified(), wszBuf.get(), m_pmu->GetEcb()))
  533. {
  534. pwsz = wszBuf.get();
  535. }
  536. break;
  537. case iana_rp_displayname:
  538. // The filename/displayname is simply the name of the file
  539. // and we should be able to pick it off from the path with
  540. // little and/or no trouble at all. However, we will use
  541. // the URI instead. We do this such that the displayname
  542. // for a vroot is the name of the vroot and not the name of
  543. // the physical disk directory.
  544. //
  545. pwsz = wcsrchr (m_pwszURI, L'/');
  546. if (NULL == pwsz)
  547. {
  548. // Arrgh. If there was no path separator in the filename
  549. // I don't know that we can really give a reasonable value
  550. // for this file.
  551. //
  552. TrapSz ("resource path has no slashes....");
  553. return S_FALSE;
  554. }
  555. // One more check. If this is a directory path,
  556. // they might have a trailing slash. If that is what we're
  557. // pointing to right now (next char is NULL), back up to the
  558. // next delimiter to get the real item name.
  559. //
  560. if (L'\0' == pwsz[1])
  561. {
  562. // This better be a collection. Although it may not
  563. // be if the client mis-terminated his/her url
  564. //
  565. // There is a special case that we need to check for
  566. // here. It is possible that the URI was strictly "/"
  567. // which means that if we continue this processing, the
  568. // displayname and/or the filename would be empty or
  569. // non-existant. In this case only, return "/" as the
  570. // display name.
  571. //
  572. if (m_pwszURI == pwsz)
  573. {
  574. pwsz = L"/";
  575. }
  576. else
  577. {
  578. // Now we have to copy the string, to rip off that
  579. // trailing slash we found in the step above.
  580. // We want to remove the final slash because this is the
  581. // displayname, not a URI.
  582. //
  583. LPCWSTR pwszEnd;
  584. UINT cchNew;
  585. for (pwszEnd = pwsz--; pwsz > m_pwszURI; pwsz--)
  586. if (L'/' == *pwsz)
  587. break;
  588. if (L'/' != *pwsz)
  589. {
  590. // Arrgh. If there was no path separator in the
  591. // filename I don't know that we can really give
  592. // a reasonable value for this file.
  593. //
  594. TrapSz ("resource path has no slashes (redux)....");
  595. return S_FALSE;
  596. }
  597. // At this point, the segment defined by (pwsz + 1, pwszEnd)
  598. // names the resource.
  599. //
  600. cchNew = static_cast<UINT>(pwszEnd - ++pwsz);
  601. if (NULL == wszBuf.resize(CbSizeWsz(cchNew)))
  602. {
  603. sc = E_OUTOFMEMORY;
  604. goto ret;
  605. }
  606. memcpy(wszBuf.get(), pwsz, cchNew * sizeof(WCHAR));
  607. wszBuf[cchNew] = L'\0';
  608. pwsz = wszBuf.get();
  609. }
  610. }
  611. else
  612. {
  613. // At this point, the segment defined by (pwsz + 1, '\0'] names
  614. // the resource.
  615. //
  616. pwsz++;
  617. }
  618. break;
  619. case iana_rp_resourcetype:
  620. // Create the element to pass back
  621. //
  622. sc = en.ScConstructNode (xml, enParent.Pxn(), sc_rp[irp].pwsz);
  623. if (FAILED (sc))
  624. goto ret;
  625. if (m_cri.FCollection())
  626. {
  627. CEmitterNode enSub;
  628. sc = en.ScAddNode (gc_wszCollection, enSub);
  629. if (FAILED (sc))
  630. goto ret;
  631. }
  632. goto ret;
  633. case iana_rp_content_length:
  634. m_cri.FileSize(li);
  635. pwszType = gc_wszDavType_Int;
  636. //$ REVIEW: negative values of _int64 seem to have problems in
  637. // the __i64tow() API. Handle those cases ourselves.
  638. //
  639. // In this instance, we shouldn't have to worry about it because
  640. // the content-length *shouldn't* ever be negative. We'll assert
  641. // that this is the case.
  642. //
  643. Assert (li.QuadPart >= 0);
  644. _i64tow (li.QuadPart, wszBuf.get(), 10);
  645. pwsz = wszBuf.get();
  646. break;
  647. case iana_rp_creation_date:
  648. FileTimeToSystemTime (m_cri.PftCreation(), &st);
  649. if (FGetDateIso8601FromSystime (&st, wszBuf.get(), wszBuf.size()))
  650. {
  651. pwszType = gc_wszDavType_Date_ISO8601;
  652. pwsz = wszBuf.get();
  653. }
  654. break;
  655. case iana_rp_last_modified:
  656. FileTimeToSystemTime (m_cri.PftLastModified(), &st);
  657. if (FGetDateRfc1123FromSystime (&st, wszBuf.get(), wszBuf.size()))
  658. {
  659. pwszType = gc_wszDavType_Date_Rfc1123;
  660. pwsz = wszBuf.get();
  661. }
  662. break;
  663. case iana_rp_supportedlock:
  664. case iana_rp_lockdiscovery:
  665. // Get the prop from the lock cache (and related subsystem calls).
  666. //
  667. sc = HrGetLockProp (m_pmu,
  668. sc_rp[irp].pwsz,
  669. m_pwszPath,
  670. m_cri.FCollection() ? RT_COLLECTION : RT_DOCUMENT,
  671. xml,
  672. enParent);
  673. // Regardless of error or success, we are done here. If we
  674. // succeeded, then the pel has already been constructed and
  675. // is ready to pass back. Otherwise, we just want to report
  676. // the error.
  677. //
  678. goto ret;
  679. case iana_rp_ishidden:
  680. pwszType = gc_wszDavType_Boolean;
  681. _itow (!!m_cri.FHidden(), wszBuf.get(), 10);
  682. pwsz = wszBuf.get();
  683. break;
  684. case iana_rp_iscollection:
  685. pwszType = gc_wszDavType_Boolean;
  686. _itow (!!m_cri.FCollection(), wszBuf.get(), 10);
  687. pwsz = wszBuf.get();
  688. break;
  689. // Special case: getcontenttype should really be stored, but there
  690. // are some cases where the file may live in such a place as there
  691. // would be no property stream available to store the value in.
  692. //
  693. case iana_rp_content_type:
  694. // Get the content-type if it was not stored in the property
  695. // stream.
  696. //
  697. for (UINT cch = wszBuf.celems();;)
  698. {
  699. if (NULL == wszBuf.resize(CbSizeWsz(cch)))
  700. {
  701. sc = E_OUTOFMEMORY;
  702. goto ret;
  703. }
  704. if (m_pmu->FGetContentType(m_pwszURI, wszBuf.get(), &cch))
  705. break;
  706. }
  707. }
  708. }
  709. // Create the element to pass back
  710. //
  711. sc = en.ScConstructNode (xml, enParent.Pxn(), sc_rp[irp].pwsz, pwsz, pwszType);
  712. if (FAILED (sc))
  713. goto ret;
  714. ret:
  715. return sc;
  716. }
  717. SCODE
  718. CFSProp::ScGetSpecificProps (CXMLEmitter& msr,
  719. CEmitterNode& enItem,
  720. ULONG cProps,
  721. LPCWSTR* rgwszPropNames,
  722. LONG ip_getcontenttype)
  723. {
  724. // safe_propvariant_array ----------------------------------------------------
  725. //
  726. // Used to make sure the array of VARIANT can always be safely freed
  727. //
  728. class safe_propvariant_array
  729. {
  730. PROPVARIANT * m_rgv;
  731. ULONG m_cv;
  732. public:
  733. safe_propvariant_array (PROPVARIANT* rgv, ULONG cv)
  734. : m_rgv(rgv),
  735. m_cv(cv)
  736. {
  737. memset (rgv, 0, sizeof(PROPVARIANT) * cv);
  738. }
  739. ~safe_propvariant_array ()
  740. {
  741. ULONG i;
  742. for (i = 0; i < m_cv; i++)
  743. PropVariantClear(&m_rgv[i]);
  744. }
  745. };
  746. SCODE sc = S_OK;
  747. CStackBuffer<PROPVARIANT> rgv;
  748. UINT iv;
  749. CStatusCache csn;
  750. CEmitterNode enPropStat;
  751. CEmitterNode enPropOK;
  752. // allocate space to hold an array of variants and stuff it into
  753. // a safe_variant_array to ensure cleanup
  754. //
  755. rgv.resize(sizeof(PROPVARIANT) * cProps);
  756. safe_propvariant_array sva(rgv.get(), cProps);
  757. sc = csn.ScInit();
  758. if (FAILED(sc))
  759. goto ret;
  760. // Get the properties
  761. //
  762. sc = ScGetPropsInternal (cProps, rgwszPropNames, rgv.get(), ip_getcontenttype);
  763. if (FAILED(sc))
  764. {
  765. // When getting properties, it is perfectly OK to ignore SMB errors
  766. // and treat the file as if it were hosted on a FAT drive
  767. //
  768. if (sc == E_DAV_SMB_PROPERTY_ERROR)
  769. sc = S_OK;
  770. // What this means is that the default not-found processing should
  771. // kick in.
  772. //
  773. }
  774. // Rip through the returned properties, adding to the response as we go
  775. //
  776. for (iv = 0; iv < cProps; iv++)
  777. {
  778. // If there is a value to the property, write the variant as
  779. // an XML element and add it to the response
  780. //
  781. if (rgv[iv].vt != VT_EMPTY)
  782. {
  783. if (!enPropOK.Pxn())
  784. {
  785. // Get the insert point for props
  786. //
  787. sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enPropOK);
  788. if (FAILED(sc))
  789. goto ret;
  790. }
  791. // Write the variant as an XML element
  792. //
  793. sc = ScEmitFromVariant (msr,
  794. enPropOK,
  795. rgwszPropNames[iv],
  796. rgv[iv]);
  797. if (FAILED (sc))
  798. goto ret;
  799. }
  800. else
  801. {
  802. UINT irp;
  803. // Check if it's a reserved property
  804. //
  805. if (FReservedProperty (rgwszPropNames[iv], RESERVED_GET, &irp) ||
  806. (irp == iana_rp_content_type))
  807. {
  808. if (!enPropOK.Pxn())
  809. {
  810. // Get the insert point for props
  811. //
  812. sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enPropOK);
  813. if (FAILED(sc))
  814. goto ret;
  815. }
  816. // If the property was reserved, then extract it from
  817. // the property class directly
  818. //
  819. sc = ScGetReservedProp (msr, enPropOK, irp);
  820. if (FAILED (sc))
  821. goto ret;
  822. continue;
  823. }
  824. // Now, if we got here, then for CFSProp, the property
  825. // must not have existed.
  826. //
  827. sc = csn.ScAddErrorStatus (HSC_NOT_FOUND, rgwszPropNames[iv]);
  828. if (FAILED(sc))
  829. goto ret;
  830. }
  831. }
  832. // Need to close the previous prop stat before more status node to be emitted
  833. //
  834. if (!csn.FEmpty())
  835. {
  836. // The order is important, inner node must be closed first
  837. //
  838. sc = enPropOK.ScDone();
  839. if (FAILED(sc))
  840. goto ret;
  841. sc = enPropStat.ScDone();
  842. if (FAILED(sc))
  843. goto ret;
  844. sc = csn.ScEmitErrorStatus (enItem);
  845. if (FAILED(sc))
  846. goto ret;
  847. }
  848. ret:
  849. return sc;
  850. }
  851. SCODE
  852. CFSProp::ScGetAllProps (CXMLEmitter& msr,
  853. CEmitterNode& enItem,
  854. BOOL fFindValues)
  855. {
  856. auto_com_ptr<IEnumSTATPROPBAG> penum;
  857. BOOL fContentType = FALSE;
  858. BOOL fHrefAdded = FALSE;
  859. SCODE sc = S_OK;
  860. UINT irp;
  861. CEmitterNode enPropStat;
  862. CEmitterNode enProp;
  863. // There really should only be one scenario where this could happen
  864. // -- and it is a cheap test, so it is worth doing. The case where
  865. // we might see an invalid pbag is when the document extisted, but
  866. // there was no existing property set to impose the pbag on. Other
  867. // than that, OLE is always giving us a property bag, regardless of
  868. // whether the target drive can support it.
  869. //
  870. if (!FInvalidPbag())
  871. {
  872. sc = m_pbag->Enum (NULL, 0, &penum);
  873. if (FAILED(sc))
  874. {
  875. // AddHref was delayed to be done after local namespace is loaded
  876. // but in this case, we know there'll be no local namespaces at all.
  877. // so add href now
  878. //
  879. (void) ScAddHref (enItem,
  880. m_pmu,
  881. PwszPath(),
  882. FCollection(),
  883. PcvrTranslation());
  884. if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
  885. {
  886. // This is the common path for when we are trying to access
  887. // something over an SMB, but the host cannot support the
  888. // request (it is not an NT5 NTFS machine). We want to treat
  889. // this as if the operation was against a FAT drive
  890. //
  891. sc = E_DAV_SMB_PROPERTY_ERROR;
  892. goto get_reserved;
  893. }
  894. goto ret;
  895. }
  896. // We must preload all the potential namespaces in the <response> node,
  897. // Note that the namespace for all reserved properties is "DAV:", which
  898. // has been added already in CFSFind::ScLoadNamespace()
  899. //
  900. do
  901. {
  902. safe_statpropbag ssp[PROP_CHUNK_SIZE];
  903. ULONG csp = 0;
  904. UINT isp;
  905. // Get next chunk of props
  906. //
  907. sc = penum->Next (PROP_CHUNK_SIZE, ssp[0].load(), &csp);
  908. if (FAILED(sc))
  909. goto ret;
  910. // At this point, we either want to call the underlying
  911. // property container to retrieve all the property data
  912. // or we just want to emit the names.
  913. //
  914. for (isp = 0; isp < csp; isp++)
  915. {
  916. Assert (ssp[isp].get().lpwstrName);
  917. sc = msr.ScPreloadLocalNamespace (enItem.Pxn(), ssp[isp].get().lpwstrName);
  918. if (FAILED(sc))
  919. goto ret;
  920. }
  921. } while (sc != S_FALSE);
  922. // Addhref must be done after all the local nmespaces has been emitted
  923. //
  924. sc = ScAddHref (enItem,
  925. m_pmu,
  926. PwszPath(),
  927. FCollection(),
  928. PcvrTranslation());
  929. if (FAILED (sc))
  930. goto ret;
  931. fHrefAdded = TRUE;
  932. // Reset the enumerator back to the beginning
  933. //
  934. sc = penum->Reset();
  935. if (FAILED(sc))
  936. goto ret;
  937. // Get the insert point for props
  938. //
  939. sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enProp);
  940. if (FAILED(sc))
  941. goto ret;
  942. // Enumerate the props and emit
  943. //
  944. do
  945. {
  946. safe_statpropbag ssp[PROP_CHUNK_SIZE];
  947. safe_propvariant propvar[PROP_CHUNK_SIZE];
  948. LPWSTR rglpwstr[PROP_CHUNK_SIZE] = {0};
  949. ULONG csp = 0;
  950. UINT isp;
  951. // Get next chunk of props
  952. //
  953. sc = penum->Next (PROP_CHUNK_SIZE, ssp[0].load(), &csp);
  954. if (FAILED(sc))
  955. goto ret;
  956. // At this point, we either want to call the underlying
  957. // property container to retrieve all the property data
  958. // or we just want to emit the names.
  959. //
  960. for (isp = 0; isp < csp; isp++)
  961. {
  962. Assert (ssp[isp].get().lpwstrName);
  963. // We need to track whether or not the getcontenttype
  964. // property was actually stored or not. If it wasn't,
  965. // then, we will want to default it at a later time.
  966. //
  967. if (!fContentType)
  968. {
  969. if (!wcscmp (ssp[isp].get().lpwstrName,
  970. gc_wszProp_iana_getcontenttype))
  971. {
  972. // Note that content-type is included
  973. //
  974. fContentType = TRUE;
  975. }
  976. }
  977. // If we are just asking for names, then add the
  978. // name to the list now...
  979. //
  980. if (!fFindValues)
  981. {
  982. CEmitterNode en;
  983. // Add the result to the response
  984. //
  985. sc = enProp.ScAddNode (ssp[isp].get().lpwstrName, en);
  986. if (FAILED (sc))
  987. goto ret;
  988. }
  989. else
  990. rglpwstr[isp] = ssp[isp].get().lpwstrName;
  991. }
  992. // If we are just asking about names, then we really
  993. // are done with this group of properties, otherwise
  994. // we need to generate the values and emit them.
  995. //
  996. if (!fFindValues)
  997. continue;
  998. // Read properties in chunk
  999. //
  1000. if (csp)
  1001. {
  1002. sc = m_pbag->ReadMultiple (csp,
  1003. rglpwstr,
  1004. propvar[0].addressof(),
  1005. NULL);
  1006. if (FAILED (sc))
  1007. goto ret;
  1008. }
  1009. // Emit properties
  1010. //
  1011. for (isp = 0; isp < csp; isp++)
  1012. {
  1013. // Contstruct the pel from the variant
  1014. //
  1015. sc = ScEmitFromVariant (msr,
  1016. enProp,
  1017. ssp[isp].get().lpwstrName,
  1018. const_cast<PROPVARIANT&>(propvar[isp].get()));
  1019. if (FAILED (sc))
  1020. goto ret;
  1021. }
  1022. } while (sc != S_FALSE);
  1023. }
  1024. get_reserved:
  1025. // Render all the reserved properties, this relies on the fact that
  1026. // the first non-GET reserved property is "DAV:getcontenttype".
  1027. //
  1028. Assert (iana_rp_content_type == sc_crp_get_reserved);
  1029. if (!fHrefAdded)
  1030. {
  1031. // Need to build the HREF node because it wasn't built above.
  1032. // This can happen when we don't have a pbag (like on FAT16).
  1033. //
  1034. sc = ScAddHref (enItem,
  1035. m_pmu,
  1036. PwszPath(),
  1037. FCollection(),
  1038. PcvrTranslation());
  1039. if (FAILED (sc))
  1040. goto ret;
  1041. }
  1042. if (!enProp.Pxn())
  1043. {
  1044. // Get the insert point for props
  1045. //
  1046. sc = ScGetPropNode (enItem, HSC_OK, enPropStat, enProp);
  1047. if (FAILED(sc))
  1048. goto ret;
  1049. }
  1050. for (irp = 0; irp <= sc_crp_get_reserved; irp++)
  1051. {
  1052. // If the content-type has already been processed, then
  1053. // don't do it here.
  1054. //
  1055. if ((irp == sc_crp_get_reserved) && fContentType)
  1056. break;
  1057. // Construct the pel from the reserved property
  1058. //
  1059. sc = ScGetReservedProp (msr, enProp, irp, fFindValues);
  1060. if (FAILED (sc))
  1061. goto ret;
  1062. }
  1063. // We are done with all the local namespaces
  1064. //
  1065. msr.DoneWithLocalNamespace();
  1066. ret:
  1067. return sc;
  1068. }
  1069. SCODE
  1070. CFSProp::ScSetProps (CStatusCache& csn,
  1071. ULONG cProps,
  1072. LPCWSTR* rgwszProps,
  1073. PROPVARIANT* rgvProps)
  1074. {
  1075. UINT ip;
  1076. SCODE sc = S_OK;
  1077. ULONG hsc;
  1078. // Zero props is a no-op
  1079. //
  1080. if (!cProps)
  1081. return S_OK;
  1082. Assert (!FInvalidPbag());
  1083. sc = m_pbag->WriteMultiple (cProps, rgwszProps, rgvProps);
  1084. if (FAILED(sc))
  1085. {
  1086. // This is the common path for when we are trying to access
  1087. // something over an SMB, but the host cannot support the
  1088. // request (it is not an NT5 NTFS machine).
  1089. //
  1090. if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
  1091. return E_DAV_SMB_PROPERTY_ERROR;
  1092. }
  1093. // we don't know exactly which prop failed,
  1094. // return same error for all props
  1095. //
  1096. hsc = HscFromHresult(sc);
  1097. for (ip = 0; ip < cProps; ip++)
  1098. {
  1099. sc = csn.ScAddErrorStatus (hsc, rgwszProps[ip]);
  1100. if (FAILED(sc))
  1101. goto ret;
  1102. }
  1103. ret:
  1104. return FAILED(sc) ? S_FALSE : S_OK;
  1105. }
  1106. SCODE
  1107. CFSProp::ScDeleteProps (CStatusCache& csn,
  1108. ULONG cProps,
  1109. LPCWSTR* rgwszProps)
  1110. {
  1111. UINT ip;
  1112. SCODE sc = S_OK;
  1113. ULONG hsc;
  1114. // Zero props is a no-op
  1115. //
  1116. if (!cProps)
  1117. return S_OK;
  1118. Assert (!FInvalidPbag());
  1119. sc = m_pbag->DeleteMultiple (cProps, rgwszProps, 0);
  1120. if (FAILED(sc))
  1121. {
  1122. // This is the common path for when we are trying to access
  1123. // something over an SMB, but the host cannot support the
  1124. // request (it is not an NT5 NTFS machine).
  1125. //
  1126. if ((sc == STG_E_INVALIDNAME) || !FIsVolumeNTFS())
  1127. return E_DAV_SMB_PROPERTY_ERROR;
  1128. }
  1129. // we don't know exactly which prop failed,
  1130. // return same error for all props
  1131. //
  1132. hsc = HscFromHresult(sc);
  1133. for (ip = 0; ip < cProps; ip++)
  1134. {
  1135. sc = csn.ScAddErrorStatus (hsc, rgwszProps[ip]);
  1136. if (FAILED(sc))
  1137. goto ret;
  1138. }
  1139. ret:
  1140. return FAILED(sc) ? S_FALSE : S_OK;
  1141. }
  1142. SCODE
  1143. CFSProp::ScPersist ()
  1144. {
  1145. // We are not transacted now, just
  1146. //
  1147. return S_OK;
  1148. }
  1149. // Content properties --------------------------------------------------------
  1150. //
  1151. SCODE
  1152. ScSetContentProperties (IMethUtil * pmu, LPCWSTR pwszPath, HANDLE hFile)
  1153. {
  1154. LPCWSTR pwszContentType;
  1155. LPCWSTR pwszContentLanguage;
  1156. LPCWSTR pwszContentEncoding;
  1157. SCODE sc = S_OK;
  1158. // Figure out which content properties we have
  1159. //
  1160. pwszContentType = pmu->LpwszGetRequestHeader (gc_szContent_Type, FALSE);
  1161. pwszContentLanguage = pmu->LpwszGetRequestHeader (gc_szContent_Language, FALSE);
  1162. pwszContentEncoding = pmu->LpwszGetRequestHeader (gc_szContent_Encoding, FALSE);
  1163. // Content-Type is special -- it is always set in the metabase.
  1164. // It should be set *before* setting any properties in the property bag
  1165. // since it's OK for the property bag stuff to fail.
  1166. //
  1167. if (NULL != pwszContentType)
  1168. {
  1169. // Setting the content-type will not work if the metabase is read only,
  1170. // which is exactly what is happening in .NET server. So we need to
  1171. // ignore the error -- if any.
  1172. //
  1173. (void) pmu->ScSetContentType (pmu->LpwszRequestUrl(), pwszContentType);
  1174. }
  1175. // Set any content properties we have in the property bag
  1176. //
  1177. if (pwszContentLanguage || pwszContentEncoding)
  1178. {
  1179. auto_com_ptr<IPropertyBagEx> pbe;
  1180. CStackBuffer<WCHAR> pwsz;
  1181. // Try to open the property bag. If this fails because we're not
  1182. // on an NTFS filesystem, that's OK. We just won't set the properties
  1183. // there.
  1184. //
  1185. // We need to open propertybag by handle as it the main stream might
  1186. // be locked.
  1187. //
  1188. sc = ScGetPropertyBag (pwszPath,
  1189. STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
  1190. &pbe,
  1191. FALSE, // not a collection
  1192. hFile);
  1193. if (FAILED(sc))
  1194. {
  1195. //$ REVIEW:
  1196. //
  1197. // We did our best here. For DocFiles this will fail because
  1198. // of how we have to open the files. What this means is that we
  1199. // could potentially lose the content-encoding and content-language
  1200. // which would put us on par with IIS (they don't store these either).
  1201. //
  1202. sc = S_OK;
  1203. goto ret;
  1204. }
  1205. CResourceInfo cri;
  1206. CFSProp xpt(pmu,
  1207. pbe,
  1208. pmu->LpwszRequestUrl(),
  1209. pwszPath,
  1210. NULL,
  1211. cri);
  1212. // Content-Type
  1213. //
  1214. if (NULL != pwszContentType)
  1215. {
  1216. sc = xpt.ScSetStringProp (sc_rp[iana_rp_content_type].pwsz, pwszContentType);
  1217. if (FAILED (sc))
  1218. goto ret;
  1219. }
  1220. // Content-Language
  1221. //
  1222. if (NULL != pwszContentLanguage)
  1223. {
  1224. sc = xpt.ScSetStringProp (sc_rp[iana_rp_content_language].pwsz, pwszContentLanguage);
  1225. if (FAILED (sc))
  1226. goto ret;
  1227. }
  1228. // Content-Encoding
  1229. //
  1230. if (NULL != pwszContentEncoding)
  1231. {
  1232. sc = xpt.ScSetStringProp (sc_rp[iana_rp_content_encoding].pwsz, pwszContentEncoding);
  1233. if (FAILED (sc))
  1234. goto ret;
  1235. }
  1236. // Persist the changes
  1237. //
  1238. sc = xpt.ScPersist();
  1239. if (FAILED(sc))
  1240. goto ret;
  1241. }
  1242. ret:
  1243. // It is perfectly OK to ignore SMB errors when setting content properties.
  1244. //
  1245. if (sc == E_DAV_SMB_PROPERTY_ERROR)
  1246. sc = S_OK;
  1247. return sc;
  1248. }
  1249. // ScFindFileProps -----------------------------------------------------------
  1250. //
  1251. SCODE
  1252. ScFindFileProps (IMethUtil* pmu,
  1253. CFSFind& cfc,
  1254. CXMLEmitter& msr,
  1255. LPCWSTR pwszUri,
  1256. LPCWSTR pwszPath,
  1257. CVRoot* pcvrTranslation,
  1258. CResourceInfo& cri,
  1259. BOOL fEmbedErrorsInResponse)
  1260. {
  1261. auto_com_ptr<IPropertyBagEx> pbag;
  1262. CFSProp fsp(pmu, pbag, pwszUri, pwszPath, pcvrTranslation, cri);
  1263. SCODE sc = S_OK;
  1264. // Check access permission
  1265. //
  1266. sc = pmu->ScCheckMoveCopyDeleteAccess (pwszUri,
  1267. pcvrTranslation,
  1268. cri.FCollection(),
  1269. FALSE, // do not check against scriptmaps
  1270. MD_ACCESS_READ);
  1271. if (FAILED (sc))
  1272. {
  1273. // No permission to read, we certainly do not want
  1274. // to try and traverse down into the directory (if
  1275. // it was one), we do this by returning S_FALSE.
  1276. //
  1277. if (fEmbedErrorsInResponse)
  1278. {
  1279. sc = cfc.ScErrorAllProps (msr,
  1280. pmu,
  1281. pwszPath,
  1282. cri.FCollection(),
  1283. pcvrTranslation,
  1284. sc);
  1285. if (FAILED (sc))
  1286. goto ret;
  1287. // Pass back S_FALSE so that no further traversal of
  1288. // this resource is performed
  1289. //
  1290. sc = S_FALSE;
  1291. }
  1292. if (S_OK != sc)
  1293. goto ret;
  1294. }
  1295. // Don't get pbag for remote files. This would cause the files to
  1296. // be recalled, etc.
  1297. //
  1298. if (!cri.FRemote())
  1299. {
  1300. // Get the IPropertyBagEx interface
  1301. //
  1302. // Before call into this function, we've checked we have read access to
  1303. // this file. so we should always be able to read the proerties however,
  1304. // if the file is write locked, there may be some problems from the OLE
  1305. // properties code.
  1306. //
  1307. sc = ScGetPropertyBag (pwszPath,
  1308. STGM_READ | STGM_SHARE_DENY_WRITE,
  1309. &pbag,
  1310. cri.FCollection());
  1311. if (FAILED (sc))
  1312. {
  1313. // We need to check the volume of the file we are trying
  1314. // to read.
  1315. //
  1316. if (VOLTYPE_NTFS == VolumeType (pwszPath, pmu->HitUser()))
  1317. {
  1318. // Report the errors for this file and come on back...
  1319. //
  1320. if (fEmbedErrorsInResponse)
  1321. {
  1322. sc = cfc.ScErrorAllProps (msr,
  1323. pmu,
  1324. pwszPath,
  1325. cri.FCollection(),
  1326. pcvrTranslation,
  1327. sc);
  1328. }
  1329. goto ret;
  1330. }
  1331. }
  1332. }
  1333. // Find the properties
  1334. //
  1335. sc = cfc.ScFind (msr, pmu, fsp);
  1336. if (FAILED (sc))
  1337. goto ret;
  1338. ret:
  1339. return sc;
  1340. }
  1341. SCODE
  1342. ScFindFilePropsDeep (IMethUtil* pmu,
  1343. CFSFind& cfc,
  1344. CXMLEmitter& msr,
  1345. LPCWSTR pwszUri,
  1346. LPCWSTR pwszPath,
  1347. CVRoot* pcvrTranslation,
  1348. LONG lDepth)
  1349. {
  1350. BOOL fSubDirectoryAccess = TRUE;
  1351. SCODE sc = S_OK;
  1352. // Query subdirs when do deep query
  1353. //
  1354. Assert ((lDepth == DEPTH_ONE) ||
  1355. (lDepth == DEPTH_ONE_NOROOT) ||
  1356. (lDepth == DEPTH_INFINITY));
  1357. CDirIter di(pwszUri,
  1358. pwszPath,
  1359. NULL, // no destination url
  1360. NULL, // no destination path
  1361. NULL, // no destination translation
  1362. lDepth == DEPTH_INFINITY);
  1363. while (S_OK == (sc = di.ScGetNext (fSubDirectoryAccess)))
  1364. {
  1365. CResourceInfo cri;
  1366. // If we found another directory, then iterate on it
  1367. //
  1368. fSubDirectoryAccess = FALSE;
  1369. if (di.FDirectory())
  1370. {
  1371. auto_ref_ptr<CVRoot> arp;
  1372. // Skip the special and/or hidden directories
  1373. //
  1374. if (di.FSpecial())
  1375. continue;
  1376. // If we happen to traverse into a directory
  1377. // that happens to be a vroot (as identified
  1378. // by url), then there is another entry in
  1379. // the list of child vroots that will refer
  1380. // to this directory. Let that processing
  1381. // handle this directory instead of the
  1382. // doing it here.
  1383. //
  1384. // This means that the file hierarchy is not
  1385. // strictly preserved, but I think that this
  1386. // is OK.
  1387. //
  1388. if (pmu->FFindVRootFromUrl (di.PwszUri(), arp))
  1389. continue;
  1390. // Check the directory browsing bit and see
  1391. // if it is enabled. And only progess down
  1392. // if it is set.
  1393. //
  1394. {
  1395. auto_ref_ptr<IMDData> pMDData;
  1396. if (SUCCEEDED(pmu->HrMDGetData (di.PwszUri(), pMDData.load())) &&
  1397. (pMDData->DwDirBrowsing() & MD_DIRBROW_ENABLED))
  1398. {
  1399. // Prepare to go into the subdir
  1400. //
  1401. fSubDirectoryAccess = TRUE;
  1402. }
  1403. }
  1404. }
  1405. // Find the properties for the resource
  1406. //
  1407. *cri.PfdLoad() = di.FindData();
  1408. sc = ScFindFileProps (pmu,
  1409. cfc,
  1410. msr,
  1411. di.PwszUri(),
  1412. di.PwszSource(),
  1413. pcvrTranslation,
  1414. cri,
  1415. TRUE /*fEmbedErrorsInResponse*/);
  1416. if (FAILED (sc))
  1417. goto ret;
  1418. // S_FALSE is a special return code that
  1419. // means we did not have access to read the
  1420. // resource...
  1421. //
  1422. if (sc == S_FALSE)
  1423. {
  1424. // ... and since we really didn't have access,
  1425. // we don't want to delve into the children of
  1426. // the resource.
  1427. //
  1428. fSubDirectoryAccess = FALSE;
  1429. }
  1430. }
  1431. ret:
  1432. return sc;
  1433. }
  1434. // ScCopyProps ---------------------------------------------------------------
  1435. //
  1436. /*
  1437. * ScCopyProps()
  1438. *
  1439. * Purpose:
  1440. *
  1441. * Copies the properties from one resource to another. This is
  1442. * really only useful for copying full directories. Standard file
  1443. * copies do the dirty work for us, but for directories, we need to
  1444. * do it ourselves.
  1445. * If we don't find any propstream on the source, we DELETE
  1446. * any propstream on the destination.
  1447. */
  1448. SCODE
  1449. ScCopyProps (IMethUtil* pmu, LPCWSTR pwszSrc, LPCWSTR pwszDst,
  1450. BOOL fCollection, HANDLE hSource, HANDLE hDest)
  1451. {
  1452. enum { CHUNK_SIZE = 16 };
  1453. auto_com_ptr<IPropertyBagEx> pbeSrc;
  1454. auto_com_ptr<IPropertyBagEx> pbeDst;
  1455. auto_com_ptr<IEnumSTATPROPBAG> penumSrc;
  1456. auto_com_ptr<IEnumSTATPROPBAG> penumDst;
  1457. SCODE sc;
  1458. SCODE scEnum;
  1459. ULONG cProp;
  1460. MCDTrace ("Dav: MCD: copying props manually: %ws -> %ws\n", pwszSrc, pwszDst);
  1461. // Get the IPropertyBagEx on the source
  1462. //
  1463. sc = ScGetPropertyBag (pwszSrc,
  1464. STGM_READ | STGM_SHARE_DENY_WRITE,
  1465. &pbeSrc,
  1466. fCollection,
  1467. hSource);
  1468. if (sc != S_OK)
  1469. goto ret;
  1470. MCDTrace ("Dav: MCD: opened source property bag: %ws\n", pwszSrc);
  1471. // Get the IPropertyBagEx on the destination
  1472. //
  1473. sc = ScGetPropertyBag (pwszDst,
  1474. STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
  1475. &pbeDst,
  1476. fCollection,
  1477. hDest);
  1478. if (FAILED(sc))
  1479. goto ret;
  1480. MCDTrace ("Dav: MCD: opened destination property bag: %ws\n", pwszDst);
  1481. // Get the IEnumSTATPROPBAG interface on source
  1482. //
  1483. sc = pbeSrc->Enum (NULL, 0, &penumSrc);
  1484. if (FAILED(sc))
  1485. goto ret;
  1486. // Get the IEnumSTATPROPBAG interface on destination
  1487. //
  1488. sc = pbeDst->Enum (NULL, 0, &penumDst);
  1489. if (FAILED(sc))
  1490. goto ret;
  1491. // Delete all props from destination if there's any
  1492. //$ COME BACK
  1493. //$ Instead of delete props one by one, we can just delete the
  1494. // prop stream.
  1495. //
  1496. for (;;)
  1497. {
  1498. safe_statpropbag ssp[CHUNK_SIZE];
  1499. safe_propvariant propvar[CHUNK_SIZE];
  1500. ULONG csp = 0;
  1501. // Get next chunk of props
  1502. //
  1503. Assert (sizeof(safe_statpropbag) == sizeof(STATPROPBAG));
  1504. scEnum = penumDst->Next(CHUNK_SIZE,
  1505. reinterpret_cast<STATPROPBAG *>(&ssp[0]),
  1506. &csp);
  1507. if (FAILED(scEnum))
  1508. {
  1509. sc = scEnum;
  1510. goto ret;
  1511. }
  1512. MCDTrace ("Dav: MCD: copying %ld props\n", csp);
  1513. // Delete one by one
  1514. //
  1515. for (cProp = 0; cProp < csp; cProp++)
  1516. {
  1517. Assert (ssp[cProp].get().lpwstrName);
  1518. // Write to the destination
  1519. //
  1520. LPCWSTR pwsz = ssp[cProp].get().lpwstrName;
  1521. sc = pbeDst->DeleteMultiple (1, &pwsz, 0);
  1522. if (FAILED(sc))
  1523. goto ret;
  1524. }
  1525. if (scEnum == S_FALSE)
  1526. break;
  1527. }
  1528. // Enumerate the props and emit
  1529. //
  1530. for (;;)
  1531. {
  1532. safe_statpropbag ssp[CHUNK_SIZE];
  1533. safe_propvariant propvar[CHUNK_SIZE];
  1534. LPWSTR rglpwstr[CHUNK_SIZE] = {0};
  1535. ULONG csp = 0;
  1536. // Get next chunk of props
  1537. //
  1538. Assert (sizeof(safe_statpropbag) == sizeof(STATPROPBAG));
  1539. scEnum = penumSrc->Next (CHUNK_SIZE,
  1540. reinterpret_cast<STATPROPBAG *>(&ssp[0]),
  1541. &csp);
  1542. if (FAILED(scEnum))
  1543. {
  1544. sc = scEnum;
  1545. goto ret;
  1546. }
  1547. // Prepare to call read multiple props
  1548. //
  1549. for (cProp=0; cProp<csp; cProp++)
  1550. {
  1551. Assert (ssp[cProp].get().lpwstrName);
  1552. rglpwstr[cProp] = ssp[cProp].get().lpwstrName;
  1553. }
  1554. if (csp)
  1555. {
  1556. // Read properties in chunk from source
  1557. //
  1558. sc = pbeSrc->ReadMultiple (csp, rglpwstr, &propvar[0], NULL);
  1559. if (FAILED(sc))
  1560. goto ret;
  1561. // Write to the destination
  1562. //
  1563. sc = pbeDst->WriteMultiple (csp, rglpwstr, propvar[0].addressof());
  1564. if (FAILED(sc))
  1565. goto ret;
  1566. }
  1567. if (scEnum == S_FALSE)
  1568. break;
  1569. }
  1570. ret:
  1571. // Copying properties is a harmless failure that
  1572. // we should feel free to ignore if we are not on
  1573. // an NFTS volume
  1574. //
  1575. if (FAILED(sc))
  1576. {
  1577. if ((sc == STG_E_INVALIDNAME) ||
  1578. VOLTYPE_NTFS != VolumeType (pwszSrc, pmu->HitUser()) ||
  1579. VOLTYPE_NTFS != VolumeType (pwszDst, pmu->HitUser()))
  1580. {
  1581. // This is the common path for when we are trying to access
  1582. // something over an SMB, but the host cannot support the
  1583. // request (it is not an NT5 NTFS machine).
  1584. //
  1585. sc = S_OK;
  1586. }
  1587. }
  1588. return sc;
  1589. }
  1590. // OLE 32 IPropertyBagEx Access ----------------------------------------------
  1591. //
  1592. // StgOpenStorageOnHandle() and StgCreateStorageOnHandle() are implemented
  1593. // in OLE32.DLL but not exported. We must load the library and get the proc
  1594. // instances ourselves. We wrap the calls to these functions with this small
  1595. // wrapper such that we can catch when the API changes.
  1596. //
  1597. STDAPI
  1598. StgOpenStorageOnHandle (
  1599. IN HANDLE hStream,
  1600. IN DWORD grfMode,
  1601. IN void *reserved1,
  1602. IN void *reserved2,
  1603. IN REFIID riid,
  1604. OUT void **ppObjectOpen )
  1605. {
  1606. Assert (g_pfnStgOpenStorageOnHandle);
  1607. // Yes, we've asserted.
  1608. // However, if it does happen, we don't want to fail and we can
  1609. // just treat this like we are on a FAT. (i.e. no property support)
  1610. //
  1611. if (!g_pfnStgOpenStorageOnHandle)
  1612. return E_DAV_SMB_PROPERTY_ERROR;
  1613. return (*g_pfnStgOpenStorageOnHandle) (hStream,
  1614. grfMode,
  1615. reserved1,
  1616. reserved2,
  1617. riid,
  1618. ppObjectOpen);
  1619. }
  1620. // ScGetPropertyBag() --------------------------------------------------------
  1621. //
  1622. // Helper function used to get IPropertyBagEx interface. The important
  1623. // thing to know about this function is that there are three interesting
  1624. // return values:
  1625. //
  1626. // S_OK means everything was OK, and there should be a
  1627. // propertybag associated with the file in the out param.
  1628. //
  1629. // S_FALSE means that the file did not exist. There will
  1630. // not be an associated property bag in that scenario.
  1631. //
  1632. // FAILED(sc) means that there was a failure of some sort,
  1633. // not all of which are fatal. In many cases, we will simply
  1634. // treat the file as if it was hosted on a FAT file system.
  1635. //
  1636. SCODE
  1637. ScGetPropertyBag (LPCWSTR pwszPath,
  1638. DWORD dwAccessDesired,
  1639. IPropertyBagEx** ppbe,
  1640. BOOL fCollection,
  1641. HANDLE hLockFile)
  1642. {
  1643. SCODE sc = S_OK;
  1644. auto_handle<HANDLE> hAlt;
  1645. // READ!!
  1646. //
  1647. // The storage of property bag is different between docfile and flat file,
  1648. // In a flat file, the property bag is stored in an alternative file stream,
  1649. // (currently, ":Docf_\005Bagaaqy23kudbhchAaq5u2chNd"), in a docfile, the
  1650. // property bag is stored as a substream under the root storage.
  1651. //
  1652. // We should not be concerned with where the pbag is stored. The API with
  1653. // which we implement our IPropertyBagEx access is designed to have the
  1654. // behavior of...
  1655. //
  1656. // We pass in a handle to the file that we want to get a property bag
  1657. // on. If the file is a docfile, then OLE32 will dup the file handle
  1658. // and impose a IPropertyBagEx on the appropriate substorage. If the
  1659. // file is a flat file -- directories included -- then OLE32 opens up
  1660. // a handle on the alternate file stream relative to the handle given
  1661. // in the call.
  1662. //
  1663. // These are the only two combinations allowed, and we rely on this in the
  1664. // following flag checks.
  1665. //
  1666. Assert ((dwAccessDesired == (STGM_READWRITE | STGM_SHARE_EXCLUSIVE)) ||
  1667. (dwAccessDesired == (STGM_READ | STGM_SHARE_DENY_WRITE)));
  1668. if (hLockFile == INVALID_HANDLE_VALUE)
  1669. {
  1670. ULONG dwShare = 0;
  1671. ULONG dwAccess;
  1672. ULONG dwOpen;
  1673. ULONG dwFile;
  1674. //$ REVIEW: Directories are special critters and we need to
  1675. // open the directory with special access as not to conflict
  1676. // with IIS and/or ASP and their directory change notification
  1677. // stuff
  1678. //
  1679. if (fCollection)
  1680. {
  1681. dwAccess = 1; // FILE_LIST_DIRECTORY
  1682. dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
  1683. dwOpen = OPEN_EXISTING;
  1684. // The FILE_FLAG_BACKUP_SEMANTICS is used to open a directory handle
  1685. //
  1686. dwFile = FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED;
  1687. }
  1688. else
  1689. {
  1690. // Adjust access/open mode based on desired operation
  1691. //
  1692. dwAccess = GENERIC_READ;
  1693. dwFile = FILE_ATTRIBUTE_NORMAL;
  1694. if (dwAccessDesired & STGM_READWRITE)
  1695. {
  1696. dwAccess |= GENERIC_WRITE;
  1697. dwOpen = OPEN_ALWAYS;
  1698. }
  1699. else
  1700. dwOpen = OPEN_EXISTING;
  1701. // Adjust the sharing modes as well
  1702. //
  1703. if (dwAccessDesired & STGM_SHARE_DENY_WRITE)
  1704. dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
  1705. }
  1706. hAlt = DavCreateFile (pwszPath,
  1707. dwAccess,
  1708. dwShare,
  1709. NULL,
  1710. dwOpen,
  1711. dwFile,
  1712. NULL);
  1713. if (INVALID_HANDLE_VALUE == hAlt.get())
  1714. {
  1715. DWORD dwErr = GetLastError();
  1716. // When open the property bag for read (PROPFIND), ERROR_FILE/PATH_NOT_FOUND
  1717. // could be returned if the file does not exists.
  1718. //
  1719. // When open the property bag for write (PROPPATCH), ERROR_FILE/PATH_NOT_FOUND
  1720. // Could still be returned if the parent of the path does not exists
  1721. // (Such as c:\x\y\z and \x\y does not exist).
  1722. //
  1723. // We need to differenciate the above two cases, when reading a file,
  1724. // it's not a fatal error as you could still try to read reserved
  1725. // properties, when write, we should treat this as a fatal error.
  1726. //
  1727. if ((dwErr == ERROR_FILE_NOT_FOUND) || (dwErr == ERROR_PATH_NOT_FOUND))
  1728. {
  1729. // It's not a fatal error when read
  1730. //
  1731. if (dwAccessDesired == (STGM_READ|STGM_SHARE_DENY_WRITE))
  1732. sc = S_FALSE;
  1733. else
  1734. {
  1735. // This is consistent with Mkcol, it will be mapped to 409
  1736. //
  1737. Assert (dwAccessDesired == (STGM_READWRITE|STGM_SHARE_EXCLUSIVE));
  1738. sc = E_DAV_NONEXISTING_PARENT;
  1739. }
  1740. }
  1741. else
  1742. sc = HRESULT_FROM_WIN32 (dwErr);
  1743. goto ret;
  1744. }
  1745. // Setup the handle to use
  1746. //
  1747. hLockFile = hAlt.get();
  1748. }
  1749. // Try to open the propertybag.
  1750. //
  1751. Assert (hLockFile != 0);
  1752. Assert (hLockFile != INVALID_HANDLE_VALUE);
  1753. sc = StgOpenStorageOnHandle (hLockFile,
  1754. dwAccessDesired,
  1755. NULL,
  1756. NULL,
  1757. IID_IPropertyBagEx,
  1758. reinterpret_cast<LPVOID *>(ppbe));
  1759. if (FAILED (sc))
  1760. {
  1761. goto ret;
  1762. }
  1763. //$ WARNING
  1764. //
  1765. // Argh! The current implementation of OLE32 returns a non-failure
  1766. // with a NULL property bag!
  1767. //
  1768. if (*ppbe == NULL)
  1769. {
  1770. DebugTrace ("WARNING! OLE32 returned success w/NULL object!\n");
  1771. sc = E_DAV_SMB_PROPERTY_ERROR;
  1772. }
  1773. ret:
  1774. return sc;
  1775. }
  1776. // DAV-Properties Implementation ---------------------------------------------------
  1777. //
  1778. // CPropFindRequest ----------------------------------------------------------------
  1779. //
  1780. class CPropFindRequest :
  1781. public CMTRefCounted,
  1782. private IAsyncIStreamObserver
  1783. {
  1784. //
  1785. // Reference to the CMethUtil
  1786. //
  1787. auto_ref_ptr<CMethUtil> m_pmu;
  1788. //
  1789. // Translated URI path
  1790. //
  1791. LPCWSTR m_pwszPath;
  1792. // Resource info
  1793. //
  1794. CResourceInfo m_cri;
  1795. // Depth
  1796. //
  1797. LONG m_lDepth;
  1798. // Contexts
  1799. //
  1800. CFSFind m_cfc;
  1801. auto_ref_ptr<CNFFind> m_pcpf;
  1802. // Request body as an IStream. This stream is async -- it can
  1803. // return E_PENDING from Read() calls.
  1804. //
  1805. auto_ref_ptr<IStream> m_pstmRequest;
  1806. // The XML parser used to parse the request body using
  1807. // the node factory above.
  1808. //
  1809. auto_ref_ptr<IXMLParser> m_pxprs;
  1810. // IAsyncIStreamObserver
  1811. //
  1812. VOID AsyncIOComplete();
  1813. // State functions
  1814. //
  1815. VOID ParseBody();
  1816. VOID DoFind();
  1817. VOID SendResponse( SCODE sc );
  1818. // NOT IMPLEMENTED
  1819. //
  1820. CPropFindRequest (const CPropFindRequest&);
  1821. CPropFindRequest& operator= (const CPropFindRequest&);
  1822. public:
  1823. // CREATORS
  1824. //
  1825. CPropFindRequest(LPMETHUTIL pmu) :
  1826. m_pmu(pmu),
  1827. m_pwszPath(m_pmu->LpwszPathTranslated()),
  1828. m_lDepth(DEPTH_INFINITY)
  1829. {
  1830. }
  1831. // MANIPULATORS
  1832. //
  1833. VOID Execute();
  1834. };
  1835. VOID
  1836. CPropFindRequest::Execute()
  1837. {
  1838. auto_ref_handle hf;
  1839. LPCWSTR pwsz;
  1840. SCODE sc = S_OK;
  1841. //
  1842. // First off, tell the pmu that we want to defer the response.
  1843. // Even if we send it synchronously (i.e. due to an error in
  1844. // this function), we still want to use the same mechanism that
  1845. // we would use for async.
  1846. //
  1847. m_pmu->DeferResponse();
  1848. // Do ISAPI application and IIS access bits checking
  1849. //
  1850. sc = m_pmu->ScIISCheck (m_pmu->LpwszRequestUrl(), MD_ACCESS_READ);
  1851. if (FAILED(sc))
  1852. {
  1853. // Either the request has been forwarded, or some bad error occurred.
  1854. // In either case, quit here and map the error!
  1855. //
  1856. SendResponse(sc);
  1857. return;
  1858. }
  1859. // For PropFind, content-length is required
  1860. //
  1861. //
  1862. if (NULL == m_pmu->LpwszGetRequestHeader (gc_szContent_Length, FALSE))
  1863. {
  1864. pwsz = m_pmu->LpwszGetRequestHeader (gc_szTransfer_Encoding, FALSE);
  1865. if (!pwsz || _wcsicmp (pwsz, gc_wszChunked))
  1866. {
  1867. DavTrace ("Dav: PUT: missing content-length in request\n");
  1868. SendResponse(E_DAV_MISSING_LENGTH);
  1869. return;
  1870. }
  1871. }
  1872. // Ensure the resource exists
  1873. //
  1874. sc = m_cri.ScGetResourceInfo (m_pwszPath);
  1875. if (FAILED (sc))
  1876. {
  1877. SendResponse(sc);
  1878. return;
  1879. }
  1880. // Depth header only applies on directories
  1881. //
  1882. if (m_cri.FCollection())
  1883. {
  1884. // Check the depth header only on collections
  1885. //
  1886. if (!FGetDepth (m_pmu.get(), &m_lDepth))
  1887. {
  1888. // If Depth header is wrong, fail the operation
  1889. //
  1890. SendResponse(E_INVALIDARG);
  1891. return;
  1892. }
  1893. }
  1894. // This method is gated by If-xxx headers
  1895. //
  1896. sc = ScCheckIfHeaders (m_pmu.get(), m_cri.PftLastModified(), FALSE);
  1897. if (FAILED (sc))
  1898. {
  1899. SendResponse(sc);
  1900. return;
  1901. }
  1902. // Ensure the URI and resource match
  1903. //
  1904. (void) ScCheckForLocationCorrectness (m_pmu.get(), m_cri, NO_REDIRECT);
  1905. // Check state headers here.
  1906. //
  1907. // For PROPFIND, when we check the state headers,
  1908. // we want to treat the request as if it were a
  1909. // GET-type request.
  1910. //
  1911. sc = HrCheckStateHeaders (m_pmu.get(), m_pwszPath, TRUE);
  1912. if (FAILED (sc))
  1913. {
  1914. DebugTrace ("DavFS: If-State checking failed.\n");
  1915. SendResponse(sc);
  1916. return;
  1917. }
  1918. // Handle locktokens and check for locks on this resource.
  1919. // Our locks don't lock the "secondary file stream" where we keep
  1920. // the properties, so we have to check manually before we do anything else.
  1921. //$REVIEW: Joels, will this change when we switch to NT5 properties?
  1922. //$REVIEW: If so, we need to change this code!
  1923. //
  1924. // If we have a locktoken, try to get the lock handle from the cache.
  1925. // If this fails, fall through and do the normal processing.
  1926. //
  1927. pwsz = m_pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  1928. if (!pwsz || !FGetLockHandle (m_pmu.get(), m_pwszPath, GENERIC_READ, pwsz, &hf))
  1929. {
  1930. // Manually check for locks on this resource.
  1931. // (see if someone ELSE has it locked...)
  1932. // If a read lock exists, tell the caller that it's locked.
  1933. //
  1934. if (FLockViolation (m_pmu.get(), ERROR_SHARING_VIOLATION, m_pwszPath, GENERIC_READ))
  1935. {
  1936. SendResponse(E_DAV_LOCKED);
  1937. return;
  1938. }
  1939. }
  1940. // If there was no request body, we want to get all props
  1941. //
  1942. if (!m_pmu->FExistsRequestBody())
  1943. {
  1944. sc = m_cfc.ScGetAllProps (m_pwszPath);
  1945. if (FAILED (sc))
  1946. {
  1947. SendResponse(sc);
  1948. return;
  1949. }
  1950. DoFind();
  1951. return;
  1952. }
  1953. else
  1954. {
  1955. // If there's a body, there must be a content-type header
  1956. // and the value must be text/xml
  1957. //
  1958. sc = ScIsContentTypeXML (m_pmu.get());
  1959. if (FAILED(sc))
  1960. {
  1961. DebugTrace ("Dav: PROPFIND specific fails without specifying a text/xml contenttype\n");
  1962. SendResponse(sc);
  1963. return;
  1964. }
  1965. }
  1966. // Instantiate the XML parser
  1967. //
  1968. m_pcpf.take_ownership(new CNFFind(m_cfc));
  1969. m_pstmRequest.take_ownership(m_pmu->GetRequestBodyIStream(*this));
  1970. sc = ScNewXMLParser( m_pcpf.get(),
  1971. m_pstmRequest.get(),
  1972. m_pxprs.load() );
  1973. if (FAILED(sc))
  1974. {
  1975. DebugTrace( "CPropFindRequest::Execute() - ScNewXMLParser() failed (0x%08lX)\n", sc );
  1976. SendResponse(sc);
  1977. return;
  1978. }
  1979. // Parse the body
  1980. //
  1981. ParseBody();
  1982. }
  1983. VOID
  1984. CPropFindRequest::ParseBody()
  1985. {
  1986. SCODE sc;
  1987. Assert( m_pxprs.get() );
  1988. Assert( m_pcpf.get() );
  1989. Assert( m_pstmRequest.get() );
  1990. // Parse XML from the request body stream.
  1991. //
  1992. // Add a ref for the following async operation.
  1993. // Use auto_ref_ptr rather than AddRef() for exception safety.
  1994. //
  1995. auto_ref_ptr<CPropFindRequest> pRef(this);
  1996. sc = ScParseXML (m_pxprs.get(), m_pcpf.get());
  1997. if ( SUCCEEDED(sc) )
  1998. {
  1999. Assert( S_OK == sc || S_FALSE == sc );
  2000. DoFind();
  2001. }
  2002. else if ( E_PENDING == sc )
  2003. {
  2004. //
  2005. // The operation is pending -- AsyncIOComplete() will take ownership
  2006. // ownership of the reference when it is called.
  2007. //
  2008. pRef.relinquish();
  2009. }
  2010. else
  2011. {
  2012. DebugTrace( "CPropFindRequest::ParseBody() - ScParseXML() failed (0x%08lX)\n", sc );
  2013. SendResponse(sc);
  2014. }
  2015. }
  2016. VOID
  2017. CPropFindRequest::AsyncIOComplete()
  2018. {
  2019. // Take ownership of the reference added for the async operation.
  2020. //
  2021. auto_ref_ptr<CPropFindRequest> pRef;
  2022. pRef.take_ownership(this);
  2023. ParseBody();
  2024. }
  2025. VOID
  2026. CPropFindRequest::DoFind()
  2027. {
  2028. LPCWSTR pwszUrl = m_pmu->LpwszRequestUrl();
  2029. SCODE sc;
  2030. // At this point, make sure that they support text/xml
  2031. //
  2032. sc = ScIsAcceptable (m_pmu.get(), gc_wszText_XML);
  2033. if (FAILED (sc))
  2034. {
  2035. SendResponse(sc);
  2036. return;
  2037. }
  2038. // All header must be emitted before chunked XML emitting starts
  2039. //
  2040. m_pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
  2041. // Set the response code and go
  2042. //
  2043. m_pmu->SetResponseCode( HscFromHresult(W_DAV_PARTIAL_SUCCESS),
  2044. NULL,
  2045. 0,
  2046. CSEFromHresult(W_DAV_PARTIAL_SUCCESS) );
  2047. // Find the properties...
  2048. //
  2049. auto_ref_ptr<CXMLEmitter> pmsr;
  2050. auto_ref_ptr<CXMLBody> pxb;
  2051. pxb.take_ownership (new CXMLBody(m_pmu.get()));
  2052. pmsr.take_ownership (new CXMLEmitter(pxb.get(), &m_cfc));
  2053. if (DEPTH_ONE_NOROOT != m_lDepth)
  2054. {
  2055. // Get properties for root if it is not a noroot case
  2056. // Depth infinity,noroot is a bad request, that is why
  2057. // check above is valid.
  2058. //
  2059. sc = ScFindFileProps (m_pmu.get(),
  2060. m_cfc,
  2061. *pmsr,
  2062. pwszUrl,
  2063. m_pwszPath,
  2064. NULL,
  2065. m_cri,
  2066. FALSE /*fEmbeddErrorsInResponse*/);
  2067. if (FAILED (sc))
  2068. {
  2069. SendResponse(sc);
  2070. return;
  2071. }
  2072. }
  2073. // ScFindFilePropsDeep initializes the emitter root only
  2074. // when it sees there's an entry to emit. so we crash
  2075. // in the noroot empty response case, when we try to emit
  2076. // the response, as we have no entry to emit and the
  2077. // root is still NULL.
  2078. // so we here manually initialize the root,
  2079. //
  2080. sc = pmsr->ScSetRoot (gc_wszMultiResponse);
  2081. if (FAILED (sc))
  2082. {
  2083. SendResponse(sc);
  2084. return;
  2085. }
  2086. // And then, if apropriate, go deep...
  2087. //
  2088. if (m_cri.FCollection() &&
  2089. (m_lDepth != DEPTH_ZERO) &&
  2090. (m_pmu->MetaData().DwDirBrowsing() & MD_DIRBROW_ENABLED))
  2091. {
  2092. ChainedStringBuffer<WCHAR> sb;
  2093. CVRList vrl;
  2094. // Apply the property request across all the physical children
  2095. //
  2096. sc = ScFindFilePropsDeep (m_pmu.get(),
  2097. m_cfc,
  2098. *pmsr,
  2099. pwszUrl,
  2100. m_pwszPath,
  2101. NULL,
  2102. m_lDepth);
  2103. if (FAILED (sc))
  2104. {
  2105. SendResponse(sc);
  2106. return;
  2107. }
  2108. // Enumerate the child vroots and perform the
  2109. // deletion of those directories as well
  2110. //
  2111. m_pmu->ScFindChildVRoots (pwszUrl, sb, vrl);
  2112. for ( ; (!FAILED (sc) && !vrl.empty()); vrl.pop_front())
  2113. {
  2114. auto_ref_ptr<CVRoot> cvr;
  2115. LPCWSTR pwszChildUrl;
  2116. LPCWSTR pwszChildPath;
  2117. if (m_pmu->FGetChildVRoot (vrl.front().m_pwsz, cvr))
  2118. {
  2119. // Put the url into a multibyte string
  2120. //
  2121. cvr->CchGetVRoot (&pwszChildUrl);
  2122. // Only process the sub-vroot if we are
  2123. // truely are going deep or if the sub-vroot
  2124. // is the immediate child of the request URI
  2125. //
  2126. if ((m_lDepth == DEPTH_INFINITY) ||
  2127. FIsImmediateParentUrl (pwszUrl, pwszChildUrl))
  2128. {
  2129. CResourceInfo criSub;
  2130. // Crack the vroot and go...
  2131. //
  2132. cvr->CchGetVRPath (&pwszChildPath);
  2133. sc = criSub.ScGetResourceInfo (pwszChildPath);
  2134. if (!FAILED (sc))
  2135. {
  2136. // Find the properties on the vroot root
  2137. //
  2138. sc = ScFindFileProps (m_pmu.get(),
  2139. m_cfc,
  2140. *pmsr,
  2141. pwszChildUrl,
  2142. pwszChildPath,
  2143. cvr.get(),
  2144. criSub,
  2145. TRUE /*fEmbedErrorsInResponse*/);
  2146. }
  2147. if (FAILED (sc))
  2148. {
  2149. SendResponse(sc);
  2150. return;
  2151. }
  2152. else if (S_FALSE == sc)
  2153. continue;
  2154. // Find the properties on the vroot kids
  2155. //
  2156. if (m_lDepth == DEPTH_INFINITY)
  2157. {
  2158. auto_ref_ptr<IMDData> pMDData;
  2159. // See if we have directory browsing...
  2160. //
  2161. if (SUCCEEDED(m_pmu->HrMDGetData (pwszChildUrl, pMDData.load())) &&
  2162. (pMDData->DwDirBrowsing() & MD_DIRBROW_ENABLED))
  2163. {
  2164. sc = ScFindFilePropsDeep (m_pmu.get(),
  2165. m_cfc,
  2166. *pmsr,
  2167. pwszChildUrl,
  2168. pwszChildPath,
  2169. cvr.get(),
  2170. m_lDepth);
  2171. if (FAILED (sc))
  2172. {
  2173. SendResponse(sc);
  2174. return;
  2175. }
  2176. }
  2177. }
  2178. }
  2179. }
  2180. }
  2181. }
  2182. // Done with the reponse
  2183. //
  2184. pmsr->Done();
  2185. m_pmu->SendCompleteResponse();
  2186. }
  2187. VOID
  2188. CPropFindRequest::SendResponse( SCODE sc )
  2189. {
  2190. //
  2191. // Set the response code and go
  2192. //
  2193. m_pmu->SetResponseCode( HscFromHresult(sc), NULL, 0, CSEFromHresult(sc) );
  2194. m_pmu->SendCompleteResponse();
  2195. }
  2196. /*
  2197. * DAVPropFind()
  2198. *
  2199. * Purpose:
  2200. *
  2201. * Win32 file system implementation of the DAV PROPGET method. The
  2202. * PROPGET method responds with a fully constructed XML that provides
  2203. * the values of the resources property/properties.
  2204. *
  2205. * Parameters:
  2206. *
  2207. * pmu [in] pointer to the method utility object
  2208. */
  2209. void
  2210. DAVPropFind (LPMETHUTIL pmu)
  2211. {
  2212. auto_ref_ptr<CPropFindRequest> pRequest(new CPropFindRequest(pmu));
  2213. pRequest->Execute();
  2214. }
  2215. // CPropPatchRequest ----------------------------------------------------------------
  2216. //
  2217. class CPropPatchRequest :
  2218. public CMTRefCounted,
  2219. private IAsyncIStreamObserver
  2220. {
  2221. //
  2222. // Reference to the CMethUtil
  2223. //
  2224. auto_ref_ptr<CMethUtil> m_pmu;
  2225. //
  2226. // Translated URI path
  2227. //
  2228. LPCWSTR m_pwszPath;
  2229. // Holds a handle owned by the lock cache.
  2230. //
  2231. auto_ref_handle m_hf;
  2232. // Resource info
  2233. //
  2234. CResourceInfo m_cri;
  2235. // Contexts
  2236. //
  2237. CFSPatch m_cpc;
  2238. auto_ref_ptr<CNFPatch> m_pnfp;
  2239. // Request body as an IStream. This stream is async -- it can
  2240. // return E_PENDING from Read() calls.
  2241. //
  2242. auto_ref_ptr<IStream> m_pstmRequest;
  2243. // The XML parser used to parse the request body using
  2244. // the node factory above.
  2245. //
  2246. auto_ref_ptr<IXMLParser> m_pxprs;
  2247. // IAsyncIStreamObserver
  2248. //
  2249. VOID AsyncIOComplete();
  2250. // State functions
  2251. //
  2252. VOID ParseBody();
  2253. VOID DoPatch();
  2254. VOID SendResponse( SCODE sc );
  2255. // NOT IMPLEMENTED
  2256. //
  2257. CPropPatchRequest (const CPropPatchRequest&);
  2258. CPropPatchRequest& operator= (const CPropPatchRequest&);
  2259. public:
  2260. // CREATORS
  2261. //
  2262. CPropPatchRequest(LPMETHUTIL pmu) :
  2263. m_pmu(pmu),
  2264. m_pwszPath(m_pmu->LpwszPathTranslated())
  2265. {
  2266. }
  2267. SCODE ScInit() { return m_cpc.ScInit(); }
  2268. // MANIPULATORS
  2269. //
  2270. VOID Execute();
  2271. };
  2272. VOID
  2273. CPropPatchRequest::Execute()
  2274. {
  2275. LPCWSTR pwsz;
  2276. SCODE sc = S_OK;
  2277. //
  2278. // First off, tell the pmu that we want to defer the response.
  2279. // Even if we send it synchronously (i.e. due to an error in
  2280. // this function), we still want to use the same mechanism that
  2281. // we would use for async.
  2282. //
  2283. m_pmu->DeferResponse();
  2284. // Do ISAPI application and IIS access bits checking
  2285. //
  2286. sc = m_pmu->ScIISCheck (m_pmu->LpwszRequestUrl(), MD_ACCESS_WRITE);
  2287. if (FAILED(sc))
  2288. {
  2289. // Either the request has been forwarded, or some bad error occurred.
  2290. // In either case, quit here and map the error!
  2291. //
  2292. SendResponse(sc);
  2293. return;
  2294. }
  2295. // PropPatch must have a content-type header and the value must be text/xml
  2296. //
  2297. sc = ScIsContentTypeXML (m_pmu.get());
  2298. if (FAILED(sc))
  2299. {
  2300. DebugTrace ("Dav: PROPPATCH fails without specifying a text/xml contenttype\n");
  2301. SendResponse(sc);
  2302. return;
  2303. }
  2304. // Look to see the Content-length - required for this operation
  2305. // to continue.
  2306. //
  2307. if (NULL == m_pmu->LpwszGetRequestHeader (gc_szContent_Length, FALSE))
  2308. {
  2309. DebugTrace ("Dav: PROPPATCH fails without content\n");
  2310. SendResponse(E_DAV_MISSING_LENGTH);
  2311. return;
  2312. }
  2313. if (!m_pmu->FExistsRequestBody())
  2314. {
  2315. DebugTrace ("Dav: PROPPATCH fails without content\n");
  2316. SendResponse(E_INVALIDARG);
  2317. return;
  2318. }
  2319. // This method is gated by If-xxx headers
  2320. //
  2321. if (!FAILED (m_cri.ScGetResourceInfo (m_pwszPath)))
  2322. {
  2323. // Ensure the URI and resource match...
  2324. //
  2325. (void) ScCheckForLocationCorrectness (m_pmu.get(), m_cri, NO_REDIRECT);
  2326. // ... then check the headers
  2327. //
  2328. sc = ScCheckIfHeaders (m_pmu.get(), m_cri.PftLastModified(), FALSE);
  2329. }
  2330. else
  2331. sc = ScCheckIfHeaders (m_pmu.get(), m_pwszPath, FALSE);
  2332. if (FAILED (sc))
  2333. {
  2334. SendResponse(sc);
  2335. return;
  2336. }
  2337. // Check state headers here.
  2338. //
  2339. sc = HrCheckStateHeaders (m_pmu.get(), m_pwszPath, FALSE);
  2340. if (FAILED (sc))
  2341. {
  2342. DebugTrace ("DavFS: If-State checking failed.\n");
  2343. SendResponse(sc);
  2344. return;
  2345. }
  2346. // Handle locktokens and check for locks on this resource.
  2347. // Our locks don't lock the "secondary file stream" where we keep
  2348. // the properties, so we have to check manually before we do anything else.
  2349. //$REVIEW: Joels, will this change when we switch to NT5 properties?
  2350. //$REVIEW: If so, we need to change this code!
  2351. //
  2352. // If we have a locktoken, try to get the lock handle from the cache.
  2353. // If this fails, fall through and do the normal processing.
  2354. //
  2355. pwsz = m_pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  2356. if (!pwsz || !FGetLockHandle (m_pmu.get(), m_pwszPath, GENERIC_WRITE, pwsz, &m_hf))
  2357. {
  2358. // Manually check for any write locks on this resource.
  2359. // If a write lock exists, don't process the request.
  2360. //
  2361. if (FLockViolation (m_pmu.get(), ERROR_SHARING_VIOLATION, m_pwszPath, GENERIC_WRITE))
  2362. {
  2363. SendResponse(E_DAV_LOCKED);
  2364. return;
  2365. }
  2366. }
  2367. // Instantiate the XML parser
  2368. //
  2369. m_pnfp.take_ownership(new CNFPatch(m_cpc));
  2370. m_pstmRequest.take_ownership(m_pmu->GetRequestBodyIStream(*this));
  2371. sc = ScNewXMLParser( m_pnfp.get(),
  2372. m_pstmRequest.get(),
  2373. m_pxprs.load() );
  2374. if (FAILED(sc))
  2375. {
  2376. DebugTrace( "CPropPatchRequest::Execute() - ScNewXMLParser() failed (0x%08lX)\n", sc );
  2377. SendResponse(sc);
  2378. return;
  2379. }
  2380. // Start parsing it into the context
  2381. //
  2382. ParseBody();
  2383. }
  2384. VOID
  2385. CPropPatchRequest::ParseBody()
  2386. {
  2387. Assert( m_pxprs.get() );
  2388. Assert( m_pnfp.get() );
  2389. Assert( m_pstmRequest.get() );
  2390. // Parse XML from the request body stream.
  2391. //
  2392. // Add a ref for the following async operation.
  2393. // Use auto_ref_ptr rather than AddRef() for exception safety.
  2394. //
  2395. auto_ref_ptr<CPropPatchRequest> pRef(this);
  2396. SCODE sc = ScParseXML (m_pxprs.get(), m_pnfp.get());
  2397. if ( SUCCEEDED(sc) )
  2398. {
  2399. Assert( S_OK == sc || S_FALSE == sc );
  2400. DoPatch();
  2401. }
  2402. else if ( E_PENDING == sc )
  2403. {
  2404. //
  2405. // The operation is pending -- AsyncIOComplete() will take ownership
  2406. // ownership of the reference when it is called.
  2407. //
  2408. pRef.relinquish();
  2409. }
  2410. else
  2411. {
  2412. DebugTrace( "CPropPatchRequest::ParseBody() - ScParseXML() failed (0x%08lX)\n", sc );
  2413. SendResponse(sc);
  2414. }
  2415. }
  2416. VOID
  2417. CPropPatchRequest::AsyncIOComplete()
  2418. {
  2419. // Take ownership of the reference added for the async operation.
  2420. //
  2421. auto_ref_ptr<CPropPatchRequest> pRef;
  2422. pRef.take_ownership(this);
  2423. ParseBody();
  2424. }
  2425. VOID
  2426. CPropPatchRequest::DoPatch()
  2427. {
  2428. SCODE sc;
  2429. // At this point, make sure that they support text/xml
  2430. //
  2431. sc = ScIsAcceptable (m_pmu.get(), gc_wszText_XML);
  2432. if (FAILED (sc))
  2433. {
  2434. SendResponse(sc);
  2435. return;
  2436. }
  2437. // Get the IPropertyBagEx on the resource
  2438. // If the file is locked, we must use its handle to
  2439. // get the inteface. otherwise, access will be denied
  2440. //
  2441. auto_com_ptr<IPropertyBagEx> pbag;
  2442. sc = ScGetPropertyBag (m_pwszPath,
  2443. STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
  2444. &pbag,
  2445. m_cri.FLoaded() ? m_cri.FCollection() : FALSE,
  2446. m_hf.get() ? m_hf.get() : INVALID_HANDLE_VALUE);
  2447. if (FAILED (sc))
  2448. {
  2449. // You can't set properties without a property bag...
  2450. //
  2451. if (VOLTYPE_NTFS != VolumeType (m_pwszPath, m_pmu->HitUser()))
  2452. sc = E_DAV_VOLUME_NOT_NTFS;
  2453. SendResponse(sc);
  2454. return;
  2455. }
  2456. // All header must be emitted before chunked XML emitting starts
  2457. //
  2458. m_pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
  2459. // Set the response code and go
  2460. //
  2461. m_pmu->SetResponseCode( HscFromHresult(W_DAV_PARTIAL_SUCCESS),
  2462. NULL,
  2463. 0,
  2464. CSEFromHresult(W_DAV_PARTIAL_SUCCESS) );
  2465. // Apply the request
  2466. //
  2467. auto_ref_ptr<CXMLEmitter> pmsr;
  2468. auto_ref_ptr<CXMLBody> pxb;
  2469. pxb.take_ownership (new CXMLBody(m_pmu.get()));
  2470. pmsr.take_ownership (new CXMLEmitter(pxb.get(), &m_cpc));
  2471. CFSProp fsp(m_pmu.get(),
  2472. pbag,
  2473. m_pmu->LpwszRequestUrl(),
  2474. m_pwszPath,
  2475. NULL,
  2476. m_cri);
  2477. sc = m_cpc.ScPatch (*pmsr, m_pmu.get(), fsp);
  2478. // Make sure we close the file before sending any response
  2479. //
  2480. pbag.clear();
  2481. if (FAILED (sc))
  2482. {
  2483. SendResponse(sc);
  2484. return;
  2485. }
  2486. // Done with the reponse
  2487. //
  2488. pmsr->Done();
  2489. m_pmu->SendCompleteResponse();
  2490. }
  2491. VOID
  2492. CPropPatchRequest::SendResponse( SCODE sc )
  2493. {
  2494. //
  2495. // Set the response code and go
  2496. //
  2497. m_pmu->SetResponseCode( HscFromHresult(sc), NULL, 0, CSEFromHresult(sc) );
  2498. m_pmu->SendCompleteResponse();
  2499. }
  2500. /*
  2501. * DAVPropPatch()
  2502. *
  2503. * Purpose:
  2504. *
  2505. * Win32 file system implementation of the DAV PROPPATCH method. The
  2506. * PROPPATCH method responds with a fully constructed XML that identifies
  2507. * the success of each prop request.
  2508. *
  2509. * Parameters:
  2510. *
  2511. * pmu [in] pointer to the method utility object
  2512. */
  2513. void
  2514. DAVPropPatch (LPMETHUTIL pmu)
  2515. {
  2516. SCODE sc;
  2517. auto_ref_ptr<CPropPatchRequest> pRequest(new CPropPatchRequest(pmu));
  2518. sc = pRequest->ScInit();
  2519. if (FAILED(sc))
  2520. {
  2521. pmu->SetResponseCode( HscFromHresult(sc), NULL, 0, CSEFromHresult(sc) );
  2522. pmu->SendCompleteResponse();
  2523. }
  2524. pRequest->Execute();
  2525. }