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.

1451 lines
36 KiB

  1. /*
  2. * S T A T E T O K. C P P
  3. *
  4. * Sources implementation of DAV-Lock common definitions.
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_locks.h"
  9. // This is the character that will be part of the opaquelocktoken
  10. // for transaction tokens.
  11. //
  12. DEC_CONST WCHAR gc_wszTransactionOpaquePathPrefix[] = L"XN";
  13. DEC_CONST UINT gc_cchTransactionOpaquePathPrefix = CchConstString(gc_wszTransactionOpaquePathPrefix);
  14. /*
  15. * This file contains the definitions used for parsing state token
  16. * relared headers.
  17. *
  18. *
  19. * If = "If" ":" ( 1*No-tag-list | 1*Tagged-list)
  20. * No-tag-list = List
  21. * Tagged-list = Resource 1*List
  22. * Resource = Coded-url
  23. * List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")"
  24. * State-token = Coded-url
  25. * Coded-url = "<" URI ">"
  26. *
  27. *$BIG NOTE
  28. * If headers are used for two things - once to check preconditions
  29. * of the operation and once to find out the lock contexts to be
  30. * used for the operation. The second part differs in store and fs
  31. * implementations - since in fs we look for the lock only if the
  32. * operation fails because of lock conflict. In our store implementation we have
  33. * lock contexts added to the login before we start the operation.
  34. * But if-header asks us to use only certain tokens with certain resources.
  35. * We fall short here in the store implementation.
  36. *
  37. * Precondition checking should behave exactly the same in the two impls.
  38. *
  39. * Notes on the match operator:
  40. * We use the caller defined match operator to determine whether the
  41. * resource (resource path) statisfies the condition. For non-tagged
  42. * production this condition is applied for (each of) the original
  43. * operand resources for the verb. We pass the recursive flag to
  44. * check for all sub-resources or not. In the case of tagged production,
  45. * it is little more complex - first for each tagged path the parser
  46. * determines whether it comes under the scope of the operation or
  47. * not. If it does come under the scope we call the operator to apply
  48. * the condition check. Here we do not want the match to be applied to
  49. * the child resources and the recursive flag is set to FALSE;
  50. *
  51. */
  52. /*
  53. - CIfHeadParser
  54. -
  55. * This is used for syntax parse of the If: header as opposed to the
  56. * tokenization done by the IFITER.
  57. *
  58. *
  59. */
  60. class CIfHeadParser
  61. {
  62. private:
  63. // The header string
  64. //
  65. LPCWSTR m_pwszHeader;
  66. // BOOL flag indicating if it is a tagged production or not
  67. //
  68. BOOL m_fTagged;
  69. // Bool flag to indicate child resource processing.
  70. // The flag is set differently for tag and non-tag
  71. // productions. However the meaning of the flag is
  72. // consistent - it is used to tell the matchop if
  73. // we want it to look the children of the given
  74. // resource.
  75. //
  76. BOOL m_fRecursive;
  77. SCODE ScValidateTagged(LPCWSTR pwszPath);
  78. SCODE ScValidateNonTagged(LPCWSTR rgpwszPaths[], DWORD cPaths, SCODE * pSC);
  79. // Takes an array of pointers to paths and an array of bool-flags.
  80. // Requires the size of the arrays (should be same).
  81. //
  82. SCODE ScValidateList(IN LPCWSTR *ppwszPathList, IN DWORD crPaths, OUT BOOL *pfMatch);
  83. SCODE ScMatch(LPCWSTR pwszPath);
  84. // Very private member shared by our methods
  85. // to keep track of current parse head.
  86. //
  87. LPCWSTR m_pwszParseHead;
  88. // String parser
  89. //
  90. IFITER m_iter;
  91. // Match operator given to us.
  92. //
  93. CStateMatchOp *m_popMatch;
  94. // NOT IMPLEMENTED
  95. //
  96. CIfHeadParser( const CIfHeadParser& );
  97. CIfHeadParser& operator=( const CIfHeadParser& );
  98. public:
  99. // Useful consts
  100. //
  101. enum
  102. {
  103. TAG_HEAD = L'<',
  104. TAG_TAIL = L'>',
  105. ETAG_HEAD = L'[',
  106. ETAG_TAIL = L']',
  107. LIST_HEAD = L'(',
  108. LIST_TAIL = L')'
  109. };
  110. CIfHeadParser (LPCWSTR pwszHeader, CStateMatchOp *popMatch) :
  111. m_pwszHeader(pwszHeader),
  112. m_iter(pwszHeader),
  113. m_popMatch(popMatch)
  114. {
  115. Assert(pwszHeader);
  116. m_pwszParseHead = const_cast<LPWSTR>(pwszHeader);
  117. while (*m_pwszParseHead && iswspace(*m_pwszParseHead))
  118. m_pwszParseHead++;
  119. // Checks if the header is a tagged or non-tagged production.
  120. // If we find a "Coded-URI" (a URIs inside angle brackets, <uri>)
  121. // before the first list (before the first "(" char)
  122. // then we have a tagged production.
  123. //
  124. m_fTagged = (TAG_HEAD == *m_pwszParseHead);
  125. }
  126. ~CIfHeadParser()
  127. {
  128. }
  129. // Apply the if header production to the paths.
  130. // Path2 is optional. fRecursive says if the validation
  131. // is to be done to all children of the given path(s).
  132. // We may need to change the interface to support a list
  133. // of paths so that we can use it in Batch methods as well.
  134. //
  135. SCODE ScValidateIf(LPCWSTR rgpwszPaths[], DWORD cPaths, BOOL fRecursive = FALSE, SCODE * pSC = NULL);
  136. };
  137. // --------------------------------------------------------------------------------
  138. // ----------------------------- Free Helper Functions ----------------------------
  139. // --------------------------------------------------------------------------------
  140. /*
  141. - PwszSkipCodes
  142. -
  143. *
  144. * skip white spaces and the delimiters in a tagged string part
  145. * of an if-header. We expect the codes to be <> or [].
  146. *
  147. * *pdwLen must be zero or actual length of the input string.
  148. * when the call returns it will have the length of the token san
  149. * LWS and tags.
  150. *
  151. */
  152. LPCWSTR
  153. PwszSkipCodes(IN LPCWSTR pwszTagged, IN OUT DWORD *pcchLen)
  154. {
  155. LPCWSTR pwszTokHead = pwszTagged;
  156. DWORD cchTokLen;
  157. Assert(pcchLen);
  158. // find the actual length, if not specified
  159. //
  160. if (! *pcchLen)
  161. *pcchLen = static_cast<DWORD>(wcslen(pwszTokHead));
  162. cchTokLen = *pcchLen;
  163. // Calculate relevant token length skipping LWS in the
  164. // head and tail.
  165. //
  166. // Skip any LWS near the head
  167. //
  168. while((*pwszTokHead) && (iswspace(*pwszTokHead)) && (cchTokLen > 0))
  169. {
  170. cchTokLen--;
  171. pwszTokHead++;
  172. }
  173. // Skip any LWS near the tail
  174. //
  175. while((cchTokLen > 0) && iswspace(pwszTokHead[cchTokLen-1]))
  176. {
  177. cchTokLen--;
  178. }
  179. // At least two characters are expected now
  180. //
  181. if (cchTokLen < 2)
  182. {
  183. *pcchLen = 0;
  184. DebugTrace("PszSkipCodes: Invalid token.\n");
  185. return NULL;
  186. }
  187. // skip delimiters if they are present.
  188. //
  189. if (((*pwszTokHead == CIfHeadParser::TAG_HEAD) && (pwszTokHead[cchTokLen-1] == CIfHeadParser::TAG_TAIL)) ||
  190. ((*pwszTokHead == CIfHeadParser::ETAG_HEAD) && (pwszTokHead[cchTokLen-1] == CIfHeadParser::ETAG_TAIL)))
  191. {
  192. pwszTokHead++;
  193. cchTokLen -= 2;
  194. }
  195. // LWS are legal within the tags as well.
  196. // Skip any LWS near the head
  197. //
  198. while((*pwszTokHead) && (iswspace(*pwszTokHead)) && (cchTokLen > 0))
  199. {
  200. pwszTokHead++;
  201. cchTokLen--;
  202. }
  203. // Skip any LWS near the tail
  204. //
  205. while(iswspace(pwszTokHead[cchTokLen-1]) && (cchTokLen > 0))
  206. {
  207. cchTokLen--;
  208. }
  209. if (cchTokLen > 0)
  210. {
  211. *pcchLen = cchTokLen;
  212. return pwszTokHead;
  213. }
  214. else
  215. {
  216. *pcchLen = 0;
  217. DebugTrace("PszSkipCodes Invalid token length.\n");
  218. return NULL;
  219. }
  220. }
  221. // --------------------------------------------------------------------------------
  222. // ----------------------------- CIfHeadParser Impl -------------------------------
  223. // --------------------------------------------------------------------------------
  224. /*
  225. - CIfHeadParser::ScValidateTagged
  226. -
  227. *
  228. * Apply the tagged production.
  229. *
  230. *
  231. * Simply put we do this:
  232. *
  233. * for each list within the list of list
  234. * we apply the list production
  235. *
  236. * we expect the parse string to be 1 * List as the resource is
  237. * already consumed by the caller.
  238. *
  239. */
  240. //$REVIEW: How is this function any different from ScValidateNonTagged(pwsz, NULL)???
  241. SCODE
  242. CIfHeadParser::ScValidateTagged(LPCWSTR pwszPath)
  243. {
  244. SCODE sc = E_DAV_IF_HEADER_FAILURE;
  245. LPCWSTR rpwszPath[1];
  246. BOOL rfMatch[1];
  247. BOOL fMatchAny = FALSE;
  248. Assert(m_fTagged);
  249. rpwszPath[0] = pwszPath;
  250. rfMatch[0] = FALSE;
  251. // Apply one list which is
  252. // LIST_HEAD 1 * ( [ not ] (statetoken | e-tag ) ) LIST_TAIL
  253. //
  254. while ( SUCCEEDED(sc = ScValidateList(rpwszPath, 1, rfMatch)) )
  255. {
  256. if (TRUE == rfMatch[0])
  257. fMatchAny = TRUE;
  258. }
  259. // Status cannot be succesfull there as that is condition
  260. // to exit the loop above
  261. //
  262. Assert(S_OK != sc);
  263. // Now if the status is special failing error
  264. // and we found the match then we need to return S_OK.
  265. // Otherwise we will go down and return whatever
  266. // error we are given.
  267. //
  268. if ((E_DAV_IF_HEADER_FAILURE == sc) && fMatchAny)
  269. {
  270. sc = S_OK;
  271. }
  272. return sc;
  273. }
  274. /*
  275. - CIfHeadParser::ScValidateNonTagged
  276. -
  277. *
  278. * Apply the non-tagged if header production.
  279. *
  280. *
  281. * Simply put we do this:
  282. *
  283. * for each list in the header
  284. * we apply the list production
  285. *
  286. * we expect the parse string to be 1 * List as the resource is
  287. * already consumed by the caller.
  288. *
  289. * Unlike the tagged production, a non tagged production is
  290. * applied to all the resources in the scope of the operation.
  291. * This is really complex and we shifted the complexity to
  292. * the ApplyList function below which supports two resources.
  293. *
  294. * If we succesfully finish the list, both the resources must
  295. * have atleast one successful (TRUE) list production for the
  296. * whole operation to succeed.
  297. *
  298. * If pSC is NULL we'll return success or failure based on whether
  299. * or not the if header passes or fails.
  300. *
  301. * If pSC is not NULL, it points to an array of SCODEs that
  302. * indicate whether or not the if header passed for each resource
  303. * in the list. Note that in this case we will return S_OK
  304. * as the return value even if one of the resources fails. We'll
  305. * only send back a failure if there was some other unexpected
  306. * error
  307. *
  308. */
  309. SCODE
  310. CIfHeadParser::ScValidateNonTagged(LPCWSTR rgpwszPaths[], DWORD cPaths, SCODE * pSC)
  311. {
  312. CStackBuffer<BOOL> rgfMatches; // Flags indicating overall evaluation status for each path
  313. CStackBuffer<BOOL> rgfNextListMatch;// Flags used to return the results of validating next list
  314. SCODE sc = S_OK;
  315. DWORD iPath = 0;
  316. Assert(! m_fTagged);
  317. Assert(rgpwszPaths);
  318. Assert(cPaths);
  319. if ((NULL == rgfMatches.resize(sizeof(BOOL) * cPaths)) ||
  320. (NULL == rgfNextListMatch.resize(sizeof(BOOL) * cPaths)))
  321. {
  322. sc = E_OUTOFMEMORY;
  323. goto ret;
  324. }
  325. // Init the match flag list: to default FALSE
  326. //
  327. for ( iPath = 0; iPath < cPaths; iPath++ )
  328. {
  329. rgfMatches[iPath] = FALSE;
  330. if (pSC)
  331. {
  332. pSC[iPath] = E_DAV_IF_HEADER_FAILURE;
  333. }
  334. }
  335. // Apply one list which is
  336. // LIST_HEAD 1 * ( [ not ] (statetoken | e-tag ) ) LIST_TAIL
  337. //
  338. while ( SUCCEEDED(sc = ScValidateList(rgpwszPaths, cPaths, rgfNextListMatch.get())) )
  339. {
  340. // For all the paths that evaluated to TRUE in this list,
  341. // update the result flag.
  342. //
  343. for ( iPath = 0; iPath < cPaths; iPath++ )
  344. {
  345. // The result is interesting only for those whose current state is FALSE
  346. // because each of the lists of ids are OR'd together to decide whether
  347. // or not they passed.
  348. //
  349. if ( (FALSE == rgfMatches[iPath]) && (TRUE == rgfNextListMatch[iPath]) )
  350. {
  351. // Note: you may be thinking why I don't break
  352. // here. With depth locks, same list/lock can
  353. // satisfy multiple resources.
  354. //
  355. rgfMatches[iPath] = TRUE;
  356. if (pSC)
  357. {
  358. // If we are asked for a resource by resource record
  359. // of matching resource, mark that we found a success
  360. // for the current resource.
  361. //
  362. pSC[iPath] = S_OK;
  363. }
  364. }
  365. }
  366. //$NOTE
  367. // Two levels of optimization on this evaluation may look feasible:
  368. // 1) Stop evaluating when we find all the paths are validated
  369. // 2) Do not validate a path if the path is already validated against a list
  370. // Both these optimizations will work for pre-condition evaluation, however
  371. // we use the state tokens to add lock content to the logon: so we still need
  372. // to parse the entire list to obtain all the applicable lock tokens..
  373. // 3) Another possibility is to do only the lock-token matching in the above
  374. // scenario. There is no point in the etag/restag comparison if the path is
  375. // already validated: but lock token check is still required as we need to
  376. // collect all the lock tokens. This would require sharing the current global
  377. // results with the basic match function. I think I would do this some time later.
  378. //$NOTE
  379. //
  380. }
  381. // Check if that is any of special errors and reset the error code to S_OK
  382. // if that is the case. Otherwise fail out straight of.
  383. //
  384. if ((S_OK != sc) && (E_DAV_IF_HEADER_FAILURE != sc))
  385. {
  386. goto ret;
  387. }
  388. else
  389. {
  390. sc = S_OK;
  391. }
  392. // if we were asked for a resource by resource account of matching resources
  393. // we have succeeded the request. Otherwise, if any resource failed, the
  394. // if header failed.
  395. //
  396. if (pSC)
  397. {
  398. sc = S_OK;
  399. goto ret;
  400. }
  401. for ( iPath = 0; iPath < cPaths; iPath++ )
  402. {
  403. if (FALSE == rgfMatches[iPath])
  404. {
  405. sc = E_DAV_IF_HEADER_FAILURE;
  406. break;
  407. }
  408. }
  409. ret:
  410. return sc;
  411. }
  412. /*
  413. - CIfHeadParser::ScValidateList
  414. -
  415. *
  416. * Apply the list production on the resources.
  417. * For non tagged resources we need to apply the
  418. * list to all operand resources. We iterate the
  419. * header once and achieve this.
  420. *
  421. * Return: FALSE on malformed input otherwise TRUE.
  422. *
  423. * we parse the list and apply the match operation on
  424. * all the resources. In order for a TRUE match result all
  425. * the list elements must succesfully "apply" to the
  426. * resource. If atleast one element did not apply
  427. * with a truth result, we stop applying elements to
  428. * that resource.
  429. *
  430. * We return on end of list or malformed list.
  431. *
  432. */
  433. SCODE
  434. CIfHeadParser::ScValidateList(IN LPCWSTR *ppwszPathList, IN DWORD crPaths, OUT BOOL *pfMatch)
  435. {
  436. SCODE sc = S_OK;
  437. DWORD iIndex;
  438. // Do some input verification.
  439. // size of the list must be at least one.
  440. //
  441. Assert(crPaths>0);
  442. Assert(ppwszPathList[0]);
  443. Assert(pfMatch);
  444. #ifdef DBG
  445. {
  446. for (iIndex=0; iIndex<crPaths; iIndex++)
  447. {
  448. Assert(ppwszPathList[iIndex]);
  449. }
  450. }
  451. #endif
  452. // From now on, we are driven by the input.
  453. // Look for the token and decide what to do.
  454. //
  455. m_pwszParseHead = m_iter.PszNextToken(TOKEN_START_LIST);
  456. // Not a list: it is important that we fail
  457. // here specifically to handle syntaxt errors
  458. // in the list.
  459. //
  460. if (NULL == m_pwszParseHead)
  461. {
  462. sc = E_DAV_IF_HEADER_FAILURE;
  463. goto ret;
  464. }
  465. // Initialize the match flag list.
  466. // we start by assuming TRUE, since we
  467. // know that there is atleast one token in the list.
  468. //
  469. for (iIndex=0; iIndex<crPaths; iIndex++)
  470. {
  471. pfMatch[iIndex] = TRUE;
  472. }
  473. // Apply one match element at a time - which is
  474. // ( [ not ] ( statetoken | e-tag ) )
  475. //
  476. while (NULL != m_pwszParseHead)
  477. {
  478. BOOL fEtag = (ETAG_HEAD == *m_pwszParseHead);
  479. // set the current token of the operator
  480. //
  481. if (! m_popMatch->FSetToken(m_pwszParseHead, fEtag))
  482. {
  483. DebugTrace("CIfHeadParser::ScValidateList Invalid token\n");
  484. // return immediately
  485. //
  486. sc = E_DAV_IF_HEADER_FAILURE;
  487. goto ret;
  488. }
  489. // Now we obtained one complete match condition-
  490. // for all the paths check if the condition is good.
  491. // we need to do this only if all previous matchs
  492. // succeeded for the given path.
  493. // i.e if a match failed, the list is anyway going to fail
  494. // for the particular path.
  495. //
  496. for (iIndex=0; iIndex<crPaths; iIndex++)
  497. {
  498. if (TRUE == pfMatch[iIndex])
  499. {
  500. // Change the match flag only if the condition
  501. // failed. This is because the expression within a
  502. // list is ANDed together. If one fails, the whole
  503. // list fails.
  504. //
  505. sc = ScMatch(ppwszPathList[iIndex]);
  506. if (FAILED(sc))
  507. {
  508. if (E_DAV_IF_HEADER_FAILURE == sc)
  509. {
  510. pfMatch[iIndex] = FALSE;
  511. }
  512. else
  513. {
  514. goto ret;
  515. }
  516. }
  517. }
  518. }
  519. m_pwszParseHead = m_iter.PszNextToken(TOKEN_SAME_LIST);
  520. }
  521. // List is a syntactically correct one.
  522. //
  523. sc = S_OK;
  524. ret:
  525. return sc;
  526. }
  527. /*
  528. - CIfHeadParser::ScValidateIf
  529. -
  530. *
  531. * Apply the if production.
  532. *
  533. * If pSC is NULL we'll return success or failure based on whether
  534. * or not the if header passes or fails.
  535. *
  536. * If pSC is not NULL, it points to an array of SCODEs that
  537. * indicate whether or not the if header passed for each resource
  538. * in the list. Note that in this case we will return S_OK
  539. * as the return value even if one of the resources fails. We'll
  540. * only send back a failure if there was some other unexpected
  541. * error
  542. */
  543. SCODE
  544. CIfHeadParser::ScValidateIf( LPCWSTR rgpwszPaths[],
  545. DWORD cPaths,
  546. BOOL fRecursive /* = FALSE */,
  547. SCODE * pSC /* = NULL */)
  548. {
  549. SCODE sc = S_OK;
  550. // If it is a tagged production, we do not
  551. // apply the match to the children - the
  552. // match op is for the tagged resource only. If non
  553. // tagged, the method is to be applied
  554. // depending on the method's depth flag.
  555. //
  556. if (m_fTagged)
  557. m_fRecursive = FALSE;
  558. else
  559. m_fRecursive = fRecursive;
  560. // if tagged
  561. // while ok
  562. // find the tagged uri
  563. // see if the uri is within scope of the operands
  564. // if within scope apply tagged production
  565. // if non tagged
  566. // apply nontagged production on the two input uris
  567. // we are done
  568. //
  569. if (m_fTagged)
  570. {
  571. BOOL fDone = FALSE;
  572. DWORD iPath = 0;
  573. // Initialize the results array if required ...
  574. //
  575. if (pSC)
  576. {
  577. for (iPath = 0; iPath < cPaths; iPath++)
  578. pSC[iPath] = S_OK;
  579. }
  580. while(! fDone)
  581. {
  582. LPCWSTR pwszUri;
  583. LPCWSTR pwszPath;
  584. DWORD dwLen;
  585. // find the URI in the header
  586. //
  587. m_pwszParseHead = m_iter.PszNextToken(TOKEN_NEW_URI);
  588. if (NULL == m_pwszParseHead)
  589. {
  590. sc = S_OK;
  591. goto ret;
  592. }
  593. // got the tagged uri - skip the tags in both
  594. // sides and get a clean uri.
  595. //
  596. dwLen = 0;
  597. pwszUri = PwszSkipCodes(m_pwszParseHead, &dwLen);
  598. if ( (pwszUri == NULL) || (dwLen<1) )
  599. {
  600. sc = E_DAV_IF_HEADER_FAILURE;
  601. goto ret;
  602. }
  603. // convert the uri to the resource path
  604. //
  605. sc = m_popMatch->ScGetResourcePath(pwszUri, &pwszPath);
  606. if (FAILED(sc))
  607. {
  608. // error code will be E_OUTOFMEMORY
  609. // if we get here
  610. //
  611. goto ret;
  612. }
  613. // Check if the tagged URI is within scope of
  614. // the method and apply the state match operation
  615. // only if it does.
  616. //
  617. for (iPath = 0; iPath < cPaths; iPath++)
  618. {
  619. // check path validity - depending on the operation depth.
  620. // If the operation is not deep, the paths must match
  621. // exactly.
  622. //
  623. if (FIsChildPath(rgpwszPaths[iPath], pwszPath, fRecursive))
  624. {
  625. sc = ScValidateTagged(pwszPath);
  626. // If the caller wants a list of the scodes resource
  627. // by resource, set it into the array. Otherwise
  628. // we can stop verifying the resource because we've
  629. // already found a resource that fails the if statement.
  630. // Note that we return the failure in the scode array
  631. // only for pre-condition failures, other errors like
  632. // memory errors (or even redirect errors) fail the
  633. // whole request immediately.
  634. //
  635. if ((E_DAV_IF_HEADER_FAILURE == sc) && (pSC))
  636. pSC[iPath] = sc;
  637. else if (FAILED(sc))
  638. goto ret;
  639. // This path is done
  640. //
  641. break;
  642. }
  643. }
  644. }
  645. }
  646. else
  647. {
  648. sc = ScValidateNonTagged(rgpwszPaths, cPaths, pSC);
  649. goto ret;
  650. }
  651. sc = E_DAV_IF_HEADER_FAILURE;
  652. ret:
  653. return sc;
  654. }
  655. /*
  656. - CIfHeadParser::ScMatch
  657. -
  658. * Call the appropriate operator and return the value of the
  659. * expression.
  660. *
  661. *
  662. */
  663. SCODE
  664. CIfHeadParser::ScMatch(LPCWSTR pwszPath)
  665. {
  666. SCODE sc = S_OK;
  667. BOOL fNot = m_iter.FCurrentNot();
  668. Assert(m_popMatch);
  669. // determine the type of the token and call the
  670. // appropriate handler
  671. //
  672. switch(m_popMatch->GetTokenType())
  673. {
  674. case CStateToken::TOKEN_LOCK:
  675. sc = m_popMatch->ScMatchLockToken(pwszPath, m_fRecursive);
  676. break;
  677. case CStateToken::TOKEN_RESTAG:
  678. sc = m_popMatch->ScMatchResTag(pwszPath);
  679. break;
  680. case CStateToken::TOKEN_ETAG:
  681. sc = m_popMatch->ScMatchETag(pwszPath, m_fRecursive);
  682. break;
  683. case CStateToken::TOKEN_TRANS:
  684. sc = m_popMatch->ScMatchTransactionToken(pwszPath);
  685. break;
  686. default:
  687. DebugTrace("CStateMatchOp::Unsupported token type\n");
  688. sc = E_DAV_IF_HEADER_FAILURE;
  689. goto ret;
  690. }
  691. // Unless we applied the match operators above we
  692. // should not even reach here.
  693. //
  694. if (fNot)
  695. {
  696. if (E_DAV_IF_HEADER_FAILURE == sc)
  697. {
  698. sc = S_OK;
  699. }
  700. else if (S_OK == sc)
  701. {
  702. sc = E_DAV_IF_HEADER_FAILURE;
  703. }
  704. }
  705. ret:
  706. return sc;
  707. }
  708. // --------------------------------------------------------------------------------
  709. // ----------------------------- CStateMatchOp Impl -------------------------------
  710. // --------------------------------------------------------------------------------
  711. /*
  712. - CStateMatchOp::ScParseIf
  713. -
  714. *
  715. *
  716. */
  717. SCODE
  718. CStateMatchOp::ScParseIf(LPCWSTR pwszIfHeader,
  719. LPCWSTR rgpwszPaths[],
  720. DWORD cPaths,
  721. BOOL fRecur,
  722. SCODE * pSC)
  723. {
  724. SCODE sc = S_OK;
  725. CIfHeadParser ifParser(pwszIfHeader, this);
  726. sc = ifParser.ScValidateIf(rgpwszPaths, cPaths, fRecur, pSC);
  727. return sc;
  728. }
  729. // --------------------------------------------------------------------------------
  730. // ----------------------------- CStateToken Impl -------------------------------
  731. // --------------------------------------------------------------------------------
  732. /*
  733. - CStateToken::FSetToken
  734. -
  735. * We expect pszToken to be an e-tag enclosed within [ ] or
  736. * a state token enclosed within < >.
  737. *
  738. */
  739. BOOL
  740. CStateToken::FSetToken(LPCWSTR pwszToken, BOOL fEtag, DWORD dwLen)
  741. {
  742. LPCWSTR pwszTokHead = pwszToken;
  743. m_tType = TOKEN_NONE;
  744. // update the length and skip the tags
  745. //
  746. pwszTokHead = PwszSkipCodes(pwszToken, &dwLen);
  747. if ( (NULL == pwszTokHead) || (dwLen < 1) )
  748. {
  749. return FALSE;
  750. }
  751. // add one for the null char.
  752. //
  753. dwLen++;
  754. // allocate buffer for the token.
  755. // we try to optimize allocations by using a heuristic
  756. // size value. Most of our tokens are of form
  757. // prefix-guid-smallstring
  758. //
  759. if ((NULL == m_pwszToken) || (dwLen > m_cchBuf))
  760. {
  761. if (NULL != m_pwszToken)
  762. ExFree(m_pwszToken);
  763. if (dwLen > NORMAL_STATE_TOKEN_SIZE)
  764. {
  765. m_pwszToken = reinterpret_cast<LPWSTR>(ExAlloc(dwLen * sizeof(WCHAR)));
  766. m_cchBuf = dwLen;
  767. }
  768. else
  769. {
  770. m_pwszToken = reinterpret_cast<LPWSTR>(ExAlloc(NORMAL_STATE_TOKEN_SIZE * sizeof(WCHAR)));
  771. m_cchBuf = NORMAL_STATE_TOKEN_SIZE;
  772. }
  773. }
  774. if (NULL == m_pwszToken)
  775. {
  776. m_cchBuf = 0;
  777. return FALSE;
  778. }
  779. // Remember that dwLen contains size of the buffer (including
  780. // Null char).
  781. // Make our copy of the string
  782. //
  783. wcsncpy(m_pwszToken, pwszTokHead, (dwLen - 1));
  784. // add the null character to terminate the string.
  785. //
  786. m_pwszToken[dwLen-1] = L'\0';
  787. if (fEtag)
  788. {
  789. Assert(CIfHeadParser::ETAG_HEAD == *pwszToken);
  790. m_tType = CStateToken::TOKEN_ETAG;
  791. return TRUE;
  792. }
  793. // parse the token to find our the token type.
  794. //
  795. else if (0 == _wcsnicmp(pwszTokHead,
  796. gc_wszOpaquelocktokenPrefix,
  797. gc_cchOpaquelocktokenPrefix) )
  798. {
  799. // Since token is a client input, let us be careful
  800. // with it. Make sure that the size is minimum expected,
  801. // which is opaquelocktoken:guid:<at least one char extension>.
  802. // Lock tokens, unfortunately, can be either transaction
  803. // or plain lock tokens. To find out if it is transaction
  804. // token, we will have to parse the token and reach the
  805. // extension part. For performance reasons I am going to
  806. // skip parsing and jump directly to the place where I
  807. // can get the information. This is not bad as we any way
  808. // correctly parse the token when we are looking for its
  809. // contents.
  810. //
  811. // gc_cchOpaquelocktokenPrefix includes the :, gc_cchMaxGuid
  812. // includes the null char (cch?). Hence the expression below.
  813. //
  814. if ( dwLen > (gc_cchOpaquelocktokenPrefix + gc_cchMaxGuid) )
  815. {
  816. if (0 == _wcsnicmp(&pwszTokHead[gc_cchOpaquelocktokenPrefix + gc_cchMaxGuid],
  817. gc_wszTransactionOpaquePathPrefix,
  818. gc_cchTransactionOpaquePathPrefix) )
  819. {
  820. m_tType = TOKEN_TRANS;
  821. return TRUE;
  822. }
  823. else
  824. {
  825. m_tType = TOKEN_LOCK;
  826. return TRUE;
  827. }
  828. }
  829. else
  830. {
  831. DebugTrace("CStateMatchOp::lock state token too small %ls\n", pwszTokHead);
  832. return FALSE;
  833. }
  834. }
  835. // Our restag-type URIs all start with 'r'.
  836. // (And no other URIs that start with 'r' are valid statetokens.)
  837. //
  838. else if (L'r' == *pwszTokHead)
  839. {
  840. m_tType = TOKEN_RESTAG;
  841. return TRUE;
  842. }
  843. else
  844. {
  845. DebugTrace("CStateMatchOp::Unsupported/unrecognized state token %ls\n",
  846. pwszTokHead);
  847. return FALSE;
  848. }
  849. }
  850. /*
  851. - CStateToken::FGetLockTokenInfo
  852. -
  853. * Parse the state token as if it is a lock token. Note that this
  854. * works for transaction tokens as well.
  855. *
  856. */
  857. BOOL
  858. CStateToken::FGetLockTokenInfo(unsigned __int64 *pi64SeqNum, LPWSTR pwszGuid)
  859. {
  860. LPWSTR pwszToken = m_pwszToken;
  861. Assert(pwszGuid);
  862. if ((TOKEN_LOCK != m_tType) && (TOKEN_TRANS != m_tType))
  863. {
  864. return FALSE;
  865. }
  866. // We assume that the token is validated when we reach here.
  867. // skip any LWS and the opaquetoken part.
  868. //
  869. while((*pwszToken) && iswspace(*pwszToken))
  870. pwszToken++;
  871. // we check for opaque token when we set the token - so
  872. // just skip the portion
  873. //
  874. pwszToken += gc_cchOpaquelocktokenPrefix;
  875. // no check for validity of buf size. duh factor.
  876. //
  877. wcsncpy(pwszGuid, pwszToken, gc_cchMaxGuid - 1);
  878. // terminate the guid string.
  879. //
  880. pwszGuid[gc_cchMaxGuid - 1] = L'\0';
  881. pwszToken = wcschr(pwszToken, L':');
  882. if (NULL == pwszToken)
  883. {
  884. DebugTrace("CStateToken::FGetLockTokenInfo invalid lock token.\n");
  885. return FALSE;
  886. }
  887. Assert(L':' == *pwszToken);
  888. // skip the ":"
  889. //
  890. pwszToken++;
  891. // Transaction tokens will have a T at the head of the extension
  892. // part of the token.
  893. //
  894. if (TOKEN_TRANS == m_tType)
  895. {
  896. Assert(gc_wszTransactionOpaquePathPrefix[0] == *pwszToken);
  897. pwszToken += gc_cchTransactionOpaquePathPrefix;
  898. }
  899. // the lock-id string follows
  900. //
  901. *pi64SeqNum = _wtoi64(pwszToken);
  902. //$TODO:
  903. // Is there a way to validate if atoi failed?
  904. //
  905. return TRUE;
  906. }
  907. /*
  908. - CStateToken::FIsEqual
  909. -
  910. * Nifty equality operator
  911. *
  912. *
  913. */
  914. BOOL
  915. CStateToken::FIsEqual(CStateToken *pstokRhs)
  916. {
  917. if (pstokRhs->GetTokenType() != m_tType)
  918. return FALSE;
  919. LPCWSTR pwszLhs = m_pwszToken;
  920. LPCWSTR pwszRhs = pstokRhs->WszGetToken();
  921. if (!pwszLhs || !pwszRhs)
  922. return FALSE;
  923. return (0 == _wcsicmp(pwszLhs, pwszRhs));
  924. }
  925. /*
  926. - IFITER::PszNextToken
  927. -
  928. *
  929. *
  930. */
  931. // ------------------------------------------------------------------------
  932. // IFITER::PszNextToken
  933. //
  934. // Fetch the next token.
  935. // Can be restricted to the next token in this list (AND-ed set inside a
  936. // particular set of parens), the next token in a new list (new set of parens),
  937. // or the next token in the whole header-line.
  938. //
  939. LPCWSTR
  940. IFITER::PszNextToken (FETCH_TOKEN_TYPE type)
  941. {
  942. LPCWSTR pwsz;
  943. LPCWSTR pwszEnd;
  944. WCHAR wchEnd = L'\0';
  945. // If no header existed, then there is nothing to do
  946. //
  947. if (NULL == m_pwch)
  948. return NULL;
  949. // Quick state-check.
  950. // If the current node is a "Not", then we MUST be in a list.
  951. // (Not is a qualifier on a token inside a list. Can't have a Not
  952. // outside of a list.)
  953. // Logically, m_fCurrentNot _implies_ m_state is STATE_LIST.
  954. //
  955. Assert (!m_fCurrentNot || STATE_LIST == m_state);
  956. // Clear our "Not" bit before starting our fetch of the next token.
  957. // If the token we return has a "Not" qualifier, set the flag correctly below.
  958. //
  959. m_fCurrentNot = FALSE;
  960. // Eat all the white space
  961. //
  962. while (*m_pwch && iswspace(*m_pwch))
  963. m_pwch++;
  964. // Quit if there is nothing left to process
  965. //
  966. if (L'\0' == *m_pwch)
  967. return NULL;
  968. // If the last state was a LIST, we need to check for the close
  969. // of the list, and set our state back to NONE.
  970. //
  971. if (STATE_LIST == m_state)
  972. {
  973. // If the next char is a close paren, that's the end of this list.
  974. if (L')' == *m_pwch)
  975. {
  976. m_pwch++;
  977. m_state = STATE_NONE;
  978. // Eat all the white space
  979. //
  980. while (*m_pwch && iswspace(*m_pwch))
  981. m_pwch++;
  982. // Quit if there is nothing left to process
  983. //
  984. if (L'\0' == *m_pwch)
  985. return NULL;
  986. // Update our state if we were asked for "any list item".
  987. // (Now we should find a list start.)
  988. //
  989. if (TOKEN_ANY_LIST == type)
  990. type = TOKEN_START_LIST;
  991. }
  992. }
  993. // If the caller asked for any list item, and we didn't change that
  994. // request because of our state above, change it here to specifically
  995. // search for the next item in the same list.
  996. //
  997. if (TOKEN_ANY_LIST == type)
  998. type = TOKEN_SAME_LIST;
  999. //
  1000. // Process the request.
  1001. //
  1002. switch (type)
  1003. {
  1004. // This case is really dumb. I thought I might use
  1005. // it for "counting" tokens. If it's not being used, remove it!
  1006. //
  1007. case TOKEN_NONE:
  1008. {
  1009. // If they're asking for a raw count (type == TOKEN_NONE),
  1010. // give it to 'em.....
  1011. // NOTE: This code is a little sloppy. It will count names
  1012. // as state tokens.
  1013. //
  1014. m_pwch = wcschr (m_pwch, L'<');
  1015. if (!m_pwch)
  1016. {
  1017. return NULL;
  1018. }
  1019. wchEnd = L'>';
  1020. // Go copy the data.
  1021. //
  1022. break;
  1023. }
  1024. case TOKEN_NEW_URI:
  1025. {
  1026. // Grab a name, skipping all lists.
  1027. // If there are no names left, give NULL.
  1028. // Three places we could be -- NONE, NAME, LIST.
  1029. //
  1030. while (m_pwch && *m_pwch)
  1031. {
  1032. // If we're at a uri-delimiter now, AND
  1033. // we're in the NONE state, just go fetch the token below...
  1034. //
  1035. if (L'<' == *m_pwch &&
  1036. STATE_NONE == m_state)
  1037. {
  1038. break;
  1039. }
  1040. #ifdef DBG
  1041. // Debug-only check of our state.
  1042. if (L'(' == *m_pwch)
  1043. {
  1044. Assert(STATE_NONE == m_state ||
  1045. STATE_NAME == m_state);
  1046. }
  1047. else if (L'<' == *m_pwch)
  1048. {
  1049. Assert(STATE_LIST == m_state);
  1050. }
  1051. #endif // DBG
  1052. // Zip to the end of the current list.
  1053. //
  1054. m_pwch = wcschr (m_pwch + 1, L')');
  1055. if (!m_pwch)
  1056. {
  1057. return NULL;
  1058. }
  1059. m_pwch++; // Skip past the closing paren.
  1060. // Eat all the white space
  1061. //
  1062. while (*m_pwch && iswspace(*m_pwch))
  1063. m_pwch++;
  1064. // Quit if there is nothing left to process
  1065. //
  1066. if (L'\0' == *m_pwch)
  1067. return NULL;
  1068. m_state = STATE_NONE;
  1069. }
  1070. // Fallthrough to the next segment to check the token
  1071. // and fetch our uri.
  1072. }
  1073. case TOKEN_URI:
  1074. {
  1075. // Grab a name, iff the next item is a name.
  1076. // Otherwise, give NULL.
  1077. // Quit if the next item is not a name.
  1078. //
  1079. if (L'<' != *m_pwch)
  1080. return NULL;
  1081. // Quit if we aren't in the correct state to look for a name.
  1082. // (This could happen if we already have a name, or if we are
  1083. // already INSIDE a list....)
  1084. //
  1085. if (STATE_NONE != m_state)
  1086. return NULL;
  1087. // Set our state and fallthru to fetch the data.
  1088. //
  1089. m_state = STATE_NAME;
  1090. wchEnd = L'>';
  1091. // Go copy the data.
  1092. //
  1093. break;
  1094. }
  1095. case TOKEN_NEW_LIST:
  1096. {
  1097. // Fast-forward to the next new list and fetch the first item.
  1098. // If we're still inside a list, must skip the rest of this list.
  1099. // If there are no more new lists for this URI, return NULL.
  1100. if (STATE_LIST == m_state)
  1101. {
  1102. // We're inside a list. Get out by seeking to the next
  1103. // list-end-char (right paren).
  1104. //
  1105. m_pwch = wcschr (m_pwch, L')');
  1106. if (!m_pwch)
  1107. return NULL;
  1108. m_state = STATE_NONE;
  1109. m_pwch++; // Skip past the closing paren.
  1110. // Eat all the white space
  1111. //
  1112. while (*m_pwch && iswspace(*m_pwch))
  1113. m_pwch++;
  1114. // Quit if there is nothing left to process
  1115. //
  1116. if (L'\0' == *m_pwch)
  1117. return NULL;
  1118. }
  1119. Assert(m_pwch);
  1120. Assert(*m_pwch);
  1121. // And fallthrough here to the TOKEN_START_LIST case.
  1122. // It will verify & skip past the list start char
  1123. // and fetch out the next token.
  1124. //
  1125. }
  1126. case TOKEN_START_LIST:
  1127. {
  1128. // Grab a list item, iff the next item is a NEW list item.
  1129. // Otherwise, return NULL.
  1130. // Quit if the next item is not a list.
  1131. //
  1132. if (L'(' != *m_pwch)
  1133. return NULL;
  1134. // Quit if we aren't in the correct state to look for a name.
  1135. // (This could happen if we are already INSIDE a list....)
  1136. //
  1137. if (STATE_LIST == m_state)
  1138. return NULL;
  1139. // Fetch the token.
  1140. //
  1141. m_state = STATE_LIST;
  1142. m_pwch++; // Skip the open paren.
  1143. // Eat all the white space
  1144. //
  1145. while (*m_pwch && iswspace(*m_pwch))
  1146. m_pwch++;
  1147. // Quit if there is nothing left to process
  1148. //
  1149. if (L'\0' == *m_pwch)
  1150. return NULL;
  1151. // Fallthrough to the TOKEN_SAME_LIST processing
  1152. // to actually fetch the token.
  1153. //
  1154. }
  1155. case TOKEN_SAME_LIST:
  1156. {
  1157. // Grab the next list item.
  1158. // If the next item is not a list item, return NULL.
  1159. // Quit if we aren't in the correct state to look for a name.
  1160. // (This could happen if we are NOT inside a list....)
  1161. //
  1162. if (STATE_LIST != m_state)
  1163. return NULL;
  1164. // Check for the magic "Not" qualifier.
  1165. //
  1166. if (!_wcsnicmp (gc_wszNot, m_pwch, 3))
  1167. {
  1168. // Remember the data and skip these chars.
  1169. //
  1170. m_fCurrentNot = TRUE;
  1171. m_pwch += 3;
  1172. // Eat all the white space
  1173. //
  1174. while (*m_pwch && iswspace(*m_pwch))
  1175. m_pwch++;
  1176. // Quit if there is nothing left to process
  1177. //
  1178. if (L'\0' == *m_pwch)
  1179. return NULL;
  1180. }
  1181. // Quit if the next item is not a token.
  1182. //
  1183. if (L'<' != *m_pwch &&
  1184. L'[' != *m_pwch)
  1185. {
  1186. return NULL;
  1187. }
  1188. // Fetch the token.
  1189. //
  1190. // Next token must start with either < for statetokens, or [ for etags.
  1191. //
  1192. if (L'<' == *m_pwch)
  1193. {
  1194. wchEnd = L'>';
  1195. }
  1196. else if (L'[' == *m_pwch)
  1197. {
  1198. wchEnd = L']';
  1199. }
  1200. else
  1201. {
  1202. DebugTrace("HrCheckIfHeaders -- Found list start, but no tokens!\n");
  1203. return NULL;
  1204. }
  1205. // Go copy the data.
  1206. //
  1207. break;
  1208. }
  1209. default:
  1210. {
  1211. DebugTrace("HrCheckIfHeaders -- Unrecognized request: 0x%0x", type);
  1212. return NULL;
  1213. }
  1214. }
  1215. // We should have set these items above. They are needed to
  1216. // snip off the current token string (below).
  1217. //
  1218. Assert (m_pwch);
  1219. Assert (*m_pwch);
  1220. Assert (wchEnd);
  1221. // Quick state-check.
  1222. // If the current node is a "Not", then we MUST be in a list.
  1223. // (Not is a qualifier on a token inside a list. Can't have a Not
  1224. // outside of a list.)
  1225. // Logically, m_fCurrentNot _implies_ m_state is STATE_LIST.
  1226. //
  1227. Assert (!m_fCurrentNot || STATE_LIST == m_state);
  1228. // Find the end of this data item.
  1229. //
  1230. // Keep a pointer to the start, and seek for the end.
  1231. //$REVIEW: Do we need to be super-careful here?
  1232. //$REVIEW: This strchr *could* jump past stuff, but only in MALFORMED data.
  1233. //
  1234. pwsz = m_pwch;
  1235. m_pwch = wcschr (pwsz + 1, wchEnd);
  1236. if (!m_pwch)
  1237. {
  1238. // No end-of-token-char found for this token!
  1239. //
  1240. DebugTrace("HrCheckIfHeader -- No end char (%lc) found for token %ls",
  1241. wchEnd, pwsz);
  1242. return NULL;
  1243. }
  1244. // Save off the end pointer, then advance past the end char.
  1245. // (m_pch now points to the start for the NEXT token.)
  1246. //
  1247. pwszEnd = m_pwch++;
  1248. // Copy the data.
  1249. //
  1250. // The two pointers better be set before we try to copy the data.
  1251. Assert (pwsz);
  1252. Assert (pwszEnd);
  1253. // The difference between, the two pointers gives us
  1254. // the size of the current entry.
  1255. //
  1256. m_buf.AppendAt (0, static_cast<UINT>(pwszEnd - pwsz + 1) * sizeof(WCHAR), pwsz);
  1257. m_buf.Append (sizeof(WCHAR), L""); // NULL-terminate it!
  1258. // Return the string
  1259. //
  1260. return m_buf.PContents();
  1261. }