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.

1206 lines
33 KiB

  1. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. //
  3. // LOCKUTIL.CPP
  4. //
  5. // HTTP 1.1/DAV 1.0 LOCK request handling UTILITIES
  6. //
  7. //
  8. // Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  9. //
  10. #include "_davfs.h"
  11. #include <tchar.h> //_strspnp
  12. #include <statetok.h>
  13. #include <xlock.h>
  14. #include "_shlkmgr.h"
  15. // ========================================================================
  16. //
  17. // ScLockDiscoveryFromSNewLockData
  18. //
  19. // Takes an emitter and an already-constructed lockdiscovery node,
  20. // and adds an activelock node for this CLock under it.
  21. // May be called multiple times -- each call will add a new activelock
  22. // node under the lockdiscovery node in en.
  23. //$HACK:ROSEBUD_OFFICE9_TIMEOUT_HACK
  24. // For the bug where rosebud waits until the last second
  25. // before issueing the refresh. Need to filter out this check with
  26. // the user agent string. The hack is to increase the timeout
  27. // by 30 seconds and return the actual timeout. So we
  28. // need the ecb/pmu to findout the user agent. If we
  29. // remove this hack ever (I doubt if we can ever do that), then
  30. // change the interface of ScLockDiscoveryFromCLock.
  31. //$HACK:END ROSEBUD_OFFICE9_TIMEOUT_HACK
  32. //
  33. SCODE
  34. ScLockDiscoveryFromSNewLockData(LPMETHUTIL pmu,
  35. CXMLEmitter& emitter,
  36. CEmitterNode& en,
  37. SNewLockData * pnld,
  38. LPCWSTR pwszLockToken)
  39. {
  40. BOOL fRollback;
  41. BOOL fDepthInfinity;
  42. DWORD dwLockScope;
  43. DWORD dwLockType;
  44. LPCWSTR pwszLockScope = NULL;
  45. LPCWSTR pwszLockType = NULL;
  46. HRESULT hr = S_OK;
  47. DWORD dwSeconds = 0;
  48. Assert(pmu);
  49. Assert(pnld);
  50. // Get the lock flags from the lock.
  51. //
  52. dwLockType = pnld->m_dwLockType;
  53. // Note if the lock is a rollback
  54. //
  55. fRollback = !!(dwLockType & DAV_LOCKTYPE_ROLLBACK);
  56. // Note if the lock is recursive
  57. //
  58. fDepthInfinity = !!(dwLockType & DAV_RECURSIVE_LOCK);
  59. // Write lock?
  60. //
  61. if (dwLockType & GENERIC_WRITE)
  62. {
  63. pwszLockType = gc_wszLockTypeWrite;
  64. }
  65. #ifdef DBG
  66. if (dwLockType & GENERIC_READ)
  67. {
  68. pwszLockType = L"read";
  69. }
  70. #else // !DBG
  71. else
  72. {
  73. TrapSz ("Unexpected lock type!");
  74. }
  75. #endif // DBG, else
  76. // Lock scope
  77. //
  78. dwLockScope = pnld->m_dwLockScope;
  79. if (dwLockScope & DAV_SHARED_LOCK)
  80. {
  81. pwszLockScope = gc_wszLockScopeShared;
  82. }
  83. else
  84. {
  85. Assert (dwLockScope & DAV_EXCLUSIVE_LOCK);
  86. pwszLockScope = gc_wszLockScopeExclusive;
  87. }
  88. dwSeconds = pnld->m_dwSecondsTimeout;
  89. //$HACK:ROSEBUD_OFFICE9_TIMEOUT_HACK
  90. // For the bug where rosebud waits until the last second
  91. // before issueing the refresh. Need to filter out this check with
  92. // the user agent string. The hack is to increase the timeout
  93. // by 30 seconds. Now decrease 30 seconds to send requested timeout.
  94. //
  95. if (pmu && pmu->FIsOffice9Request())
  96. {
  97. if (dwSeconds > gc_dwSecondsHackTimeoutForRosebud)
  98. {
  99. dwSeconds -= gc_dwSecondsHackTimeoutForRosebud;
  100. }
  101. }
  102. //$HACK: END: ROSEBUD_OFFICE9_TIMEOUT_HACK
  103. // Construct the lockdiscovery node
  104. //
  105. hr = ScBuildLockDiscovery (emitter,
  106. en,
  107. pwszLockToken,
  108. pwszLockType,
  109. pwszLockScope,
  110. fRollback,
  111. fDepthInfinity,
  112. dwSeconds,
  113. pnld->m_pwszOwnerComment,
  114. NULL);
  115. if (FAILED (hr))
  116. {
  117. goto ret;
  118. }
  119. ret:
  120. return hr;
  121. }
  122. // ------------------------------------------------------------------------
  123. //
  124. // ScAddSupportedLockProp
  125. //
  126. // Add a lockentry node with the listed information.
  127. // NOTE: wszExtra is currently used for rollback information.
  128. //
  129. SCODE
  130. ScAddSupportedLockProp (CEmitterNode& en,
  131. LPCWSTR wszLockType,
  132. LPCWSTR wszLockScope,
  133. LPCWSTR wszExtra = NULL)
  134. {
  135. CEmitterNode enEntry;
  136. SCODE sc = S_OK;
  137. Assert (wszLockType);
  138. Assert (wszLockScope);
  139. // Create a lockentry node to hold this info.
  140. //
  141. sc = en.ScAddNode (gc_wszLockEntry, enEntry);
  142. if (FAILED (sc))
  143. goto ret;
  144. // Create a node for the locktype under the lockentry.
  145. //
  146. {
  147. // Must scope here, all sibling nodes must be constructed sequentially
  148. //
  149. CEmitterNode enType;
  150. sc = enEntry.ScAddNode (wszLockType, enType);
  151. if (FAILED (sc))
  152. goto ret;
  153. }
  154. // Create a node for the locktype under the lockentry.
  155. //
  156. {
  157. // Must scope here, all sibling nodes must be constructed sequentially
  158. //
  159. CEmitterNode enScope;
  160. sc = enEntry.ScAddNode (wszLockScope, enScope);
  161. if (FAILED (sc))
  162. goto ret;
  163. }
  164. // If we have extra info, create a node for it under the lockentry.
  165. //
  166. if (wszExtra)
  167. {
  168. // Must scope here, all sibling nodes must be constructed sequentially
  169. //
  170. CEmitterNode enExtra;
  171. sc = enEntry.ScAddNode (wszExtra, enExtra);
  172. if (FAILED (sc))
  173. goto ret;
  174. }
  175. ret:
  176. return sc;
  177. }
  178. // ------------------------------------------------------------------------
  179. //
  180. // HrGetLockProp
  181. //
  182. // Get the requested lock property for the requested resource.
  183. // (The lock properties are lockdiscovery and supportedlock.)
  184. // Lockdiscovery and supportedlock should ALWAYS be found --
  185. // they are required DAV: properties. Add an empty node if there is
  186. // no real data to return.
  187. // NOTE: This function still assumes that write is the only locktype.
  188. // It will NOT add read/mixed locktypes.
  189. //
  190. // Returns
  191. // S_FALSE if prop not found/not recognized.
  192. // error only if something really bad happens.
  193. //
  194. //$REVIEW: Should I return the depth element too? -- No (for now).
  195. //$REVIEW: Spec does NOT list depth under the lockentry XML element.
  196. //
  197. HRESULT
  198. HrGetLockProp (LPMETHUTIL pmu,
  199. LPCWSTR wszPropName,
  200. LPCWSTR wszResource,
  201. RESOURCE_TYPE rtResource,
  202. CXMLEmitter& emitter,
  203. CEmitterNode& enParent)
  204. {
  205. SCODE sc = S_OK;
  206. Assert (pmu);
  207. Assert (wszPropName);
  208. Assert (wszResource);
  209. if (!wcscmp (wszPropName, gc_wszLockDiscovery))
  210. {
  211. // Fill in lockdiscovery info.
  212. //
  213. // Check for any lock in our lock cache.
  214. // This call will scan the lock cache for any matching items
  215. // and add a 'DAV:activelock' node for each match.
  216. // We pass in DAV_LOCKTYPE_FLAGS so that we will find all matches.
  217. //
  218. if (!CSharedLockMgr::Instance().FGetLockOnError (pmu,
  219. wszResource,
  220. DAV_LOCKTYPE_FLAGS,
  221. TRUE, // Emit XML body
  222. &emitter,
  223. enParent.Pxn()))
  224. {
  225. // This resource is not in our lock cache.
  226. //
  227. FsLockTrace ("HrGetLockProp -- No locks found for lockdiscovery.\n");
  228. // And return. This is a SUCCESS case!
  229. //
  230. }
  231. }
  232. else if (!wcscmp (wszPropName, gc_wszLockSupportedlock))
  233. {
  234. DWORD dwLockType;
  235. CEmitterNode en;
  236. // Construct the 'DAV:supportedlock' node
  237. //
  238. sc = en.ScConstructNode (emitter, enParent.Pxn(), gc_wszLockSupportedlock);
  239. if (FAILED (sc))
  240. goto ret;
  241. // Get the list of supported lock flags from the impl.
  242. //
  243. dwLockType = DwGetSupportedLockType (rtResource);
  244. if (!dwLockType)
  245. {
  246. // No locktypes are supported. We already have our empty
  247. // supportedlock node.
  248. // Just return. This is a SUCCESS case!
  249. goto ret;
  250. }
  251. // Add a lockentry node under the supportedlock node for each
  252. // combination of flags that we detect.
  253. //
  254. // NOTE: Currently, write is the only allowed access type.
  255. //
  256. if (dwLockType & GENERIC_WRITE)
  257. {
  258. // Add a lockentry for each lockscope in the flags.
  259. //
  260. if (dwLockType & DAV_SHARED_LOCK)
  261. {
  262. sc = ScAddSupportedLockProp (en,
  263. gc_wszLockTypeWrite,
  264. gc_wszLockScopeShared);
  265. if (FAILED (sc))
  266. goto ret;
  267. // If we support lock rollback, add another lockentry for this combo.
  268. //
  269. if (dwLockType & DAV_LOCKTYPE_ROLLBACK)
  270. {
  271. sc = ScAddSupportedLockProp (en,
  272. gc_wszLockTypeWrite,
  273. gc_wszLockScopeShared,
  274. gc_wszLockRollback);
  275. if (FAILED (sc))
  276. goto ret;
  277. }
  278. }
  279. if (dwLockType & DAV_EXCLUSIVE_LOCK)
  280. {
  281. sc = ScAddSupportedLockProp (en,
  282. gc_wszLockTypeWrite,
  283. gc_wszLockScopeExclusive);
  284. if (FAILED (sc))
  285. goto ret;
  286. // If we support lock rollback, add another lockentry for this combo.
  287. //
  288. if (dwLockType & DAV_LOCKTYPE_ROLLBACK)
  289. {
  290. sc = ScAddSupportedLockProp (en,
  291. gc_wszLockTypeWrite,
  292. gc_wszLockScopeExclusive,
  293. gc_wszLockRollback);
  294. if (FAILED (sc))
  295. goto ret;
  296. }
  297. }
  298. }
  299. }
  300. else
  301. {
  302. // Unrecognized lock property. So we clearly do not have one
  303. //
  304. sc = S_FALSE;
  305. goto ret;
  306. }
  307. ret:
  308. return sc;
  309. }
  310. // ------------------------------------------------------------------------
  311. //
  312. // FLockViolation
  313. //
  314. // TRUE return here means that we found a lock, and sent the response.
  315. //
  316. //$LATER: Need to be able to return an error here!
  317. //
  318. BOOL
  319. FLockViolation (LPMETHUTIL pmu, HRESULT hr, LPCWSTR pwszPath, DWORD dwAccess)
  320. {
  321. BOOL fFound = FALSE;
  322. SCODE sc = S_OK;
  323. auto_ref_ptr<CXMLBody> pxb;
  324. auto_ref_ptr<CXMLEmitter> emitter;
  325. Assert (pmu);
  326. Assert (pwszPath);
  327. AssertSz (dwAccess, "FLockViolation: Looking for a lock with no access!");
  328. // Construct the root ('DAV:prop') for the lock response
  329. //$NOTE: this xml body is created NOT chunked
  330. //
  331. pxb.take_ownership (new CXMLBody (pmu, FALSE) );
  332. emitter.take_ownership (new CXMLEmitter(pxb.get()));
  333. sc = emitter->ScSetRoot (gc_wszProp);
  334. if (FAILED (sc))
  335. goto ret;
  336. // If the error code is one of the "locked" error codes,
  337. // check our lock cache for a corresponding lock object.
  338. //
  339. if ((ERROR_SHARING_VIOLATION == ((SCODE)hr) ||
  340. HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr ||
  341. STG_E_SHAREVIOLATION == hr) &&
  342. CSharedLockMgr::Instance().FGetLockOnError (pmu, pwszPath, dwAccess, TRUE, emitter.get(), emitter->PxnRoot()))
  343. {
  344. // Set our found bit to TRUE now, so that we'll report the lock's
  345. // existence, even if the emitting below fails!
  346. // NOTE: This is important for scenarios, like HTTPEXT PROPPATCH
  347. // and destination deletion for Overwrite handling, that
  348. // PRE-check the lock cache (protocol-enforced locks)
  349. // before trying to hit the file.
  350. //
  351. fFound = TRUE;
  352. // Set content type header
  353. //
  354. pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
  355. // Must set the response code before we set the body data.
  356. //
  357. pmu->SetResponseCode (HSC_LOCKED, NULL, 0);
  358. // Emit the XML body
  359. //
  360. emitter->Done();
  361. }
  362. // Tell our caller if we found any locks on this item.
  363. //
  364. ret:
  365. return fFound;
  366. }
  367. // ------------------------------------------------------------------------
  368. //
  369. // HrLockIdFromString
  370. //
  371. // Returns S_OK on success (syntax check and conversion).
  372. // Returns E_DAV_INVALID_HEADER on syntax error or non-matching token guid (not ours).
  373. // Returns other errors if something fatal happened.
  374. //
  375. HRESULT
  376. HrLockIdFromString (LPMETHUTIL pmu,
  377. LPCWSTR pwszToken,
  378. LARGE_INTEGER * pliLockID)
  379. {
  380. HRESULT hr = S_OK;
  381. LPCWSTR pwsz = pwszToken;
  382. UINT cchGUIDString = gc_cchMaxGuid;
  383. WCHAR rgwszGUIDString[gc_cchMaxGuid];
  384. Assert (pmu);
  385. Assert (pwszToken);
  386. Assert (pliLockID);
  387. (*pliLockID).QuadPart = 0;
  388. // Skip any initial whitespace.
  389. //
  390. pwsz = _wcsspnp (pwsz, gc_wszLWS);
  391. if (!pwsz)
  392. {
  393. FsLockTrace ("Dav: Invalid locktoken in HrLockIdFromString.\n");
  394. hr = E_DAV_INVALID_HEADER;
  395. goto ret;
  396. }
  397. // Skip delimiter: double-quotes or angle-brackets.
  398. // It's okay if no delimiter is present. Caller just passed us raw locktoken string.
  399. //
  400. if (L'\"' == *pwsz ||
  401. L'<' == *pwsz)
  402. pwsz++;
  403. if (wcsncmp (gc_wszOpaquelocktokenPrefix, pwsz, gc_cchOpaquelocktokenPrefix))
  404. {
  405. FsLockTrace ("Dav: Lock token is missing opaquelocktoken: prefix.\n");
  406. hr = E_DAV_INVALID_HEADER;
  407. goto ret;
  408. }
  409. // Skip the opaquelocktoken: prefix
  410. //
  411. pwsz += gc_cchOpaquelocktokenPrefix;
  412. // Compare GUIDS here
  413. //
  414. hr = CSharedLockMgr::Instance().HrGetGUIDString(pmu->HitUser(),
  415. cchGUIDString,
  416. rgwszGUIDString,
  417. &cchGUIDString);
  418. if (FAILED(hr))
  419. {
  420. goto ret;
  421. }
  422. // Subtract L'\0' termination
  423. //
  424. Assert(cchGUIDString);
  425. cchGUIDString--;
  426. if (_wcsnicmp(pwsz, rgwszGUIDString, cchGUIDString))
  427. {
  428. FsLockTrace ("Dav: Error comparing guids -- not our locktoken!\n");
  429. hr = E_DAV_INVALID_HEADER;
  430. goto ret;
  431. }
  432. // Skip the GUID, go to the lockid string.
  433. //
  434. pwsz = wcschr (pwsz, L':');
  435. if (!pwsz)
  436. {
  437. FsLockTrace ("Dav: Error skipping guid of opaquelocktoken.\n");
  438. hr = E_DAV_INVALID_HEADER;
  439. goto ret;
  440. }
  441. // And skip the colon separator.
  442. //
  443. Assert (L':' == *pwsz);
  444. pwsz++;
  445. // Convert the string to lockID and return (this one actually has boundary
  446. // condition that is not covered - lockID can actually be 0 in theory if there
  447. // were so many locks that we rolled over)
  448. //
  449. (*pliLockID).QuadPart = _wtoi64(pwsz);
  450. if (0 == (*pliLockID).QuadPart)
  451. {
  452. hr = E_DAV_INVALID_HEADER;
  453. goto ret;
  454. }
  455. ret:
  456. return hr;
  457. }
  458. // ------------------------------------------------------------------------
  459. // HrValidTokenExpression()
  460. //
  461. // Helper function for If: header processing.
  462. // Once we've found a token, this function will check the path.
  463. // (So this function only succeeds completely if the token is still valid,
  464. // AND the token matches the provided path.)
  465. // If this token is valid, this function returns S_OK
  466. // If this token is not valid, this function returns E_DAV_INVALID_HEADER
  467. // If other fatal errors occured we propogate them out of the function
  468. //
  469. HRESULT
  470. HrValidTokenExpression (IMethUtil * pmu,
  471. LPCWSTR pwszToken,
  472. LPCWSTR pwszPath,
  473. OUT LARGE_INTEGER * pliLockID)
  474. {
  475. HRESULT hr = S_OK;
  476. LARGE_INTEGER liLockID;
  477. Assert (pmu);
  478. Assert (pwszToken);
  479. Assert (pwszPath);
  480. // Get the lock tokens
  481. //
  482. hr = HrLockIdFromString (pmu, pwszToken, &liLockID);
  483. if (FAILED(hr))
  484. {
  485. // Unrecognized locktoken. Does not match.
  486. //
  487. goto ret;
  488. }
  489. // Check if the locktoken is valid (live in the cache).
  490. // E_DAV_INVALID_HEADER means the lock was not found,
  491. // paths conflicted or owners were not the same
  492. //
  493. hr = CSharedLockMgr::Instance().HrCheckLockID(liLockID,
  494. pmu->HitUser(),
  495. pwszPath);
  496. if (FAILED(hr))
  497. {
  498. if (E_DAV_LOCK_NOT_FOUND == hr ||
  499. HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hr ||
  500. E_DAV_CONFLICTING_PATHS == hr)
  501. {
  502. hr = E_DAV_INVALID_HEADER;
  503. }
  504. goto ret;
  505. }
  506. // If they requested the lock id back, give it to 'em.
  507. //
  508. if (pliLockID)
  509. {
  510. *pliLockID = liLockID;
  511. }
  512. ret:
  513. return hr;
  514. }
  515. // ------------------------------------------------------------------------
  516. //
  517. // HrCheckIfHeader
  518. //
  519. // Check the If header.
  520. // Processing will check the lock cache to validate locktokens.
  521. //
  522. // The pmu (IMethUtil) is provided for access to the lock cache to check tokens.
  523. // The pwszPath provides the path to match for untagged lists.
  524. //
  525. // Format of the If header
  526. // If = "If" ":" ( 1*No-tag-list | 1*Tagged-list)
  527. // No-tag-list = List
  528. // Tagged-list = Resource 1*List
  529. // Resource = Coded-url
  530. // List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")"
  531. // State-token = Coded-url
  532. // Coded-url = "<" URI ">"
  533. // Basically, one thing has to match in the whole header in order for the
  534. // entire header to be "good".
  535. // Each URI has a _set_ of state lists. A list is enclosed in parentheses.
  536. // Each list is a logical "and".
  537. // A set of lists is a logical "or".
  538. //
  539. // Returns:
  540. // S_OK Process the method.
  541. // other error Map the error
  542. // (412 will be handled by this case)
  543. //
  544. // DAV-compliance shortfalls
  545. // We fall short of true DAV-compliance in three spots in this function.
  546. // 1 - This code does not prevent (fail) an utagged list followed by
  547. // a tagged list. Strict DAV-compliance would FAIL such an If-header
  548. // as a bad request.
  549. // 2 - This code does not "correctly" apply tagged lists with multiple
  550. // URIs. Strict DAV-compliance would require evaluating the If-header
  551. // once for each URI as the method is processed, and ignore any URIs
  552. // in the tagged list that never were "processed". We don't (can't)
  553. // process our MOVE/COPY/DELETEs that way, but instead do a pre-checking
  554. // pass on the If: header. At pre-check time, we treat the If-header
  555. // as if the tagged lists are all AND-ed together.
  556. // THIS MEANS that if a URI is listed, and it doesn't have a good
  557. // matching (valid) list, we will FAIL the whole method with 412 Precondition Failed.
  558. // 3 - This code does not handle ETags in the If-header.
  559. //
  560. //$LATER: When we are part of the locktoken header, check the m_fPathsSet.
  561. //$LATER: We might be able to get our info quicker if paths are already set!
  562. //
  563. HRESULT
  564. HrCheckIfHeader (IMethUtil * m_pmu, // to ease the transition later...
  565. LPCWSTR pwszDefaultPath)
  566. {
  567. HRESULT hr = S_OK;
  568. BOOL fOneMatch = FALSE;
  569. FETCH_TOKEN_TYPE tokenNext = TOKEN_SAME_LIST;
  570. LPCWSTR pwsz;
  571. LPCWSTR pwszToken;
  572. LPCWSTR pwszPath = pwszDefaultPath;
  573. CStackBuffer<WCHAR,MAX_PATH> pwszTranslated;
  574. BOOL fFirstURI;
  575. WCHAR rgwchEtag[MAX_PATH];
  576. // Quick check -- if the header doesn't exist, just process the method.
  577. //
  578. pwsz = m_pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  579. if (!pwsz)
  580. return S_OK;
  581. IFITER iter(pwsz);
  582. // Double nested loop
  583. // First loop (outer loop) looks through all the "tagged lists"
  584. // (tagged list = URI + set of lists of tokens)
  585. // If the first list is untagged, use the default path (the request URI)
  586. // for the untagged first set of lists.
  587. // Second loop looks through all the token lists for a single URI.
  588. //
  589. // NOTE: This code does NOT perfectly implement the draft.
  590. // The draft says that an untagged production (no initial URI)
  591. // can't have any subsequent URIs. Frankly, that's much more complex to
  592. // implement -- need to set another bool var and DISALLOW that one case.
  593. // So I'm skipping it for now. --BeckyAn
  594. //
  595. fFirstURI = TRUE;
  596. for (pwsz = iter.PszNextToken (TOKEN_URI); // start with the first URI
  597. pwsz || fFirstURI;
  598. pwsz = iter.PszNextToken (TOKEN_NEW_URI)) // skip to the next URI in the list
  599. {
  600. // If our search for the first URI came up blank, use
  601. // the default path instead.
  602. // NOTE: This can only happen if it's the first URI (fFirstURI is TRUE)
  603. // (we explicitly check psz in the loop condition, and QUIT the loop
  604. // if neither psz or fFirstURI are true).
  605. //
  606. if (!pwsz)
  607. {
  608. Assert (fFirstURI);
  609. pwszPath = pwszDefaultPath;
  610. }
  611. else
  612. {
  613. // If we have a name (tag, uri), use it instead of the default name.
  614. //
  615. CStackBuffer<WCHAR,MAX_PATH> pwszNormalized;
  616. SCODE sc;
  617. UINT cch;
  618. // NOTE: Our psz is still quoted with <>. Unescaping must ignore these chars.
  619. //
  620. Assert (L'<' == *pwsz);
  621. // Get sufficient buffer for canonicalization
  622. //
  623. cch = static_cast<UINT>(wcslen(pwsz + 1));
  624. if (NULL == pwszNormalized.resize(CbSizeWsz(cch)))
  625. {
  626. FsLockTrace ("HrCheckIfHeader() - Error while allocating memory 0x%08lX\n", E_OUTOFMEMORY);
  627. return E_OUTOFMEMORY;
  628. }
  629. // Canonicalize the URL taking into account that it may be fully qualified.
  630. // Does not mater what value we pass in cch - it is out parameter only.
  631. //
  632. sc = ScCanonicalizePrefixedURL (pwsz + 1,
  633. pwszNormalized.get(),
  634. &cch);
  635. if (S_OK != sc)
  636. {
  637. // We gave sufficient space
  638. //
  639. Assert(S_FALSE != sc);
  640. FsLockTrace ("HrCheckIfHeader() - ScCanonicalizePrefixedURL() failed 0x%08lX\n", sc);
  641. return sc;
  642. }
  643. // We're in a loop, so try to use a static buffer first when
  644. // converting this storage path.
  645. //
  646. cch = pwszTranslated.celems();
  647. sc = m_pmu->ScStoragePathFromUrl (pwszNormalized.get(),
  648. pwszTranslated.get(),
  649. &cch);
  650. if (S_FALSE == sc)
  651. {
  652. if (NULL == pwszTranslated.resize(cch))
  653. return E_OUTOFMEMORY;
  654. sc = m_pmu->ScStoragePathFromUrl (pwszNormalized.get(),
  655. pwszTranslated.get(),
  656. &cch);
  657. }
  658. if (FAILED (sc))
  659. {
  660. FsLockTrace ("HrCheckIfHeader -- failed to translate a URI to a path.\n");
  661. return sc;
  662. }
  663. Assert ((S_OK == sc) || (W_DAV_SPANS_VIRTUAL_ROOTS == sc));
  664. // Sniff the last character and remove any final quoting '>' here.
  665. //
  666. cch = static_cast<UINT>(wcslen(pwszTranslated.get()));
  667. if (L'>' == pwszTranslated[cch - 1])
  668. pwszTranslated[cch - 1] = L'\0';
  669. // Hold onto the path.
  670. //
  671. pwszPath = pwszTranslated.get();
  672. }
  673. Assert (pwszPath);
  674. // This is no longer our first time through the URI loop. Clear our flag.
  675. //
  676. fFirstURI = FALSE;
  677. // Loop through all tokens, checking as we go.
  678. //$REVIEW: Right now, PszNextToken can't give different returns
  679. //$REVIEW: for "not found" versus "syntax error".
  680. //$REVIEW: That means we'll can't really give different, distinct
  681. //$REVEIW: codes for syntax problems -- any failure is mapped to 412 Precond Failed.
  682. //
  683. for (pwszToken = iter.PszNextToken (TOKEN_START_LIST) ;
  684. pwszToken;
  685. pwszToken = iter.PszNextToken (tokenNext) )
  686. {
  687. Assert (pwszToken);
  688. // Check this one token for validity.
  689. //$LATER: These checks could be folded into the HrValidTokenExpression
  690. //$LATER: call. This will be important later, when we have
  691. //$LATER: more different token types to work with.
  692. //
  693. if (L'<' == *pwszToken)
  694. {
  695. hr = HrValidTokenExpression (m_pmu, pwszToken, pwszPath, NULL);
  696. }
  697. else if (L'[' == *pwszToken)
  698. {
  699. FILETIME ft;
  700. hr = S_OK;
  701. // Manually fetch the Etag for this item, and compare it
  702. // against the provided Etag. Set the error code the
  703. // same way that HrValidTokenExpression does:
  704. // If the Etag does NOT match, set the error code to
  705. // E_DAV_INVALID_HEADER.
  706. // Remember to skip the enclosing brackets ([]) when
  707. // comparing the Etag strings.
  708. //
  709. if (!FGetLastModTime (NULL, pwszPath, &ft))
  710. hr = E_DAV_INVALID_HEADER;
  711. else if (!FETagFromFiletime (&ft, rgwchEtag, m_pmu->GetEcb()))
  712. hr = E_DAV_INVALID_HEADER;
  713. else
  714. {
  715. // Skip the square bracket -- this level of quoting
  716. // is just for the if-header, not
  717. //
  718. pwszToken++;
  719. // Since we do not do week ETAG checking, if the
  720. // ETAG starts with "W/" skip those bits
  721. //
  722. if (L'W' == *pwszToken)
  723. {
  724. Assert (L'/' == *(pwszToken + 1));
  725. pwszToken += 2;
  726. }
  727. // Our current Etags must be quoted.
  728. //
  729. Assert (L'\"' == pwszToken[0]);
  730. // Compare these etags, INcluding the double-quotes,
  731. // but EXcluding the square-brackets (those were added
  732. // just for the IF: header.
  733. //
  734. if (wcsncmp (rgwchEtag, pwszToken, wcslen(rgwchEtag)))
  735. hr = E_DAV_INVALID_HEADER;
  736. }
  737. }
  738. else
  739. hr = E_FAIL;
  740. if ((S_OK == hr && !iter.FCurrentNot()) ||
  741. (S_OK != hr && iter.FCurrentNot()))
  742. {
  743. // Either token matches, and this is NOT a "Not" expression,
  744. // OR the token does NOT match, and this IS a "Not" expression.
  745. // This one expression in the current list is true.
  746. // Rember this match, and check the next token in the same list.
  747. // If we don't find another token in the same list, we will
  748. // drop out of the for-each-token loop with fOneMatch TRUE,
  749. // and we will know that one whole list matched, so this URI
  750. // has a valid list.
  751. //
  752. fOneMatch = TRUE;
  753. tokenNext = TOKEN_SAME_LIST;
  754. continue;
  755. }
  756. else
  757. {
  758. // Either the token was not valid in a non-"Not" expression,
  759. // or the token was valid in a "Not" expression.
  760. // This one expression in this list is NOT true.
  761. // That makes this list NOT true -- skip the rest of this
  762. // list and move on to the next list for this URI.
  763. //
  764. fOneMatch = FALSE;
  765. tokenNext = TOKEN_NEW_LIST;
  766. continue;
  767. }
  768. } // rof - tokens in this list
  769. // Check if we parsed a whole list with matches.
  770. //
  771. if (fOneMatch)
  772. {
  773. // This whole list matched! Return OK.
  774. //
  775. hr = S_OK;
  776. }
  777. else
  778. {
  779. // This list did not match.
  780. //
  781. // NOTE: We are quitting here if any one URI is lacking
  782. // a matching list. We are treating the URI-sets as if they
  783. // are AND-ed together. This is not strictly DAV-compliant.
  784. // NOTE: See the comments at the top of this function about
  785. // true DAV-compliance and multi-URI Ifs.
  786. //
  787. hr = E_DAV_IF_HEADER_FAILURE;
  788. // We've failed. Quit now.
  789. //
  790. break;
  791. }
  792. } // rof - URIs in this header
  793. return hr;
  794. }
  795. HRESULT
  796. HrCheckStateHeaders (IMethUtil * pmu,
  797. LPCWSTR pwszPath,
  798. BOOL fGetMeth)
  799. {
  800. return HrCheckIfHeader(pmu, pwszPath);
  801. }
  802. // ------------------------------------------------------------------------
  803. // CParseLockTokenHeader::FOneToken
  804. // Special test -- F if not EXACTLY ONE item in the header.
  805. BOOL
  806. CParseLockTokenHeader::FOneToken()
  807. {
  808. LPCWSTR pwsz;
  809. LPCWSTR pwszToken;
  810. BOOL fOnlyOne = FALSE;
  811. // Quick check -- if the header doesn't exist, just process the method.
  812. //
  813. pwsz = m_pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  814. if (!pwsz)
  815. return FALSE;
  816. IFITER iter(pwsz);
  817. // If we have LESS than one token, return FALSE.
  818. pwszToken = iter.PszNextToken(TOKEN_START_LIST);
  819. if (!pwszToken)
  820. goto ret;
  821. // If we have MORE than one token in this list, return FALSE.
  822. pwszToken = iter.PszNextToken(TOKEN_SAME_LIST);
  823. if (pwszToken)
  824. goto ret;
  825. // If we have other lists for this uri, return FALSE.
  826. pwszToken = iter.PszNextToken(TOKEN_NEW_LIST);
  827. if (pwszToken)
  828. goto ret;
  829. fOnlyOne = TRUE;
  830. ret:
  831. // We have exactly one token.
  832. return fOnlyOne;
  833. }
  834. // ------------------------------------------------------------------------
  835. // CParseLockTokenHeader::SetPaths
  836. // Feed the relevant paths to this lock token parser.
  837. HRESULT
  838. CParseLockTokenHeader::SetPaths (LPCWSTR pwszPath, LPCWSTR pwszDest)
  839. {
  840. HRESULT hr = S_OK;
  841. // They better be passing in at least one path.
  842. Assert(pwszPath);
  843. Assert(!m_fPathsSet);
  844. // Copy the provided paths locally.
  845. //
  846. m_pwszPath = WszDupWsz (pwszPath);
  847. m_cwchPath = static_cast<UINT>(wcslen (m_pwszPath.get()));
  848. if (pwszDest)
  849. {
  850. m_pwszDest = WszDupWsz (pwszDest);
  851. m_cwchDest = static_cast<UINT>(wcslen (m_pwszDest.get()));
  852. }
  853. m_fPathsSet = TRUE;
  854. return hr;
  855. }
  856. // ------------------------------------------------------------------------
  857. // CParseLockTokenHeader::HrGetLockIdForPath
  858. // Get the token string for a path WITH a certain kind of access.
  859. //$LATER: Obey fPathLookup (should be true on depth-type ops, when we add dir-locks)
  860. //$LATER: Do back-path-lookup to find the dir-lock that is locking us.
  861. HRESULT
  862. CParseLockTokenHeader::HrGetLockIdForPath (LPCWSTR pwszPath,
  863. DWORD dwAccess,
  864. LARGE_INTEGER * pliLockID,
  865. BOOL fPathLookup) // defaulted to FALSE
  866. {
  867. HRESULT hr = E_DAV_LOCK_NOT_FOUND;
  868. FETCH_TOKEN_TYPE tokenNext = TOKEN_SAME_LIST;
  869. LPCWSTR pwsz;
  870. LPCWSTR pwszToken;
  871. // Assert that we're in the correct state to call this method.
  872. //
  873. Assert(m_fPathsSet);
  874. // Init our out parameter.
  875. //
  876. Assert(pliLockID);
  877. (*pliLockID).QuadPart = 0;
  878. // The requested path must be a child of one of our set paths.
  879. //
  880. Assert (!_wcsnicmp (pwszPath, m_pwszPath.get(), m_cwchPath) ||
  881. (m_pwszDest.get() &&
  882. !_wcsnicmp (pwszPath, m_pwszDest.get(), m_cwchDest)));
  883. // Quick check -- if the header doesn't exist, just process the method.
  884. //
  885. pwsz = m_pmu->LpwszGetRequestHeader (gc_szLockToken, TRUE);
  886. if (!pwsz)
  887. return hr;
  888. IFITER iter(pwsz);
  889. // If this is a tagged production, there will be a URI here
  890. // (pszToken will be non-NULL). In that case, search for
  891. // the URI that matches (translates to match) our pwszPath.
  892. // If there is NO URI here, we're a non-tagged production, and
  893. // all lists & tokens are applied to the root URI of the request.
  894. //
  895. pwszToken = iter.PszNextToken (TOKEN_URI);
  896. if (pwszToken)
  897. {
  898. // Loop through the tokens, looking only at uris.
  899. // When we find the one that matches our given path, break out.
  900. // Then the iter will hold our place, and the next set of code
  901. // will search through the lists for this uri....
  902. //
  903. for (; // already fetched first URI token above
  904. pwszToken;
  905. pwszToken = iter.PszNextToken (TOKEN_NEW_URI) )
  906. {
  907. CStackBuffer<WCHAR,MAX_PATH> pwszNormalized;
  908. CStackBuffer<WCHAR,MAX_PATH> pwszTranslated;
  909. SCODE sc;
  910. UINT cch;
  911. Assert (pwszToken);
  912. // NOTE: Our psz is still quoted with <>. Unescaping must ignore these chars.
  913. //
  914. Assert (L'<' == *pwszToken);
  915. // Get sufficient buffer for canonicalization
  916. //
  917. cch = static_cast<UINT>(wcslen(pwszToken + 1));
  918. if (NULL == pwszNormalized.resize(CbSizeWsz(cch)))
  919. {
  920. FsLockTrace ("CParseLockTokenHeader::HrGetLockIdForPath() - Error while allocating memory 0x%08lX\n", E_OUTOFMEMORY);
  921. return E_OUTOFMEMORY;
  922. }
  923. // Canonicalize the URL taking into account that it may be fully qualified.
  924. // Does not mater what value we pass in cch - it is out parameter only.
  925. //
  926. sc = ScCanonicalizePrefixedURL (pwszToken + 1,
  927. pwszNormalized.get(),
  928. &cch);
  929. if (S_OK != sc)
  930. {
  931. // We gave sufficient space
  932. //
  933. Assert(S_FALSE != sc);
  934. FsLockTrace ("HrCheckIfHeader() - ScCanonicalizePrefixedURL() failed 0x%08lX\n", sc);
  935. return sc;
  936. }
  937. // We're in a loop, so try to use a static buffer first when
  938. // converting this storage path.
  939. //
  940. cch = pwszTranslated.celems();
  941. sc = m_pmu->ScStoragePathFromUrl (pwszNormalized.get(),
  942. pwszTranslated.get(),
  943. &cch);
  944. if (S_FALSE == sc)
  945. {
  946. if (NULL == pwszTranslated.resize(cch))
  947. {
  948. return E_OUTOFMEMORY;
  949. }
  950. sc = m_pmu->ScStoragePathFromUrl (pwszNormalized.get(),
  951. pwszTranslated.get(),
  952. &cch);
  953. }
  954. if (FAILED (sc))
  955. {
  956. FsLockTrace ("HrCheckIfHeader -- failed to translate a URI to a path.\n");
  957. return sc;
  958. }
  959. Assert ((S_OK == sc) || (W_DAV_SPANS_VIRTUAL_ROOTS == sc));
  960. // Remove any final quoting '>' here.
  961. //
  962. cch = static_cast<UINT>(wcslen (pwszTranslated.get()));
  963. if (L'>' == pwszTranslated[cch - 1])
  964. pwszTranslated[cch - 1] = L'\0';
  965. if (!_wcsicmp (pwszPath, pwszTranslated.get()))
  966. break;
  967. }
  968. // If we fall out of the loop with NO pszToken, then we didn't
  969. // find ANY matching paths.... return an error.
  970. //
  971. if (!pwszToken)
  972. {
  973. hr = E_DAV_LOCK_NOT_FOUND;
  974. goto ret;
  975. }
  976. }
  977. else if (_wcsicmp (pwszPath, m_pwszPath.get()))
  978. {
  979. // There is NO URI st the start, so we're a non-tagged production,
  980. // BUT the caller was looking for some path BESIDES the root URI's path
  981. // (didn't match m_pwszPath in the above test!!!).
  982. // FAIL and tell them that we can't find any locktokens for this path.
  983. //
  984. hr = E_DAV_LOCK_NOT_FOUND;
  985. goto ret;
  986. }
  987. // Now, the IFITER should be positioned at the start of the list
  988. // that applies to this path.
  989. // Look for a token under this tag that matches.
  990. //
  991. // Loop through all tokens, checking as we go.
  992. //$REVIEW: Right now, PszNextToken can't give different returns
  993. //$REVIEW: for "not found" versus "syntax error".
  994. //$REVIEW: That means we'll never give "bad request" for syntax problems....
  995. //
  996. for (pwszToken = iter.PszNextToken (TOKEN_START_LIST);
  997. pwszToken;
  998. pwszToken = iter.PszNextToken (tokenNext) )
  999. {
  1000. LARGE_INTEGER liLockID;
  1001. Assert (pwszToken);
  1002. // Check this one token for validity.
  1003. //
  1004. if (L'<' == *pwszToken)
  1005. {
  1006. hr = HrValidTokenExpression (m_pmu,
  1007. pwszToken,
  1008. pwszPath,
  1009. &liLockID);
  1010. }
  1011. else
  1012. {
  1013. // This is not a locktoken -- ignore it for now.
  1014. //
  1015. // This list still could have our locktoken -- keep looking in
  1016. // this same list.
  1017. //
  1018. // NTRaid#244243 -- However, this list might NOT have our locktoken.
  1019. // Need to look at any list for this uri.
  1020. //
  1021. tokenNext = TOKEN_ANY_LIST;
  1022. continue;
  1023. }
  1024. // We only want this lock token if it IS valid, AND
  1025. // it's not from a "Not" expression, AND it comes from a
  1026. // valid list. So, if we hit an invalid token, QUIT searching
  1027. // this list. (Skip ahead to the next list.)
  1028. //
  1029. if (S_OK == hr && !iter.FCurrentNot())
  1030. {
  1031. // The token matches, AND it's not from a "Not" expression.
  1032. // This one's good. Send it back.
  1033. //
  1034. *pliLockID = liLockID;
  1035. hr = S_OK;
  1036. goto ret;
  1037. }
  1038. else if (S_OK != hr && iter.FCurrentNot())
  1039. {
  1040. // The token does NOT match, and this IS a "Not" expression.
  1041. // This list still could be true overall -- keep looking in
  1042. // this same list.
  1043. //
  1044. // NTRaid#244243 -- However, this list might NOT have our locktoken.
  1045. // Need to look at any list for this uri.
  1046. //
  1047. tokenNext = TOKEN_ANY_LIST;
  1048. continue;
  1049. }
  1050. else
  1051. {
  1052. // Either the token was not valid in a non-"Not" expression,
  1053. // or the token was valid in a "Not" expression.
  1054. // This expression in this list is NOT true.
  1055. // Since this is not a "good" list, don't look here
  1056. // for a matching token -- skip to the next list.
  1057. //
  1058. tokenNext = TOKEN_NEW_LIST;
  1059. continue;
  1060. }
  1061. }
  1062. // We didn't find a token for this item.
  1063. //
  1064. hr = E_DAV_LOCK_NOT_FOUND;
  1065. ret:
  1066. return hr;
  1067. }