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.

1841 lines
44 KiB

  1. /*
  2. * F S S E A R C H . C P P
  3. *
  4. * Sources file system implementation of DAV-Search
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davfs.h"
  9. #ifdef __cplusplus
  10. extern "C" {
  11. #endif
  12. #include <msidxs.h>
  13. #ifdef __cplusplus
  14. }
  15. #endif
  16. #include "_fssrch.h"
  17. #include <oledberr.h>
  18. #include <cierror.h>
  19. // 20001801-5de6-11d1-8e38-00c04fb9386d is FMTID_PropertySet as defined
  20. // in pbagex.h. It's the guid of custom props,
  21. //
  22. static const WCHAR gsc_wszSetPropertyName[] =
  23. L"SET PROPERTYNAME '20001801-5de6-11d1-8e38-00c04fb9386d' PROPID '%s' AS \"%s\"";
  24. // gsc_wszPath is used for the Tripoli prop "Path", so don't move to common sz.cpp
  25. //
  26. static const WCHAR gsc_wszSelectPath[] = L"SELECT Path ";
  27. static const ULONG MAX_FULLY_QUALIFIED_LENGTH = 2048;
  28. static const WCHAR gsc_wszShallow[] = L"Shallow";
  29. static const ULONG gsc_cchShallow = CchConstString(gsc_wszShallow);
  30. // class CDBCreateCommand ----------------------------------------------------
  31. //
  32. class CDBCreateCommand : private OnDemandGlobal<CDBCreateCommand, SCODE *>
  33. {
  34. //
  35. // Friend declarations required by OnDemandGlobal template
  36. //
  37. friend class Singleton<CDBCreateCommand>;
  38. friend class RefCountedGlobal<CDBCreateCommand, SCODE *>;
  39. //
  40. // Pointer to the IDBCreateCommand object
  41. //
  42. auto_com_ptr<IDBCreateCommand> m_pDBCreateCommand;
  43. // CREATORS
  44. //
  45. // Declared private to ensure that arbitrary instances
  46. // of this class cannot be created. The Singleton
  47. // template (declared as a friend above) controls
  48. // the sole instance of this class.
  49. //
  50. CDBCreateCommand() {}
  51. BOOL FInit( SCODE * psc );
  52. public:
  53. static SCODE CreateCommand( ICommandText ** ppCommandText );
  54. static VOID Release()
  55. {
  56. DeinitIfUsed();
  57. }
  58. };
  59. BOOL
  60. CDBCreateCommand::FInit( SCODE * psc )
  61. {
  62. SCODE sc = S_OK;
  63. auto_com_ptr<IDBInitialize> pDBInit;
  64. auto_com_ptr<IDBCreateSession> pDBCS;
  65. // Get provider "MSIDXS"
  66. //
  67. sc = CoCreateInstance(CLSID_MSIDXS, NULL, CLSCTX_INPROC_SERVER,
  68. IID_IDBInitialize, (void **)&pDBInit);
  69. if (FAILED(sc))
  70. {
  71. DebugTrace ("Failed to initialized provider MSIDXS \n");
  72. goto ret;
  73. }
  74. // Initialize the provider
  75. //
  76. sc = pDBInit->Initialize();
  77. if (FAILED(sc))
  78. {
  79. DebugTrace ("IDBInitialize::Initialize failed\n");
  80. goto ret;
  81. }
  82. // Get IDBCreateSession
  83. //
  84. sc = pDBInit->QueryInterface(IID_IDBCreateSession, (void**) &pDBCS);
  85. if (FAILED(sc))
  86. {
  87. DebugTrace("QI for IDBCreateSession failed\n");
  88. goto ret;
  89. }
  90. // Create a Session object
  91. //
  92. sc = pDBCS->CreateSession(NULL, IID_IDBCreateCommand,
  93. (IUnknown**) m_pDBCreateCommand.load());
  94. if (FAILED(sc))
  95. {
  96. DebugTrace("pDBCS->CreateSession failed\n");
  97. goto ret;
  98. }
  99. ret:
  100. *psc = sc;
  101. return SUCCEEDED(sc);
  102. }
  103. SCODE
  104. CDBCreateCommand::CreateCommand( ICommandText ** ppCommandText )
  105. {
  106. SCODE sc = S_OK;
  107. if ( !FInitOnFirstUse( &sc ) )
  108. {
  109. DebugTrace( "CDBCreateCommand::CreateCommand() - DwInitRef() failed (0x%08lX)\n", sc );
  110. goto ret;
  111. }
  112. Assert( Instance().m_pDBCreateCommand );
  113. sc = Instance().m_pDBCreateCommand->CreateCommand (NULL, IID_ICommandText,
  114. (IUnknown**) ppCommandText);
  115. ret:
  116. return sc;
  117. }
  118. // ReleaseDBCreateCommandObject()
  119. //
  120. // Called from FSTerminate to free the DBCreateCommand object before quit
  121. //
  122. VOID
  123. ReleaseDBCreateCommandObject()
  124. {
  125. CDBCreateCommand::Release();
  126. }
  127. // Search specifics ----------------------------------------------------------
  128. //
  129. BOOL IsLegalVarChar(WCHAR wch)
  130. {
  131. return iswalnum(wch)
  132. || (L'.' == wch)
  133. || (L':' == wch)
  134. || (L'-' == wch)
  135. || (L'_' == wch)
  136. || (L'/' == wch)
  137. || (L'*' == wch); // * included to support 'select *'
  138. }
  139. //
  140. // FTranslateScope
  141. // detect whether a given URI or a path is under the
  142. // davfs virutal directory
  143. //
  144. // pmu [in] pointer to IMethUtil object
  145. // pwszURIOrPath [in] URI or the physical path, non-NULL terminated
  146. // cchPath [in] the number of chars of the path
  147. // ppwszPath [in] receive the pointer to the translated path
  148. //
  149. BOOL
  150. FTranslateScope (LPMETHUTIL pmu,
  151. LPCWSTR pwszURI,
  152. ULONG cchURI,
  153. auto_heap_ptr<WCHAR>& pwszPath)
  154. {
  155. SCODE sc = S_OK;
  156. CStackBuffer<WCHAR,MAX_PATH> pwszTerminatedURI;
  157. CStackBuffer<WCHAR,MAX_PATH> pwszURINormalized;
  158. UINT cchURINormalized;
  159. UINT cch;
  160. // We need to make a copy of '\0' terminated URI
  161. //
  162. if (NULL == pwszTerminatedURI.resize(CbSizeWsz(cchURI)))
  163. {
  164. sc = E_OUTOFMEMORY;
  165. DebugTrace("FTranslatedScope() - Error while allocating memory 0x%08lX\n", sc);
  166. return FALSE;
  167. }
  168. memcpy(pwszTerminatedURI.get(), pwszURI, cchURI * sizeof(WCHAR));
  169. pwszTerminatedURI[cchURI] = L'\0';
  170. // We need to unescape the scope URI before translate
  171. //
  172. cchURINormalized = pwszURINormalized.celems();
  173. sc = ScNormalizeUrl (pwszTerminatedURI.get(),
  174. &cchURINormalized,
  175. pwszURINormalized.get(),
  176. NULL);
  177. if (S_FALSE == sc)
  178. {
  179. if (NULL == pwszURINormalized.resize(cchURINormalized * sizeof(WCHAR)))
  180. {
  181. sc = E_OUTOFMEMORY;
  182. DebugTrace("FTranslatedScope() - Error while allocating memory 0x%08lX\n", sc);
  183. return FALSE;
  184. }
  185. sc = ScNormalizeUrl (pwszTerminatedURI.get(),
  186. &cchURINormalized,
  187. pwszURINormalized.get(),
  188. NULL);
  189. // Since we've given ScNormalizeUrl() the space it asked for,
  190. // we should never get S_FALSE again. Assert this!
  191. //
  192. Assert(S_FALSE != sc);
  193. }
  194. if (FAILED (sc))
  195. {
  196. DebugTrace("FTranslatedScope() - ScNormalizeUrl() failed 0x%08lX\n", sc);
  197. return FALSE;
  198. }
  199. // Do the translation and check the validation
  200. //
  201. // At most we should go through the processing below twice, as the byte
  202. // count required is an out param.
  203. //
  204. cch = MAX_PATH;
  205. do {
  206. pwszPath.realloc(cch * sizeof(WCHAR));
  207. sc = pmu->ScStoragePathFromUrl (pwszURINormalized.get(), pwszPath, &cch);
  208. } while (sc == S_FALSE);
  209. if (FAILED (sc))
  210. {
  211. DebugTrace ("FTranslateScope() - IMethUtil::ScStoragePathFromUrl() failed to translate scope URI 0x%08lX\n", sc);
  212. return FALSE;
  213. }
  214. //$ SECURITY:
  215. //
  216. // Check to see if the scope is really a short filename.
  217. //
  218. sc = ScCheckIfShortFileName (pwszPath, pmu->HitUser());
  219. if (FAILED (sc))
  220. {
  221. DebugTrace ("FTranslateScope() - ScCheckIfShortFileName() failed to scope, is short filename 0x%08lX\n", sc);
  222. return FALSE;
  223. }
  224. //$ SECURITY:
  225. //
  226. // Check to see if the destination is really the default
  227. // data stream via alternate file access.
  228. //
  229. sc = ScCheckForAltFileStream (pwszPath);
  230. if (FAILED (sc))
  231. {
  232. DebugTrace ("FTranslateScope() - ScCheckForAltFileStream() failed to scope, is short filename 0x%08lX\n", sc);
  233. return FALSE;
  234. }
  235. return TRUE;
  236. }
  237. //
  238. // ScSetPropertyName
  239. //
  240. // execute SET PROPERTYNAME command on the passed in property
  241. // so that Index Server will be aware of this prop
  242. //
  243. SCODE
  244. ScSetPropertyName(ICommandText * pCommandText, LPWSTR pwszName)
  245. {
  246. CStackBuffer<WCHAR,MAX_FULLY_QUALIFIED_LENGTH> pwszSet;
  247. auto_com_ptr<IRowset> pRowset;
  248. SCODE sc = S_OK;
  249. int cchNeeded;
  250. int cchStored;
  251. Assert(pCommandText != NULL);
  252. Assert(pwszName != NULL);
  253. if ((NULL == pCommandText) || (NULL == pwszName))
  254. {
  255. sc = E_POINTER;
  256. goto ret;
  257. }
  258. // cchNeeded is the length of the final formatted string, including the
  259. // terminating null
  260. //
  261. cchNeeded = CchConstString(gsc_wszSetPropertyName) + wcslen(pwszName) * 2 + 1;
  262. if (NULL == pwszSet.resize(cchNeeded * sizeof(WCHAR)))
  263. {
  264. sc = E_OUTOFMEMORY;
  265. goto ret;
  266. }
  267. // generate the SET PROPERTYNAME command
  268. //
  269. cchStored = _snwprintf(pwszSet.get(), cchNeeded, gsc_wszSetPropertyName, pwszName, pwszName);
  270. // _snwprintf returns the number of chars stored, not including the terminating null.
  271. // It returns a negative value if the buffer was too short to store the formatted string
  272. // plus the terminating null.
  273. // So, a non-negative result means that the string plus the terminating null was stored.
  274. //
  275. // We check even more strictly here - we always expect our up-front length calculation
  276. // to be exact.
  277. //
  278. Assert(cchStored == cchNeeded - 1);
  279. if (cchStored != cchNeeded - 1)
  280. {
  281. sc = E_FAIL;
  282. goto ret;
  283. }
  284. // set the command text
  285. //
  286. sc = pCommandText->SetCommandText(DBGUID_DEFAULT, pwszSet.get());
  287. if (FAILED(sc))
  288. {
  289. DebugTrace ("failed to set command text %ws\n", pwszSet.get());
  290. goto ret;
  291. }
  292. // do the actual set
  293. //
  294. sc = pCommandText->Execute(NULL, IID_IRowset, 0, 0, (IUnknown**) &pRowset);
  295. if (FAILED(sc))
  296. {
  297. DebugTrace ("failed to execute %ws\n", pwszSet.get());
  298. goto ret;
  299. }
  300. Assert (DB_S_NORESULT == sc);
  301. Assert (!pRowset);
  302. ret:
  303. return (sc == DB_S_NORESULT) ? S_OK : sc;
  304. }
  305. void
  306. AddChildVrPaths (IMethUtil* pmu,
  307. LPCWSTR pwszUrl,
  308. ChainedStringBuffer<WCHAR>& sb,
  309. CVRList& vrl,
  310. CWsziList& lst)
  311. {
  312. CVRList::iterator it;
  313. ChainedStringBuffer<WCHAR> sbLocal;
  314. // See if there are child vroots to process as well. We don't
  315. // have a path at this time for scoping, so we can pass NULL and
  316. // duplicates will get removed when we sort/unique.
  317. //
  318. if (S_OK == pmu->ScFindChildVRoots (pwszUrl, sbLocal, vrl))
  319. {
  320. for (it = vrl.begin(); it != vrl.end(); it++)
  321. {
  322. auto_ref_ptr<CVRoot> cvr;
  323. if (pmu->FGetChildVRoot (it->m_pwsz, cvr))
  324. {
  325. LPCWSTR pwszPath;
  326. UINT cch;
  327. // Add it to the list
  328. //
  329. cch = cvr->CchGetVRPath (&pwszPath);
  330. lst.push_back(CRCWszi(sb.Append (CbSizeWsz(cch), pwszPath)));
  331. }
  332. }
  333. lst.sort();
  334. lst.unique();
  335. }
  336. }
  337. // Tripoli prop names
  338. //
  339. static const WCHAR gsc_Tripoli_wszFilename[] = L"filename";
  340. static const WCHAR gsc_Tripoli_wszSize[] = L"size";
  341. static const WCHAR gsc_Tripoli_wszCreate[] = L"create";
  342. static const WCHAR gsc_Tripoli_wszWrite[] = L"write";
  343. static const WCHAR gsc_Tripoli_wszAttrib[] = L"attrib";
  344. // ScMapReservedPropInWhereClause
  345. //
  346. // Helper function to map DAV reserved props to
  347. //
  348. SCODE
  349. ScMapReservedPropInWhereClause (LPWSTR pwszName, UINT * pirp)
  350. {
  351. UINT irp;
  352. SCODE sc = S_OK;
  353. Assert (pirp);
  354. // We only care those properties not stored in propertybag
  355. // RESERVED_GET is just for this purpose
  356. //
  357. if (CFSProp::FReservedProperty (pwszName, CFSProp::RESERVED_GET, &irp))
  358. {
  359. // Here's our mapping table
  360. //
  361. // DAV Prop Tripoli prop
  362. //
  363. // DAV:getcontentlength size
  364. // DAV:displayname filename
  365. // DAV:creationdate create
  366. // DAV:lastmodified write
  367. // DAV:ishidden attrib
  368. // DAV:iscollection attrib
  369. // DAV:resourcetype <no mapping>
  370. // DAV:getetag <no mapping>
  371. // DAV:lockdiscovery <no mapping>
  372. // DAV:supportedlock <no mapping>
  373. // Now that we are to overwrite dav reserved prop name with
  374. // the Tripoli prop name in place, the buffer must have enough
  375. // space
  376. // Assert this fact that all the six reserved we will ever map
  377. // satisfy this requirement
  378. //
  379. Assert ((wcslen(sc_rp[iana_rp_content_length].pwsz) >= wcslen (gsc_Tripoli_wszSize)) &&
  380. (wcslen(sc_rp[iana_rp_creation_date].pwsz) >= wcslen (gsc_Tripoli_wszCreate)) &&
  381. (wcslen(sc_rp[iana_rp_displayname].pwsz) >= wcslen (gsc_Tripoli_wszFilename)) &&
  382. (wcslen(sc_rp[iana_rp_last_modified].pwsz) >= wcslen (gsc_Tripoli_wszWrite)) &&
  383. (wcslen(sc_rp[iana_rp_ishidden].pwsz) >= wcslen (gsc_Tripoli_wszAttrib)) &&
  384. (wcslen(sc_rp[iana_rp_iscollection].pwsz) >= wcslen (gsc_Tripoli_wszAttrib)));
  385. switch (irp)
  386. {
  387. case iana_rp_content_length:
  388. wcscpy (pwszName, gsc_Tripoli_wszSize);
  389. break;
  390. case iana_rp_creation_date:
  391. wcscpy (pwszName, gsc_Tripoli_wszCreate);
  392. break;
  393. case iana_rp_displayname:
  394. wcscpy (pwszName, gsc_Tripoli_wszFilename);
  395. break;
  396. case iana_rp_last_modified:
  397. wcscpy (pwszName, gsc_Tripoli_wszWrite);
  398. break;
  399. case iana_rp_ishidden:
  400. case iana_rp_iscollection:
  401. wcscpy (pwszName, gsc_Tripoli_wszAttrib);
  402. break;
  403. case iana_rp_etag:
  404. case iana_rp_resourcetype:
  405. case iana_rp_lockdiscovery:
  406. case iana_rp_supportedlock:
  407. // Among these four props, we data type of resourcetype is
  408. // a XML node, no way to express that in SQL.
  409. // And the rest three, we don't have a Tripoli mapping for them
  410. //
  411. // DB_E_ERRORSINCOMMAND will be mapped to 400 Bad Request
  412. //
  413. sc = DB_E_ERRORSINCOMMAND;
  414. goto ret;
  415. default:
  416. // Catch the bad boy
  417. //
  418. AssertSz (FALSE, "Unexpected reserved props");
  419. break;
  420. }
  421. *pirp = irp;
  422. }
  423. ret:
  424. return sc;
  425. }
  426. const WCHAR gsc_wszStar[] = L"*";
  427. const WCHAR gsc_wszAll[] = L"all";
  428. const WCHAR gsc_wszDistinct[] = L"distinct";
  429. // FSSearch::ScSetSQL
  430. //
  431. // Translate a SQL query, basically is to just replace the alias with the
  432. // corresponding namespace.
  433. //
  434. SCODE
  435. CFSSearch::ScSetSQL (CParseNmspcCache * pnsc, LPCWSTR pwszSQL)
  436. {
  437. BOOL fPropAdded = FALSE;
  438. BOOL fStarUsed = FALSE;
  439. BOOL fQuoted = FALSE;
  440. CStackBuffer<WCHAR,128> pwszUrlT;
  441. LPCWSTR pwsz;
  442. LPCWSTR pwszNameBegin;
  443. LPCWSTR pwszWordBegin;
  444. SCODE sc = S_OK;
  445. UINT cLen;
  446. typedef enum {
  447. SQL_NO_STATE,
  448. SQL_SELECT,
  449. SQL_FROM,
  450. SQL_WHERE,
  451. SQL_MORE
  452. } SQL_STATE;
  453. SQL_STATE state = SQL_NO_STATE;
  454. // Create the command text object
  455. //
  456. sc = CDBCreateCommand::CreateCommand (m_pCommandText.load());
  457. if (FAILED(sc))
  458. goto ret;
  459. // Parse out the SQL
  460. //
  461. pwsz = const_cast<LPWSTR>(pwszSQL);
  462. Assert (pwsz);
  463. while (*pwsz)
  464. {
  465. // Filter out white spaces
  466. //
  467. while (*pwsz && iswspace(*pwsz))
  468. pwsz++;
  469. // check to see if we reach the end of the string
  470. //
  471. if (!(*pwsz))
  472. break;
  473. // remember the starting position
  474. //
  475. pwszWordBegin = pwsz;
  476. if (IsLegalVarChar(*pwsz))
  477. {
  478. CStackBuffer<WCHAR> pwszName;
  479. pwszNameBegin = pwsz;
  480. cLen = 0;
  481. // look for a variable
  482. //
  483. if (fQuoted)
  484. {
  485. // Pick up the propname as a whole
  486. //
  487. while (*pwsz && (*pwsz != L'"'))
  488. pwsz++;
  489. }
  490. else
  491. {
  492. while (*pwsz && IsLegalVarChar(*pwsz))
  493. pwsz++;
  494. }
  495. // Translate the name here
  496. //
  497. cLen = static_cast<UINT>(pwsz - pwszNameBegin);
  498. if (NULL == pwszName.resize(CbSizeWsz(cLen)))
  499. {
  500. sc = E_OUTOFMEMORY;
  501. goto ret;
  502. }
  503. wcsncpy (pwszName.get(), pwszNameBegin, cLen);
  504. pwszName[cLen] = 0;
  505. switch (state)
  506. {
  507. case SQL_NO_STATE:
  508. if (!_wcsnicmp (pwszWordBegin, gc_wszSelect, pwsz-pwszWordBegin))
  509. state = SQL_SELECT;
  510. break;
  511. case SQL_SELECT:
  512. if (!_wcsnicmp (pwszWordBegin, gc_wszFrom, pwsz-pwszWordBegin))
  513. {
  514. // Empty select statement is an error
  515. //
  516. if (!fPropAdded && !fStarUsed)
  517. {
  518. sc = E_INVALIDARG;
  519. goto ret;
  520. }
  521. // We've finished the SELECT statement.
  522. // Note that all that we need is 'SELECT path '.
  523. // We take care of all the rest ourselves, so restruct
  524. // the SELECT path here before we continue
  525. //
  526. m_sbSQL.Reset();
  527. m_sbSQL.Append(gsc_wszSelectPath);
  528. state = SQL_FROM;
  529. break;
  530. }
  531. // Add to our list of properties to retrieve
  532. //
  533. if (!wcscmp(pwszName.get(), gsc_wszStar))
  534. {
  535. sc = m_cfc.ScGetAllProps (NULL);
  536. if (FAILED(sc))
  537. goto ret;
  538. fStarUsed = TRUE;
  539. }
  540. else
  541. {
  542. // Following Monarch Stage 1.
  543. //
  544. if (!fQuoted)
  545. {
  546. if (!_wcsicmp(pwszName.get(), gsc_wszAll))
  547. break;
  548. if (!_wcsicmp(pwszName.get(), gsc_wszDistinct))
  549. {
  550. // Monarch does not allow distinct
  551. //
  552. sc = E_INVALIDARG;
  553. goto ret;
  554. }
  555. }
  556. // Normal props
  557. //
  558. sc = m_cfc.ScAddProp (NULL, pwszName.get(), FALSE);
  559. if (FAILED(sc))
  560. goto ret;
  561. fPropAdded = TRUE;
  562. }
  563. break;
  564. case SQL_FROM:
  565. {
  566. BOOL fScopeExist = FALSE;
  567. CWsziList lst;
  568. CWsziList::iterator itPath;
  569. LPCWSTR pwszScopePath = m_pmu->LpwszPathTranslated();
  570. LPCWSTR pwszUrl = m_pmu->LpwszRequestUrl();
  571. BOOL fShallow = FALSE;
  572. // Monarch Syntax:
  573. // FROM { SCOPE( [ 'Scope_Arguments' ] ) | View_Name }
  574. // Scope_Arguments =
  575. // ' [ Traversal_Type ] ( "Path" [ , "Path", ...]
  576. // Path can be URI or physical path
  577. // We verify every path should must be under our
  578. // virtual directory and we allow only one path
  579. // Note, if we ever want to accept multiple path, then
  580. // we need some extra code, mainly another for loop.
  581. // For now, talk with Joels, keep it this way.
  582. //
  583. if (!_wcsnicmp (pwszWordBegin, gc_wszScope, pwsz-pwszWordBegin))
  584. {
  585. StringBuffer<WCHAR> sbNameBuilder;
  586. LPCWSTR pwszStart = pwsz;
  587. ULONG cLevel = 0;
  588. BOOL fInSingleQuote = FALSE;
  589. sbNameBuilder.Append(static_cast<UINT>(sizeof(WCHAR) * wcslen(pwszName.get())), pwszName.get());
  590. // Parse the scope arguments list
  591. //
  592. while (*pwsz)
  593. {
  594. if (L'(' == *pwsz)
  595. {
  596. cLevel++;
  597. }
  598. else if (L')' == *pwsz)
  599. {
  600. if (NULL == (--cLevel))
  601. break;
  602. }
  603. else if (L'\'' == *pwsz)
  604. {
  605. // If this is a closing single quote, flip the
  606. // switch.
  607. //
  608. if (fInSingleQuote)
  609. {
  610. // It's an error if no scope inside ''
  611. //
  612. if (!fScopeExist)
  613. {
  614. sc = E_INVALIDARG;
  615. goto ret;
  616. }
  617. // The next single quote will be an
  618. // opening single quote
  619. //
  620. fInSingleQuote = FALSE;
  621. }
  622. else
  623. {
  624. // We need to find out the traversal type
  625. // as we rely on Monarch to check syntax, so it
  626. // is OK for us to assume the syntax is correct,
  627. // Anything we missed can be caught later in
  628. // Monarch.
  629. //
  630. pwsz++;
  631. while (*pwsz && iswspace(*pwsz))
  632. pwsz++;
  633. // Check if it is "Shallow Traversal", again
  634. // we check only the word "shallow", any syntax
  635. // error can be caught later in Monarch.
  636. //
  637. if (!_wcsnicmp(pwsz, gsc_wszShallow, gsc_cchShallow))
  638. fShallow = TRUE;
  639. // The next single quote will be a closing
  640. // sinlge quote
  641. //
  642. fInSingleQuote = TRUE;
  643. // we've point to the next char, so loop back
  644. // immediately
  645. //
  646. continue;
  647. }
  648. }
  649. else if (L'"' == *pwsz)
  650. {
  651. auto_heap_ptr<WCHAR> pwszPath;
  652. LPCWSTR pwszPathStart;
  653. // Copy over bytes up to '"'.
  654. //
  655. pwsz++;
  656. sbNameBuilder.Append(static_cast<UINT>(sizeof(WCHAR) * (pwsz -pwszStart)), pwszStart);
  657. // Look for the start of scope
  658. //
  659. while ((*pwsz) && iswspace(*pwsz))
  660. pwsz++;
  661. pwszPathStart = pwsz;
  662. // We really only allow a single
  663. // path in our scope. Fail others
  664. // with bad request
  665. //
  666. if (fScopeExist)
  667. {
  668. sc = E_INVALIDARG;
  669. goto ret;
  670. }
  671. // look for the end of the path
  672. //
  673. while (*(pwsz) && *pwsz != L'"')
  674. pwsz++;
  675. if (!(*pwsz))
  676. break;
  677. fScopeExist = TRUE;
  678. // Translate the scope:
  679. // - forbid the physical path
  680. // - translate the URI and reject
  681. // any URI beyond our VR
  682. //
  683. if (pwsz > pwszPathStart)
  684. {
  685. UINT cchUrlT;
  686. if (!FTranslateScope (m_pmu,
  687. pwszPathStart,
  688. static_cast<UINT>(pwsz-pwszPathStart),
  689. pwszPath))
  690. {
  691. // return an error that would be mapped to
  692. // HSC_FORBIDDEN
  693. //
  694. sc = STG_E_DISKISWRITEPROTECTED;
  695. Assert (HSC_FORBIDDEN == HscFromHresult(sc));
  696. goto ret;
  697. }
  698. // use the translated physical path
  699. //
  700. pwszScopePath = AppendChainedSz(m_csb, pwszPath);
  701. lst.push_back(CRCWszi(pwszScopePath));
  702. // Allocate space for the URL and keep it hanging on
  703. //
  704. cchUrlT = static_cast<UINT>(pwsz - pwszPathStart);
  705. if (NULL == pwszUrlT.resize(CbSizeWsz(cchUrlT)))
  706. {
  707. sc = E_OUTOFMEMORY;
  708. goto ret;
  709. }
  710. memcpy(pwszUrlT.get(), pwszPathStart, cchUrlT * sizeof(WCHAR));
  711. pwszUrlT[cchUrlT] = L'\0';
  712. pwszUrl = pwszUrlT.get();
  713. }
  714. else
  715. {
  716. // we've got a "". Insert the request URI
  717. //
  718. Assert (pwsz == pwszPathStart);
  719. Assert ((*pwsz == L'"') && (*(pwsz-1) == L'"'));
  720. lst.push_back(CRCWszi(pwszScopePath));
  721. }
  722. pwszStart = pwsz;
  723. }
  724. pwsz++;
  725. }
  726. // Syntax check
  727. //
  728. if (fInSingleQuote || !(*pwsz))
  729. {
  730. // unbalanced ', " or )
  731. //
  732. sc = E_INVALIDARG;
  733. goto ret;
  734. }
  735. // include ')'
  736. //
  737. pwsz++;
  738. if (!fScopeExist)
  739. {
  740. static WCHAR gs_wszScopeBegin[] = L"('\"";
  741. sbNameBuilder.Append(sizeof(WCHAR) * CchConstString(gs_wszScopeBegin), gs_wszScopeBegin);
  742. // Pickup the request uri
  743. //
  744. lst.push_back(CRCWszi(pwszScopePath));
  745. }
  746. // Search Child Vroots only if we are doing
  747. // a non-shallow traversal.
  748. //$ REVIEW(zyang).
  749. // Here we drop the subvroot in the shallow search
  750. // This is not quite right, Assume we are searching /fs
  751. // and it has a sub vroot /fs/sub. we expect to see
  752. // /fs/sub in the search result. but we lost it.
  753. // However, if we include this sub vroot in the search
  754. // path, it's even worse, as a shallow traversal on
  755. // /fs/sub will give us all /fs/sub/*, which is another
  756. // level deep.
  757. // There's no easy fix for this, unless, we keep a list
  758. // of first level vroots and emit ourselves. That's
  759. // of extra code, and don't know how much it would buy us.
  760. // As a compromise for now, we simply drop the sub vroot
  761. // in shallow search.
  762. //
  763. if (!fShallow)
  764. {
  765. AddChildVrPaths (m_pmu,
  766. pwszUrl,
  767. m_csb,
  768. m_vrl,
  769. lst);
  770. }
  771. // Construct the scope
  772. //
  773. Assert (!lst.empty());
  774. itPath = lst.begin();
  775. sbNameBuilder.Append(static_cast<UINT>(sizeof(WCHAR) * wcslen(itPath->m_pwsz)), itPath->m_pwsz);
  776. for (itPath++; itPath != lst.end(); itPath++)
  777. {
  778. static WCHAR gs_wszScopeMiddle[] = L"\", \"";
  779. sbNameBuilder.Append(sizeof(WCHAR) * CchConstString(gs_wszScopeMiddle), gs_wszScopeMiddle);
  780. sbNameBuilder.Append(static_cast<UINT>(sizeof(WCHAR) * wcslen(itPath->m_pwsz)), itPath->m_pwsz);
  781. }
  782. static WCHAR gs_wszScopeEnd[] = L"\"')";
  783. sbNameBuilder.Append(sizeof(WCHAR) * CchConstString(gs_wszScopeEnd), gs_wszScopeEnd);
  784. // Get the size of constructed string without NULL
  785. // termination
  786. //
  787. cLen = sbNameBuilder.CchSize();
  788. // NULL terminate the string
  789. //
  790. sbNameBuilder.FTerminate();
  791. // Replace with the new string
  792. //
  793. if (NULL == pwszName.resize(CbSizeWsz(cLen)))
  794. {
  795. sc = E_OUTOFMEMORY;
  796. goto ret;
  797. }
  798. lstrcpyW (pwszName.get(), sbNameBuilder.PContents());
  799. Assert(cLen == wcslen(pwszName.get()));
  800. Assert(cLen+1 <= pwszName.celems());
  801. // After the Scope is processed, the only thing that
  802. // we want to do is the custom properties. so we don't
  803. // care if the rest is a WHERE or an ORDER BY or else
  804. //
  805. state = SQL_MORE;
  806. }
  807. break;
  808. }
  809. case SQL_WHERE:
  810. case SQL_MORE:
  811. // It's not easy for us to tell which prop is custom prop
  812. // and thus need to be set to the command object.
  813. // without a real parse tree, we can't tell names from
  814. // operators and literals.
  815. //
  816. // a good guess is that if the prop is quoted by double
  817. // quote, we can treat it as a custom prop. Note, this
  818. // imposes the requirment that all props, including
  819. // namespaceless props must be quoted. all unquoted
  820. // are either Tripoli props or operators/literals which
  821. // we can just copy over. this makes our life easier
  822. //
  823. // We need to map some DAV reserved properties to tripoli
  824. // props when they appear in the where clause
  825. //
  826. if (fQuoted)
  827. {
  828. UINT irp = sc_crp_set_reserved; //max value
  829. sc = ScMapReservedPropInWhereClause (pwszName.get(), &irp);
  830. if (FAILED(sc))
  831. goto ret;
  832. if (irp != sc_crp_set_reserved)
  833. cLen = static_cast<UINT>(wcslen(pwszName.get()));
  834. else
  835. {
  836. // SET PROPERTYNAME on custom props
  837. //
  838. sc = ScSetPropertyName (m_pCommandText, pwszName.get());
  839. if (FAILED(sc))
  840. {
  841. DebugTrace ("Failed to set custom prop %ws to Monarch\n",
  842. pwszName.get());
  843. goto ret;
  844. }
  845. }
  846. }
  847. break;
  848. default:
  849. break;
  850. }
  851. // Append the name
  852. //
  853. m_sbSQL.Append(sizeof(WCHAR)*cLen, pwszName.get());
  854. if (L'"' != *pwsz)
  855. {
  856. // add seperator
  857. //
  858. m_sbSQL.Append(L" ");
  859. }
  860. }
  861. else if (L'\'' == *pwsz)
  862. {
  863. // copy literals over
  864. pwsz++;
  865. while (*pwsz && (L'\'' != *pwsz))
  866. pwsz++;
  867. if (!*pwsz)
  868. {
  869. DebugTrace("unbalanced single quote\n");
  870. sc = E_INVALIDARG;
  871. goto ret;
  872. }
  873. else
  874. {
  875. Assert(L'\'' == *pwsz);
  876. // copy over
  877. //
  878. pwsz++;
  879. m_sbSQL.Append( static_cast<UINT>(pwsz-pwszWordBegin) * sizeof(WCHAR),
  880. pwszWordBegin);
  881. }
  882. // add seperator
  883. //
  884. m_sbSQL.Append(L" ");
  885. }
  886. else if (L'"' == *pwsz)
  887. {
  888. // toggle the flag
  889. //
  890. fQuoted = !fQuoted;
  891. pwsz++;
  892. m_sbSQL.Append(L"\"");
  893. // Apeend seperator after closing '"'
  894. //
  895. if (!fQuoted)
  896. m_sbSQL.Append(L" ");
  897. }
  898. else
  899. {
  900. // some char we don't have interest on, just copy over
  901. //
  902. while (*pwsz && !IsLegalVarChar(*pwsz)
  903. && (L'\'' != *pwsz) && (L'"' != *pwsz))
  904. pwsz++;
  905. // Append the name
  906. //
  907. m_sbSQL.Append( static_cast<UINT>(pwsz-pwszWordBegin) * sizeof(WCHAR),
  908. pwszWordBegin);
  909. }
  910. }
  911. // Close the string
  912. //
  913. m_sbSQL.Append(sizeof(WCHAR), L"");
  914. SearchTrace ("Search: translated query is: \"%ls\"\n", PwszSQL());
  915. ret:
  916. return sc;
  917. }
  918. static void
  919. SafeWcsCopy (LPWSTR pwszDst, LPCWSTR pwszSrc)
  920. {
  921. // Make sure we are not doing any evil copies...
  922. //
  923. Assert (pwszDst && pwszSrc && (pwszDst <= pwszSrc));
  924. if (pwszDst == pwszSrc)
  925. return;
  926. while (*pwszSrc)
  927. *pwszDst++ = *pwszSrc++;
  928. *pwszDst = L'\0';
  929. return;
  930. }
  931. SCODE
  932. CFSSearch::ScEmitRow (CXMLEmitter& emitter)
  933. {
  934. auto_ref_ptr<IMDData> pMDData;
  935. CResourceInfo cri;
  936. CStackBuffer<WCHAR,128> pwszExt;
  937. CVRList::iterator it;
  938. LPWSTR pwszFile;
  939. SCODE sc = S_OK;
  940. UINT cch;
  941. // Get the filename
  942. //
  943. pwszFile = reinterpret_cast<LPWSTR>(m_pData.get());
  944. sc = cri.ScGetResourceInfo (pwszFile);
  945. if (FAILED (sc))
  946. goto ret;
  947. // FSPropTarget sort of needs the URI of the target.
  948. // What is really important here, is the file extension.
  949. // We can fake it out by just pretending the file
  950. // is the URL name.
  951. //
  952. cch = pwszExt.celems();
  953. sc = m_pmu->ScUrlFromStoragePath(pwszFile, pwszExt.get(), &cch);
  954. if (S_FALSE == sc)
  955. {
  956. if (NULL == pwszExt.resize(cch * sizeof(WCHAR)))
  957. {
  958. sc = E_OUTOFMEMORY;
  959. goto ret;
  960. }
  961. sc = m_pmu->ScUrlFromStoragePath(pwszFile, pwszExt.get(), &cch);
  962. }
  963. if (S_OK != sc)
  964. {
  965. Assert (S_FALSE != sc);
  966. goto ret;
  967. }
  968. // Strip the prefix
  969. //
  970. SafeWcsCopy(pwszExt.get(), PwszUrlStrippedOfPrefix(pwszExt.get()));
  971. // Emit the row (ie. call ScFindFileProps()) if-and-only-if
  972. // We know this url is to be indexed. In particular, can we
  973. // sniff the metabase, and is the index bit set.
  974. //$178052: We also need to respect the dirbrowsing bit
  975. //
  976. SearchTrace ("Search: found row at '%S'\n", pwszExt.get());
  977. if (SUCCEEDED (m_pmu->HrMDGetData (pwszExt.get(), pMDData.load())))
  978. {
  979. if (pMDData->FIsIndexed() &&
  980. (pMDData->DwDirBrowsing() & MD_DIRBROW_ENABLED))
  981. {
  982. // Find the properties
  983. //
  984. sc = ScFindFileProps (m_pmu,
  985. m_cfc,
  986. emitter,
  987. pwszExt.get(),
  988. pwszFile,
  989. NULL,
  990. cri,
  991. TRUE /*fEmbedErrorsInResponse*/);
  992. if (FAILED (sc))
  993. goto ret;
  994. }
  995. else
  996. SearchTrace ("Search: found '%S' is not indexed\n", pwszExt);
  997. pMDData.clear();
  998. }
  999. // See if any of the other translation contexts apply to this
  1000. // path as well.
  1001. //
  1002. for (it = m_vrl.begin(); it != m_vrl.end(); it++)
  1003. {
  1004. auto_ref_ptr<CVRoot> cvr;
  1005. if (!m_pmu->FGetChildVRoot (it->m_pwsz, cvr))
  1006. continue;
  1007. cch = pwszExt.celems();
  1008. sc = ScUrlFromSpannedStoragePath (pwszFile,
  1009. *(cvr.get()),
  1010. pwszExt.get(),
  1011. &cch);
  1012. if (S_FALSE == sc)
  1013. {
  1014. if (NULL == pwszExt.resize(cch * sizeof(WCHAR)))
  1015. {
  1016. sc = E_OUTOFMEMORY;
  1017. goto ret;
  1018. }
  1019. sc = ScUrlFromSpannedStoragePath (pwszFile,
  1020. *(cvr.get()),
  1021. pwszExt.get(),
  1022. &cch);
  1023. }
  1024. if (S_OK == sc)
  1025. {
  1026. SafeWcsCopy (pwszExt.get(), PwszUrlStrippedOfPrefix(pwszExt.get()));
  1027. SearchTrace ("Search: found row at '%S'\n", pwszExt.get());
  1028. // Again, we have to see if this resource is even allowed
  1029. // to be indexed...
  1030. //
  1031. LPCWSTR pwszMbPathVRoot;
  1032. CStackBuffer<WCHAR,128> pwszMbPathChild;
  1033. UINT cchPrefix;
  1034. UINT cchUrl = static_cast<UINT>(wcslen(pwszExt.get()));
  1035. // Map the URI to its equivalent metabase path, and make sure
  1036. // the URL is stripped before we call into the MDPath processing
  1037. //
  1038. cchPrefix = cvr->CchPrefixOfMetabasePath (&pwszMbPathVRoot);
  1039. if (NULL == pwszMbPathChild.resize(CbSizeWsz(cchPrefix + cchUrl)))
  1040. {
  1041. sc = E_OUTOFMEMORY;
  1042. goto ret;
  1043. }
  1044. memcpy (pwszMbPathChild.get(), pwszMbPathVRoot, cchPrefix * sizeof(WCHAR));
  1045. memcpy (pwszMbPathChild.get() + cchPrefix, pwszExt.get(), (cchUrl + 1) * sizeof(WCHAR));
  1046. // As above, Emit the row (ie. call ScFindFileProps())
  1047. // if-and-only-if We know this url is to be indexed.
  1048. // In particular, can we sniff the metabase, and is
  1049. // the index bit set.
  1050. //
  1051. if (SUCCEEDED(m_pmu->HrMDGetData (pwszMbPathChild.get(),
  1052. pwszMbPathVRoot,
  1053. pMDData.load())))
  1054. {
  1055. if (pMDData->FIsIndexed())
  1056. {
  1057. // ... and get the properties
  1058. //
  1059. sc = ScFindFileProps (m_pmu,
  1060. m_cfc,
  1061. emitter,
  1062. pwszExt.get(),
  1063. pwszFile,
  1064. cvr.get(),
  1065. cri,
  1066. TRUE /*fEmbedErrorsInResponse*/);
  1067. if (FAILED (sc))
  1068. goto ret;
  1069. }
  1070. else
  1071. SearchTrace ("Search: found '%S' is not indexed\n", pwszExt);
  1072. }
  1073. }
  1074. }
  1075. sc = S_OK;
  1076. ret:
  1077. return sc;
  1078. }
  1079. SCODE
  1080. CFSSearch::ScCreateAccessor()
  1081. {
  1082. SCODE sc = S_OK;
  1083. DBORDINAL cCols = 0;
  1084. auto_com_ptr<IColumnsInfo> pColInfo;
  1085. // QI to the IColumnsInfo interface, with which we can get the column information
  1086. //
  1087. sc = m_prs->QueryInterface (IID_IColumnsInfo,
  1088. reinterpret_cast<VOID**>(pColInfo.load()));
  1089. if (FAILED(sc))
  1090. goto ret;
  1091. // get all the column information
  1092. //
  1093. sc = pColInfo->GetColumnInfo (&cCols, &m_rgInfo, &m_pwszBuf);
  1094. if (FAILED(sc))
  1095. goto ret;
  1096. // 'Path' is the only property in our SELECT list
  1097. //
  1098. Assert (cCols == 1);
  1099. m_rgBindings = (DBBINDING *) g_heap.Alloc (sizeof (DBBINDING));
  1100. // set the m_rgBindings according to the information we know
  1101. //
  1102. m_rgBindings->dwPart = DBPART_VALUE | DBPART_STATUS;
  1103. // ignored fields
  1104. //
  1105. m_rgBindings->eParamIO = DBPARAMIO_NOTPARAM;
  1106. // set column ordinal
  1107. //
  1108. m_rgBindings->iOrdinal = m_rgInfo->iOrdinal;
  1109. // set the type
  1110. //
  1111. m_rgBindings->wType = m_rgInfo->wType;
  1112. // we own the memory
  1113. //
  1114. m_rgBindings->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  1115. // set the maximum length of the column
  1116. //
  1117. Assert (m_rgInfo->wType == DBTYPE_WSTR);
  1118. m_rgBindings->cbMaxLen = m_rgInfo->ulColumnSize * sizeof(WCHAR);
  1119. // offset to the value in the consumer's buffer
  1120. //
  1121. m_rgBindings->obValue = 0;
  1122. // offset to the status
  1123. //
  1124. m_rgBindings->obStatus = Align8(m_rgBindings->cbMaxLen);
  1125. // we'll see how to deal with objects as we know more
  1126. //
  1127. m_rgBindings->pObject = NULL;
  1128. // not used field
  1129. //
  1130. m_rgBindings->pTypeInfo = NULL;
  1131. m_rgBindings->pBindExt = NULL;
  1132. m_rgBindings->dwFlags = 0;
  1133. // Create the accessor
  1134. //
  1135. sc = m_pAcc->CreateAccessor (DBACCESSOR_ROWDATA, // row accessor
  1136. 1, // number of bindings
  1137. m_rgBindings, // array of bindings
  1138. 0, // cbRowSize, not used
  1139. &m_hAcc, // HACCESSOR *
  1140. NULL); // binding status
  1141. if (FAILED(sc))
  1142. goto ret;
  1143. ret:
  1144. return sc;
  1145. }
  1146. SCODE
  1147. CFSSearch::ScMakeQuery()
  1148. {
  1149. SCODE sc = S_OK;
  1150. // Make sure we have a query to play with
  1151. // m_pCommandText is initialized in ScSetSQL, if m_pCommantText
  1152. // is NULL, most likely is becuase ScSetSQL is not called
  1153. //
  1154. if (!PwszSQL() || !m_pCommandText.get())
  1155. {
  1156. sc = E_DAV_NO_QUERY;
  1157. goto ret;
  1158. }
  1159. // Set the command text
  1160. //
  1161. sc = m_pCommandText->SetCommandText (DBGUID_DEFAULT, PwszSQL());
  1162. if (FAILED (sc))
  1163. {
  1164. DebugTrace("pCommandText->SetCommandText failed\n");
  1165. goto ret;
  1166. }
  1167. // Excute the query
  1168. //
  1169. sc = m_pCommandText->Execute (NULL,
  1170. IID_IRowset,
  1171. 0,
  1172. 0,
  1173. reinterpret_cast<IUnknown**>(m_prs.load()));
  1174. if (FAILED(sc) || (!m_prs))
  1175. {
  1176. DebugTrace("pCommandText->Execute failed\n");
  1177. // Munge a few, select error codes
  1178. // Map these errors locally, as they may only come back from Execute
  1179. //
  1180. switch (sc)
  1181. {
  1182. case QUERY_E_FAILED: //$REVIEW: Is this a bad request?
  1183. case QUERY_E_INVALIDQUERY:
  1184. case QUERY_E_INVALIDRESTRICTION:
  1185. case QUERY_E_INVALIDSORT:
  1186. case QUERY_E_INVALIDCATEGORIZE:
  1187. case QUERY_E_ALLNOISE:
  1188. case QUERY_E_TOOCOMPLEX:
  1189. case QUERY_E_TIMEDOUT: //$REVIEW: Is this a bad request?
  1190. case QUERY_E_DUPLICATE_OUTPUT_COLUMN:
  1191. case QUERY_E_INVALID_OUTPUT_COLUMN:
  1192. case QUERY_E_INVALID_DIRECTORY:
  1193. case QUERY_E_DIR_ON_REMOVABLE_DRIVE:
  1194. case QUERY_S_NO_QUERY:
  1195. sc = E_INVALIDARG; // All query errors will be mapped to 400
  1196. break;
  1197. }
  1198. goto ret;
  1199. }
  1200. ret:
  1201. return sc;
  1202. }
  1203. // DAV-Search Implementation -------------------------------------------------
  1204. //
  1205. class CSearchRequest :
  1206. public CMTRefCounted,
  1207. private IAsyncIStreamObserver
  1208. {
  1209. //
  1210. // Reference to the CMethUtil
  1211. //
  1212. auto_ref_ptr<CMethUtil> m_pmu;
  1213. // Contexts
  1214. //
  1215. auto_ref_ptr<CNFSearch> m_pnfs;
  1216. CFSSearch m_csc;
  1217. // Request body as an IStream. This stream is async -- it can
  1218. // return E_PENDING from Read() calls.
  1219. //
  1220. auto_ref_ptr<IStream> m_pstmRequest;
  1221. // The XML parser used to parse the request body using
  1222. // the node factory above.
  1223. //
  1224. auto_ref_ptr<IXMLParser> m_pxprs;
  1225. // IAsyncIStreamObserver
  1226. //
  1227. VOID AsyncIOComplete();
  1228. // State functions
  1229. //
  1230. VOID ParseBody();
  1231. VOID DoSearch();
  1232. VOID SendResponse( SCODE sc );
  1233. // NOT IMPLEMENTED
  1234. //
  1235. CSearchRequest (const CSearchRequest&);
  1236. CSearchRequest& operator= (const CSearchRequest&);
  1237. public:
  1238. // CREATORS
  1239. //
  1240. CSearchRequest(LPMETHUTIL pmu) :
  1241. m_pmu(pmu),
  1242. m_csc(pmu)
  1243. {
  1244. }
  1245. // MANIPULATORS
  1246. //
  1247. VOID Execute();
  1248. };
  1249. VOID
  1250. CSearchRequest::Execute()
  1251. {
  1252. CResourceInfo cri;
  1253. LPCWSTR pwsz;
  1254. LPCWSTR pwszPath = m_pmu->LpwszPathTranslated();
  1255. SCODE sc = S_OK;
  1256. //
  1257. // First off, tell the pmu that we want to defer the response.
  1258. // Even if we send it synchronously (i.e. due to an error in
  1259. // this function), we still want to use the same mechanism that
  1260. // we would use for async.
  1261. //
  1262. m_pmu->DeferResponse();
  1263. // Do ISAPI application and IIS access bits checking
  1264. //
  1265. sc = m_pmu->ScIISCheck (m_pmu->LpwszRequestUrl(), MD_ACCESS_READ);
  1266. if (FAILED(sc))
  1267. {
  1268. // Either the request has been forwarded, or some bad error occurred.
  1269. // In either case, quit here and map the error!
  1270. //
  1271. SendResponse(sc);
  1272. return;
  1273. }
  1274. // Look to see the Content-length - required for this operation
  1275. // to continue.
  1276. //
  1277. //
  1278. if (NULL == m_pmu->LpwszGetRequestHeader (gc_szContent_Length, FALSE))
  1279. {
  1280. pwsz = m_pmu->LpwszGetRequestHeader (gc_szTransfer_Encoding, FALSE);
  1281. if (!pwsz || _wcsicmp (pwsz, gc_wszChunked))
  1282. {
  1283. DavTrace ("Dav: PUT: missing content-length in request\n");
  1284. SendResponse(E_DAV_MISSING_LENGTH);
  1285. return;
  1286. }
  1287. }
  1288. // Search must have a content-type header and value must be text/xml
  1289. //
  1290. sc = ScIsContentTypeXML (m_pmu.get());
  1291. if (FAILED(sc))
  1292. {
  1293. DebugTrace ("Dav: PROPPATCH fails without specifying a text/xml contenttype\n");
  1294. SendResponse(sc);
  1295. return;
  1296. }
  1297. // Check to see if the resource exists
  1298. //
  1299. sc = cri.ScGetResourceInfo (pwszPath);
  1300. if (FAILED (sc))
  1301. {
  1302. SendResponse(sc);
  1303. return;
  1304. }
  1305. // Ensure the URI and resource match
  1306. //
  1307. (void) ScCheckForLocationCorrectness (m_pmu.get(), cri, NO_REDIRECT);
  1308. // Check state headers here.
  1309. //
  1310. sc = HrCheckStateHeaders (m_pmu.get(), pwszPath, FALSE);
  1311. if (FAILED (sc))
  1312. {
  1313. DebugTrace ("DavFS: If-State checking failed.\n");
  1314. SendResponse(sc);
  1315. return;
  1316. }
  1317. // BIG NOTE ABOUT LOCKING
  1318. //
  1319. // The mechanism we actually use to do the search doesn't
  1320. // have any way to access our locked files. So we are punting
  1321. // on supporting locktokens passed into SEARCH.
  1322. // So, for now, on DAVFS, don't bother to check locktokens.
  1323. // (This isn't a big problem because currently DAVFS can only
  1324. // lock single files, not whole directories, AND becuase currently
  1325. // our only locktype is WRITE, so our locks won't prevent the
  1326. // content-indexer from READING the file!)
  1327. //
  1328. // NOTE: We still have to consider if-state-match headers,
  1329. // but that is done elsewhere (above -- HrCheckStateHeaders).
  1330. //
  1331. // Instantiate the XML parser
  1332. //
  1333. m_pnfs.take_ownership(new CNFSearch(m_csc));
  1334. m_pstmRequest.take_ownership(m_pmu->GetRequestBodyIStream(*this));
  1335. sc = ScNewXMLParser( m_pnfs.get(),
  1336. m_pstmRequest.get(),
  1337. m_pxprs.load() );
  1338. if (FAILED(sc))
  1339. {
  1340. DebugTrace( "CSearchRequest::Execute() - ScNewXMLParser() failed (0x%08lX)\n", sc );
  1341. SendResponse(sc);
  1342. return;
  1343. }
  1344. // Start parsing it into the context
  1345. //
  1346. ParseBody();
  1347. }
  1348. VOID
  1349. CSearchRequest::ParseBody()
  1350. {
  1351. Assert( m_pxprs.get() );
  1352. Assert( m_pnfs.get() );
  1353. Assert( m_pstmRequest.get() );
  1354. //
  1355. // Add a ref for the following async operation.
  1356. // Use auto_ref_ptr rather than AddRef() for exception safety.
  1357. //
  1358. auto_ref_ptr<CSearchRequest> pRef(this);
  1359. SCODE sc = ScParseXML (m_pxprs.get(), m_pnfs.get());
  1360. if ( SUCCEEDED(sc) )
  1361. {
  1362. Assert( S_OK == sc || S_FALSE == sc );
  1363. DoSearch();
  1364. }
  1365. else if ( E_PENDING == sc )
  1366. {
  1367. //
  1368. // The operation is pending -- AsyncIOComplete() will take ownership
  1369. // ownership of the reference when it is called.
  1370. //
  1371. pRef.relinquish();
  1372. }
  1373. else
  1374. {
  1375. DebugTrace( "CSearchRequest::ParseBody() - ScParseXML() failed (0x%08lX)\n", sc );
  1376. SendResponse(sc);
  1377. }
  1378. }
  1379. VOID
  1380. CSearchRequest::AsyncIOComplete()
  1381. {
  1382. // Take ownership of the reference added for the async operation.
  1383. //
  1384. auto_ref_ptr<CSearchRequest> pRef;
  1385. pRef.take_ownership(this);
  1386. ParseBody();
  1387. }
  1388. VOID
  1389. CSearchRequest::DoSearch()
  1390. {
  1391. SCODE sc;
  1392. // Do the search
  1393. //
  1394. sc = m_csc.ScMakeQuery();
  1395. if (FAILED (sc))
  1396. {
  1397. SendResponse(sc);
  1398. return;
  1399. }
  1400. // All header must be emitted before chunked XML emitting starts
  1401. //
  1402. m_pmu->SetResponseHeader (gc_szContent_Type, gc_szText_XML);
  1403. // Set the response code and go
  1404. //
  1405. m_pmu->SetResponseCode( HscFromHresult(W_DAV_PARTIAL_SUCCESS),
  1406. NULL,
  1407. 0,
  1408. CSEFromHresult(W_DAV_PARTIAL_SUCCESS) );
  1409. // Emit the results
  1410. //
  1411. auto_ref_ptr<CXMLEmitter> pmsr;
  1412. auto_ref_ptr<CXMLBody> pxb;
  1413. // Get the XML body
  1414. //
  1415. pxb.take_ownership (new CXMLBody (m_pmu.get()));
  1416. pmsr.take_ownership (new CXMLEmitter(pxb.get(), m_csc.PPreloadNamespaces()));
  1417. sc = pmsr->ScSetRoot (gc_wszMultiResponse);
  1418. if (FAILED (sc))
  1419. {
  1420. SendResponse(sc);
  1421. return;
  1422. }
  1423. sc = m_csc.ScEmitResults (*pmsr);
  1424. if (FAILED (sc))
  1425. {
  1426. SendResponse(sc);
  1427. return;
  1428. }
  1429. // Done with the reponse
  1430. //
  1431. pmsr->Done();
  1432. m_pmu->SendCompleteResponse();
  1433. }
  1434. VOID
  1435. CSearchRequest::SendResponse( SCODE sc )
  1436. {
  1437. //
  1438. // Set the response code and go
  1439. //
  1440. m_pmu->SetResponseCode( HscFromHresult(sc), NULL, 0, CSEFromHresult(sc) );
  1441. m_pmu->SendCompleteResponse();
  1442. }
  1443. void
  1444. DAVSearch (LPMETHUTIL pmu)
  1445. {
  1446. auto_ref_ptr<CSearchRequest> pRequest(new CSearchRequest(pmu));
  1447. pRequest->Execute();
  1448. }
  1449. // class CSearchRowsetContext ------------------------------------------------
  1450. //
  1451. enum { CROW_GROUP = 16 };
  1452. // Mapping from DBSTATUS to HSC.
  1453. //
  1454. ULONG
  1455. CSearchRowsetContext::HscFromDBStatus (ULONG ulStatus)
  1456. {
  1457. switch (ulStatus)
  1458. {
  1459. case DBSTATUS_S_OK:
  1460. case DBSTATUS_S_ISNULL:
  1461. case DBSTATUS_S_TRUNCATED:
  1462. case DBSTATUS_S_DEFAULT:
  1463. return HSC_OK;
  1464. case DBSTATUS_E_BADACCESSOR:
  1465. return HSC_BAD_REQUEST;
  1466. case DBSTATUS_E_UNAVAILABLE:
  1467. return HSC_NOT_FOUND;
  1468. case DBSTATUS_E_PERMISSIONDENIED:
  1469. return HSC_UNAUTHORIZED;
  1470. case DBSTATUS_E_DATAOVERFLOW:
  1471. return HSC_INSUFFICIENT_SPACE;
  1472. case DBSTATUS_E_CANTCONVERTVALUE:
  1473. case DBSTATUS_E_SIGNMISMATCH:
  1474. case DBSTATUS_E_CANTCREATE:
  1475. case DBSTATUS_E_INTEGRITYVIOLATION:
  1476. case DBSTATUS_E_SCHEMAVIOLATION:
  1477. case DBSTATUS_E_BADSTATUS:
  1478. // What error shoud these match to?
  1479. // return 400 temporarily.
  1480. //
  1481. return HSC_BAD_REQUEST;
  1482. default:
  1483. TrapSz ("New DBStutus value");
  1484. return HSC_NOT_FOUND;
  1485. }
  1486. }
  1487. SCODE
  1488. CSearchRowsetContext::ScEmitResults (CXMLEmitter& emitter)
  1489. {
  1490. SCODE sc = S_OK;
  1491. BOOL fReadAll = FALSE;
  1492. // Allocate enough space for the data buffer
  1493. //
  1494. if (!m_pData)
  1495. {
  1496. ULONG_PTR cbSize;
  1497. // Get the IAccessor interface, used later to release the accessor
  1498. //
  1499. sc = m_prs->QueryInterface (IID_IAccessor, (LPVOID *)&m_pAcc);
  1500. if (FAILED(sc))
  1501. goto ret;
  1502. // Create the accessor
  1503. //
  1504. sc = ScCreateAccessor();
  1505. if (FAILED(sc))
  1506. goto ret;
  1507. // Calculate the size of the buffer needed by each row.
  1508. // (including a ULONG for status)
  1509. //
  1510. cbSize = Align8(m_rgBindings->cbMaxLen) + Align8(sizeof(ULONG));
  1511. // allocate enough memory for the data buffer on stack
  1512. //
  1513. m_pData = (BYTE *)g_heap.Alloc(cbSize);
  1514. }
  1515. while (!fReadAll)
  1516. {
  1517. sc = m_prs->GetNextRows(NULL, 0, CROW_GROUP, (DBCOUNTITEM *) &m_cHRow, &m_rgHRow);
  1518. if (sc)
  1519. {
  1520. if (sc == DB_S_ENDOFROWSET)
  1521. {
  1522. // we have read all the rows, we'll be done after this loop
  1523. //
  1524. fReadAll = TRUE;
  1525. }
  1526. else
  1527. goto ret;
  1528. }
  1529. if (!m_cHRow)
  1530. {
  1531. // no rows available, this happens when no rows in the rowset at all
  1532. //
  1533. break;
  1534. }
  1535. AssertSz (m_rgHRow, "something really bad happened");
  1536. // For each row we have now, get data and convert it to XML and dump to the stream.
  1537. //
  1538. for (ULONG ihrow = 0; ihrow < m_cHRow; ihrow++)
  1539. {
  1540. AssertSz(m_rgHRow[ihrow], "returned row handle is NULL");
  1541. // get the data of one row.
  1542. //
  1543. sc = m_prs->GetData(m_rgHRow[ihrow], m_hAcc, m_pData);
  1544. if (FAILED(sc) && (sc != DB_E_ERRORSOCCURRED))
  1545. goto ret;
  1546. // Emit the row
  1547. //
  1548. sc = ScEmitRow (emitter);
  1549. if (FAILED(sc))
  1550. goto ret;
  1551. }
  1552. // Don't forget to clean up.
  1553. //
  1554. sc = m_prs->ReleaseRows (m_cHRow, m_rgHRow, NULL, NULL, NULL);
  1555. if (FAILED(sc))
  1556. goto ret;
  1557. // free the memory retured from OLEDB provider with IMalloc::Free
  1558. //
  1559. CoTaskMemFree (m_rgHRow);
  1560. m_rgHRow = NULL;
  1561. m_cHRow = 0;
  1562. }
  1563. ret:
  1564. CleanUp();
  1565. return sc;
  1566. }
  1567. VOID
  1568. CSearchRowsetContext::CleanUp()
  1569. {
  1570. // Try out best to clean up
  1571. // clean the array of HRows
  1572. //
  1573. if (m_rgHRow)
  1574. {
  1575. m_prs->ReleaseRows (m_cHRow, m_rgHRow, NULL, NULL, NULL);
  1576. CoTaskMemFree (m_rgHRow);
  1577. }
  1578. // Release the accessor handle
  1579. //
  1580. if (m_hAcc != DB_INVALID_HACCESSOR)
  1581. {
  1582. m_pAcc->ReleaseAccessor (m_hAcc, NULL);
  1583. }
  1584. }