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.

793 lines
20 KiB

  1. /*===================================================================
  2. Microsoft IIS 5.0 (ASP)
  3. Microsoft Confidential.
  4. Copyright 1998 Microsoft Corporation. All Rights Reserved.
  5. Component: 449 negotiations w/IE
  6. File: ie449.cpp
  7. Owner: DmitryR
  8. This file contains the implementation of the 449 negotiations w/IE
  9. ===================================================================*/
  10. #include "denpre.h"
  11. #pragma hdrstop
  12. #include "ie449.h"
  13. #include "memchk.h"
  14. /*===================================================================
  15. Globals
  16. ===================================================================*/
  17. C449FileMgr *m_p449FileMgr = NULL;
  18. /*===================================================================
  19. Internal funcitons
  20. ===================================================================*/
  21. inline BOOL FindCookie
  22. (
  23. char *szCookiesBuf,
  24. char *szCookie,
  25. DWORD cbCookie
  26. )
  27. {
  28. char *pch = szCookiesBuf;
  29. if (pch == NULL || *pch == '\0')
  30. return FALSE;
  31. while (1)
  32. {
  33. if (strnicmp(pch, szCookie, cbCookie) == 0)
  34. {
  35. if (pch[cbCookie] == '=') // next char must be '='
  36. return TRUE;
  37. }
  38. // next cookie
  39. pch = strchr(pch, ';');
  40. if (pch == NULL)
  41. break;
  42. while (*(++pch) == ' ') // skip ; and any spaces
  43. ;
  44. }
  45. return FALSE;
  46. }
  47. /*===================================================================
  48. The API
  49. ===================================================================*/
  50. /*===================================================================
  51. Init449
  52. ===================================================================*/
  53. HRESULT Init449()
  54. {
  55. // init hash table
  56. m_p449FileMgr = new C449FileMgr;
  57. if (m_p449FileMgr == NULL)
  58. return E_OUTOFMEMORY;
  59. HRESULT hr = m_p449FileMgr->Init();
  60. if (FAILED(hr))
  61. {
  62. delete m_p449FileMgr;
  63. m_p449FileMgr = NULL;
  64. return hr;
  65. }
  66. return S_OK;
  67. }
  68. /*===================================================================
  69. UnInit449
  70. ===================================================================*/
  71. HRESULT UnInit449()
  72. {
  73. if (m_p449FileMgr != NULL)
  74. {
  75. delete m_p449FileMgr;
  76. m_p449FileMgr = NULL;
  77. }
  78. return S_OK;
  79. }
  80. /*===================================================================
  81. Create449Cookie
  82. Get an existing 449 cookie from cache or create a new one
  83. Parameters
  84. szName cookie name
  85. szFile script file
  86. pp449 [out] the cookie
  87. Returns
  88. HRESULT
  89. ===================================================================*/
  90. HRESULT Create449Cookie
  91. (
  92. char *szName,
  93. TCHAR *szFile,
  94. C449Cookie **pp449
  95. )
  96. {
  97. HRESULT hr = S_OK;
  98. // Get the file first
  99. C449File *pFile = NULL;
  100. hr = m_p449FileMgr->GetFile(szFile, &pFile);
  101. if (FAILED(hr))
  102. return hr;
  103. // Create the cookie
  104. hr = C449Cookie::Create449Cookie(szName, pFile, pp449);
  105. if (FAILED(hr))
  106. pFile->Release(); // GetFile gave it addref'd
  107. return hr;
  108. }
  109. /*===================================================================
  110. Do449Processing
  111. Check
  112. if the browser is IE5+
  113. there's no echo-reply: header
  114. all the cookies are present
  115. Construct and send 449 response if needed
  116. When the response is sent, HitObj is marked as 'done with session'
  117. Parameters
  118. pHitObj the request
  119. rgpCookies array of cookie requirements
  120. cCookies number of cookie requirements
  121. Returns
  122. HRESULT
  123. ===================================================================*/
  124. HRESULT Do449Processing
  125. (
  126. CHitObj *pHitObj,
  127. C449Cookie **rgpCookies,
  128. DWORD cCookies
  129. )
  130. {
  131. HRESULT hr = S_OK;
  132. if (cCookies == 0)
  133. return hr;
  134. //////////////////////////////////////////
  135. // check the browser
  136. BOOL fBrowser = FALSE;
  137. char *szBrowser = pHitObj->PIReq()->QueryPszUserAgent();
  138. if (szBrowser == NULL || szBrowser[0] == '\0')
  139. return S_OK; // bad browser
  140. char *szMSIE = strstr(szBrowser, "MSIE ");
  141. if (szMSIE)
  142. {
  143. char chVersion = szMSIE[5];
  144. if (chVersion >= '5' && chVersion <= '9')
  145. fBrowser = TRUE;
  146. }
  147. #ifdef TINYGET449
  148. if (strcmp(szBrowser, "WBCLI") == 0)
  149. fBrowser = TRUE;
  150. #endif
  151. if (!fBrowser) // bad browser
  152. return S_OK;
  153. //////////////////////////////////////////
  154. // check the cookies
  155. char *szCookie = pHitObj->PIReq()->QueryPszCookie();
  156. // collect the arrays of pointers and sizes for not found cookies.
  157. // arrays size to at most number of cookies in the template.
  158. DWORD cNotFound = 0;
  159. DWORD cbNotFound = 0;
  160. STACK_BUFFER( tempCookies, 128 );
  161. if (!tempCookies.Resize(cCookies * sizeof(WSABUF))) {
  162. return E_OUTOFMEMORY;
  163. }
  164. LPWSABUF rgpNotFound = (LPWSABUF)tempCookies.QueryPtr();
  165. for (DWORD i = 0; SUCCEEDED(hr) && (i < cCookies); i++)
  166. {
  167. if (!FindCookie(szCookie, rgpCookies[i]->SzCookie(), rgpCookies[i]->CbCookie()))
  168. {
  169. // cookie not found -- append to the list
  170. hr = rgpCookies[i]->LoadFile();
  171. if (SUCCEEDED(hr)) // ignore bad files
  172. {
  173. rgpNotFound[cNotFound].buf = rgpCookies[i]->SzBuffer();
  174. rgpNotFound[cNotFound].len = rgpCookies[i]->CbBuffer();
  175. cbNotFound += rgpCookies[i]->CbBuffer();
  176. cNotFound++;
  177. }
  178. }
  179. }
  180. if (!SUCCEEDED(hr))
  181. return hr;
  182. if (cNotFound == 0)
  183. return S_OK; // everything's found
  184. //////////////////////////////////////////
  185. // check echo-reply header
  186. char szEcho[80];
  187. DWORD dwEchoLen = sizeof(szEcho);
  188. if (pHitObj->PIReq()->GetServerVariableA("HTTP_MS_ECHO_REPLY", szEcho, &dwEchoLen)
  189. || GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  190. {
  191. return S_OK; // already in response cycle
  192. }
  193. //////////////////////////////////////////
  194. // send the 449 response
  195. CResponse::WriteBlocksResponse
  196. (
  197. pHitObj->PIReq(), // WAM_EXEC_INFO
  198. cNotFound, // number of blocks
  199. rgpNotFound, // array of blocks
  200. cbNotFound, // total number of bytes in blocks
  201. NULL, // text/html
  202. "449 Retry with", // status
  203. "ms-echo-request: execute opaque=\"0\" location=\"BODY\"\r\n" // extra header
  204. );
  205. //////////////////////////////////////////
  206. // tell HitObj no to write anything else
  207. pHitObj->SetDoneWithSession();
  208. return S_OK;
  209. }
  210. /*===================================================================
  211. Do449ChangeNotification
  212. Change notification processing
  213. Parameters
  214. szFile file changed or NULL for all
  215. Returns
  216. HRESULT
  217. ===================================================================*/
  218. HRESULT Do449ChangeNotification
  219. (
  220. TCHAR *szFile
  221. )
  222. {
  223. // if m_p449FileMgr is NULL, we're likely getting called after
  224. // the 449 manager has been UnInited. Return S_OK in this case.
  225. if (m_p449FileMgr == NULL)
  226. return S_OK;
  227. if (szFile)
  228. return m_p449FileMgr->Flush(szFile);
  229. else
  230. return m_p449FileMgr->FlushAll();
  231. }
  232. /*===================================================================
  233. Class C449File
  234. ===================================================================*/
  235. /*===================================================================
  236. C449File::C449File
  237. Constructor
  238. ===================================================================*/
  239. C449File::C449File()
  240. {
  241. m_cRefs = 0;
  242. m_fNeedLoad = 1;
  243. m_szFile = NULL;
  244. m_szBuffer = NULL;
  245. m_cbBuffer = 0;
  246. m_pDME = NULL;
  247. m_hFileReadyForUse=NULL;
  248. m_hrLoadResult= E_FAIL;
  249. }
  250. /*===================================================================
  251. C449File::~C449File
  252. Destructor
  253. ===================================================================*/
  254. C449File::~C449File()
  255. {
  256. Assert(m_cRefs == 0);
  257. if (m_szFile)
  258. free(m_szFile);
  259. if (m_szBuffer)
  260. free(m_szBuffer);
  261. if (m_pDME)
  262. m_pDME->Release();
  263. if(m_hFileReadyForUse != NULL)
  264. CloseHandle(m_hFileReadyForUse);
  265. }
  266. /*===================================================================
  267. C449File::Init
  268. Init strings,
  269. Load file for the first time,
  270. Start change notifications
  271. ===================================================================*/
  272. HRESULT C449File::Init
  273. (
  274. TCHAR *szFile
  275. )
  276. {
  277. // remember the name
  278. m_szFile = StringDup(szFile);
  279. if (m_szFile == NULL)
  280. return E_OUTOFMEMORY;
  281. // init link element
  282. CLinkElem::Init(m_szFile, _tcslen(m_szFile)*sizeof(TCHAR));
  283. // Create event: manual-reset, ready-for-use event; non-signaled
  284. // Better create event before using it.
  285. m_hFileReadyForUse = IIS_CREATE_EVENT(
  286. "C449File::m_hFileReadyForUse",
  287. this,
  288. TRUE, // flag for manual-reset event
  289. FALSE // flag for initial state
  290. );
  291. if (!m_hFileReadyForUse)
  292. return E_OUTOFMEMORY;
  293. // load
  294. m_fNeedLoad = 1;
  295. HRESULT hr = Load();
  296. if (FAILED(hr))
  297. return hr;
  298. // start directory notifications
  299. TCHAR *pch = _tcsrchr(m_szFile, _T('\\')); // last backslash
  300. if (pch == NULL)
  301. return E_FAIL; // bogus filename?
  302. CASPDirMonitorEntry *pDME = NULL;
  303. *pch = _T('\0');
  304. RegisterASPDirMonitorEntry(m_szFile, &pDME);
  305. *pch = _T('\\');
  306. m_pDME = pDME;
  307. // done
  308. return S_OK;
  309. }
  310. /*===================================================================
  311. C449File::Load
  312. Load the file if needed
  313. ===================================================================*/
  314. HRESULT C449File::Load()
  315. {
  316. HRESULT hr = S_OK;
  317. HANDLE hFile = INVALID_HANDLE_VALUE;
  318. HANDLE hMap = NULL;
  319. BYTE *pbBytes = NULL;
  320. DWORD dwSize = 0;
  321. // check if this thread needs to load the file
  322. if (InterlockedExchange(&m_fNeedLoad, 0) == 0)
  323. {
  324. WaitForSingleObject(m_hFileReadyForUse, INFINITE);
  325. return m_hrLoadResult;
  326. }
  327. // cleanup the existing data if any
  328. if (m_szBuffer)
  329. free(m_szBuffer);
  330. m_szBuffer = NULL;
  331. m_cbBuffer = 0;
  332. // open the file
  333. if (SUCCEEDED(hr))
  334. {
  335. hFile = AspCreateFile(
  336. m_szFile,
  337. GENERIC_READ, // access (read-write) mode
  338. FILE_SHARE_READ, // share mode
  339. NULL, // pointer to security descriptor
  340. OPEN_EXISTING, // how to create
  341. FILE_ATTRIBUTE_NORMAL, // file attributes
  342. NULL // handle to file with attributes to copy
  343. );
  344. if (hFile == INVALID_HANDLE_VALUE)
  345. hr = E_FAIL;
  346. }
  347. // get file size
  348. if (SUCCEEDED(hr))
  349. {
  350. dwSize = GetFileSize(hFile, NULL);
  351. if (dwSize == 0 || dwSize == 0xFFFFFFFF)
  352. hr = E_FAIL;
  353. }
  354. // create mapping
  355. if (SUCCEEDED(hr))
  356. {
  357. hMap = CreateFileMapping(
  358. hFile, // handle to file to map
  359. NULL, // optional security attributes
  360. PAGE_READONLY, // protection for mapping object
  361. 0, // high-order 32 bits of object size
  362. 0, // low-order 32 bits of object size
  363. NULL // name of file-mapping object
  364. );
  365. if (hMap == NULL)
  366. hr = E_FAIL;
  367. }
  368. // map the file
  369. if (SUCCEEDED(hr))
  370. {
  371. pbBytes = (BYTE *)MapViewOfFile(
  372. hMap, // file-mapping object to map into address space
  373. FILE_MAP_READ, // access mode
  374. 0, // high-order 32 bits of file offset
  375. 0, // low-order 32 bits of file offset
  376. 0 // number of bytes to map
  377. );
  378. if (pbBytes == NULL)
  379. hr = E_FAIL;
  380. }
  381. // remember the bytes
  382. if (SUCCEEDED(hr))
  383. {
  384. m_szBuffer = (char *)malloc(dwSize);
  385. if (m_szBuffer != NULL)
  386. {
  387. memcpy(m_szBuffer, pbBytes, dwSize);
  388. m_cbBuffer = dwSize;
  389. }
  390. else
  391. {
  392. hr = E_OUTOFMEMORY;
  393. }
  394. }
  395. // cleanup
  396. if (pbBytes != NULL)
  397. UnmapViewOfFile(pbBytes);
  398. if (hMap != NULL)
  399. CloseHandle(hMap);
  400. if (hFile != INVALID_HANDLE_VALUE)
  401. CloseHandle(hFile);
  402. // Set the Need Load flag or flag the read event as successful.
  403. m_hrLoadResult = hr;
  404. SetEvent(m_hFileReadyForUse);
  405. return m_hrLoadResult;
  406. }
  407. /*===================================================================
  408. C449File::Create449File
  409. static constructor
  410. ===================================================================*/
  411. HRESULT C449File::Create449File
  412. (
  413. TCHAR *szFile,
  414. C449File **ppFile
  415. )
  416. {
  417. HRESULT hr = S_OK;
  418. C449File *pFile = new C449File;
  419. if (pFile == NULL)
  420. hr = E_OUTOFMEMORY;
  421. if (SUCCEEDED(hr))
  422. {
  423. hr = pFile->Init(szFile);
  424. }
  425. if (SUCCEEDED(hr))
  426. {
  427. pFile->AddRef();
  428. *ppFile = pFile;
  429. }
  430. else if (pFile)
  431. {
  432. delete pFile;
  433. }
  434. return hr;
  435. }
  436. /*===================================================================
  437. C449File::QueryInterface
  438. C449File::AddRef
  439. C449File::Release
  440. IUnknown members for C449File object.
  441. ===================================================================*/
  442. STDMETHODIMP C449File::QueryInterface(REFIID riid, VOID **ppv)
  443. {
  444. // should never be called
  445. Assert(FALSE);
  446. *ppv = NULL;
  447. return E_NOINTERFACE;
  448. }
  449. STDMETHODIMP_(ULONG) C449File::AddRef()
  450. {
  451. return InterlockedIncrement(&m_cRefs);
  452. }
  453. STDMETHODIMP_(ULONG) C449File::Release()
  454. {
  455. LONG cRefs = InterlockedDecrement(&m_cRefs);
  456. if (cRefs)
  457. return cRefs;
  458. delete this;
  459. return 0;
  460. }
  461. /*===================================================================
  462. Class C449FileMgr
  463. ===================================================================*/
  464. /*===================================================================
  465. C449FileMgr::C449FileMgr
  466. Constructor
  467. ===================================================================*/
  468. C449FileMgr::C449FileMgr()
  469. {
  470. INITIALIZE_CRITICAL_SECTION(&m_csLock);
  471. }
  472. /*===================================================================
  473. C449FileMgr::~C449FileMgr
  474. Destructor
  475. ===================================================================*/
  476. C449FileMgr::~C449FileMgr()
  477. {
  478. FlushAll();
  479. m_ht449Files.UnInit();
  480. DeleteCriticalSection(&m_csLock);
  481. }
  482. /*===================================================================
  483. C449FileMgr::Init
  484. Initialization
  485. ===================================================================*/
  486. HRESULT C449FileMgr::Init()
  487. {
  488. return m_ht449Files.Init(199);
  489. }
  490. /*===================================================================
  491. C449FileMgr::GetFile
  492. Find file in the hash table, or create a new one
  493. ===================================================================*/
  494. HRESULT C449FileMgr::GetFile
  495. (
  496. TCHAR *szFile,
  497. C449File **ppFile
  498. )
  499. {
  500. C449File *pFile = NULL;
  501. CLinkElem *pElem;
  502. Lock();
  503. pElem = m_ht449Files.FindElem(szFile, _tcslen(szFile)*sizeof(TCHAR));
  504. if (pElem)
  505. {
  506. // found
  507. pFile = static_cast<C449File *>(pElem);
  508. if (!SUCCEEDED(pFile->Load()))
  509. pFile = NULL;
  510. else
  511. pFile->AddRef(); // 1 ref to hand out
  512. }
  513. else if (SUCCEEDED(C449File::Create449File(szFile, &pFile)))
  514. {
  515. if (m_ht449Files.AddElem(pFile))
  516. pFile->AddRef(); // 1 for hash table + 1 to hand out
  517. }
  518. UnLock();
  519. *ppFile = pFile;
  520. return (pFile != NULL) ? S_OK : E_FAIL;
  521. }
  522. /*===================================================================
  523. C449FileMgr::Flush
  524. Change notification for a single file
  525. ===================================================================*/
  526. HRESULT C449FileMgr::Flush
  527. (
  528. TCHAR *szFile
  529. )
  530. {
  531. Lock();
  532. CLinkElem *pElem = m_ht449Files.FindElem(szFile, _tcslen(szFile)*sizeof(TCHAR));
  533. if (pElem)
  534. {
  535. C449File *pFile = static_cast<C449File *>(pElem);
  536. pFile->SetNeedLoad(); // next time reload
  537. }
  538. UnLock();
  539. return S_OK;
  540. }
  541. /*===================================================================
  542. C449FileMgr::FlushAll
  543. Remove all files
  544. FlushAll is always together with template flush
  545. ===================================================================*/
  546. HRESULT C449FileMgr::FlushAll()
  547. {
  548. // Unlink from hash table first
  549. Lock();
  550. CLinkElem *pElem = m_ht449Files.Head();
  551. m_ht449Files.ReInit();
  552. UnLock();
  553. // Walk the list to remove all
  554. while (pElem)
  555. {
  556. C449File *pFile = static_cast<C449File *>(pElem);
  557. pElem = pElem->m_pNext;
  558. pFile->Release();
  559. }
  560. return S_OK;
  561. }
  562. /*===================================================================
  563. Class C449Cookie
  564. ===================================================================*/
  565. /*===================================================================
  566. C449Cookie::C449Cookie
  567. Constructor
  568. ===================================================================*/
  569. C449Cookie::C449Cookie()
  570. {
  571. m_cRefs = 0;
  572. m_szName = NULL;
  573. m_cbName = 0;
  574. m_pFile = NULL;
  575. }
  576. /*===================================================================
  577. C449Cookie::~C449Cookie
  578. Destructor
  579. ===================================================================*/
  580. C449Cookie::~C449Cookie()
  581. {
  582. Assert(m_cRefs == 0);
  583. if (m_szName)
  584. free(m_szName);
  585. if (m_pFile)
  586. m_pFile->Release();
  587. }
  588. /*===================================================================
  589. C449Cookie::Init
  590. Initialize
  591. ===================================================================*/
  592. HRESULT C449Cookie::Init
  593. (
  594. char *szName,
  595. C449File *pFile
  596. )
  597. {
  598. m_szName = StringDupA(szName);
  599. if (m_szName == NULL)
  600. return E_OUTOFMEMORY;
  601. m_cbName = strlen(m_szName);
  602. m_pFile = pFile;
  603. return S_OK;
  604. }
  605. /*===================================================================
  606. C449Cookie::Create449Cookie
  607. static constructor
  608. ===================================================================*/
  609. HRESULT C449Cookie::Create449Cookie
  610. (
  611. char *szName,
  612. C449File *pFile,
  613. C449Cookie **pp449Cookie
  614. )
  615. {
  616. HRESULT hr = S_OK;
  617. C449Cookie *pCookie = new C449Cookie;
  618. if (pCookie == NULL)
  619. hr = E_OUTOFMEMORY;
  620. if (SUCCEEDED(hr))
  621. {
  622. hr = pCookie->Init(szName, pFile);
  623. }
  624. if (SUCCEEDED(hr))
  625. {
  626. pCookie->AddRef();
  627. *pp449Cookie = pCookie;
  628. }
  629. else if (pCookie)
  630. {
  631. delete pCookie;
  632. }
  633. return hr;
  634. }
  635. /*===================================================================
  636. C449Cookie::QueryInterface
  637. C449Cookie::AddRef
  638. C449Cookie::Release
  639. IUnknown members for C449Cookie object.
  640. ===================================================================*/
  641. STDMETHODIMP C449Cookie::QueryInterface(REFIID riid, VOID **ppv)
  642. {
  643. // should never be called
  644. Assert(FALSE);
  645. *ppv = NULL;
  646. return E_NOINTERFACE;
  647. }
  648. STDMETHODIMP_(ULONG) C449Cookie::AddRef()
  649. {
  650. return InterlockedIncrement(&m_cRefs);
  651. }
  652. STDMETHODIMP_(ULONG) C449Cookie::Release()
  653. {
  654. LONG cRefs = InterlockedDecrement(&m_cRefs);
  655. if (cRefs)
  656. return cRefs;
  657. delete this;
  658. return 0;
  659. }