Source code of Windows XP (NT5)
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.

4673 lines
122 KiB

  1. /*===================================================================
  2. Microsoft Denali
  3. Microsoft Confidential.
  4. Copyright 1996 Microsoft Corporation. All Rights Reserved.
  5. Component: Response object
  6. File: response.cpp
  7. Owner: CGrant
  8. This file contains the code for the implementation of the Response object.
  9. ===================================================================*/
  10. #include "denpre.h"
  11. #pragma hdrstop
  12. #include "response.h"
  13. #include "request.h"
  14. #include "Cookies.h"
  15. #include "perfdata.h"
  16. #include "winsock2.h"
  17. #include "memchk.h"
  18. #pragma warning (disable: 4355) // ignore: "'this' used in base member init
  19. static const char s_szContentLengthHeader[] = "Content-Length: ";
  20. static const char s_szContentTypeHeader[] = "Content-Type: ";
  21. static const char s_szCharSetHTML[] = "; Charset=";
  22. static const char s_szCacheControl[] = "Cache-control: ";
  23. static const char s_szCacheControlPrivate[] = "Cache-control: private\r\n";
  24. static const char s_szTransferEncoding[] = "Transfer-Encoding: chunked\r\n";
  25. static const char s_szHTML[] = "text/html";
  26. static const char s_szCDF[] = "application/x-cdf";
  27. static const char s_szDefaultStatus[] = "200 OK";
  28. inline void AddtoTotalByteOut(int cByteOut)
  29. {
  30. #ifndef PERF_DISABLE
  31. g_PerfData.Add_REQTOTALBYTEOUT(cByteOut);
  32. #endif
  33. }
  34. inline const char *GetResponseMimeType(CIsapiReqInfo *pIReq)
  35. {
  36. TCHAR *szPath = pIReq->QueryPszPathTranslated();
  37. DWORD cch = pIReq->QueryCchPathTranslated();
  38. if (cch > 4 && _tcscmp(szPath + cch - 4, _T(".CDX")) == 0)
  39. {
  40. return s_szCDF;
  41. }
  42. else
  43. {
  44. return s_szHTML;
  45. }
  46. }
  47. /*
  48. *
  49. *
  50. *
  51. * C R e s p o n s e C o o k i e s
  52. *
  53. *
  54. *
  55. */
  56. //===================================================================
  57. // CResponseCookies::CResponseCookies
  58. //
  59. // Constructor.
  60. //===================================================================
  61. CResponseCookies::CResponseCookies(CResponse *pResponse, IUnknown *pUnkOuter)
  62. : m_ISupportErrImp(this, pUnkOuter, IID_IRequestDictionary)
  63. {
  64. m_punkOuter = pUnkOuter;
  65. if (pResponse)
  66. pResponse->AddRef();
  67. m_pResponse = pResponse;
  68. m_pRequest = NULL;
  69. CDispatch::Init(IID_IRequestDictionary);
  70. }
  71. //===================================================================
  72. // CResponseCookies::~CResponseCookies
  73. //
  74. // Destructor.
  75. //===================================================================
  76. CResponseCookies::~CResponseCookies()
  77. {
  78. if (m_pRequest)
  79. m_pRequest->Release();
  80. if (m_pResponse)
  81. m_pResponse->Release();
  82. }
  83. //===================================================================
  84. // CResponseCookies::ReInit
  85. //
  86. // Parameters:
  87. // pRequest - pointer to the request object. Will need it to
  88. // read the request for the cookies
  89. //
  90. // Returns:
  91. // always S_OK, unlest pRequest is NULL.
  92. //===================================================================
  93. HRESULT CResponseCookies::ReInit(CRequest *pRequest)
  94. {
  95. if (pRequest)
  96. pRequest->AddRef();
  97. if (m_pRequest)
  98. m_pRequest->Release();
  99. m_pRequest = pRequest; // CRequest is not ref counted, so no need for AddRef/Release
  100. if (m_pRequest == NULL)
  101. return E_POINTER;
  102. return S_OK;
  103. }
  104. /*===================================================================
  105. CResponseCookies::QueryInterface
  106. CResponseCookies::AddRef
  107. CResponseCookies::Release
  108. IUnknown members for CResponseCookies object.
  109. ===================================================================*/
  110. STDMETHODIMP CResponseCookies::QueryInterface(const IID &idInterface, void **ppvObj)
  111. {
  112. *ppvObj = NULL;
  113. if (idInterface == IID_IUnknown || idInterface == IID_IRequestDictionary || idInterface == IID_IDispatch)
  114. *ppvObj = this;
  115. else if (idInterface == IID_ISupportErrorInfo)
  116. *ppvObj = &m_ISupportErrImp;
  117. if (*ppvObj != NULL)
  118. {
  119. static_cast<IUnknown *>(*ppvObj)->AddRef();
  120. return S_OK;
  121. }
  122. return ResultFromScode(E_NOINTERFACE);
  123. }
  124. STDMETHODIMP_(ULONG) CResponseCookies::AddRef()
  125. {
  126. return m_punkOuter->AddRef();
  127. }
  128. STDMETHODIMP_(ULONG) CResponseCookies::Release()
  129. {
  130. return m_punkOuter->Release();
  131. }
  132. /*===================================================================
  133. CResponseCookies::get_Item
  134. Function called from DispInvoke to get values from the Response.Cookies
  135. collection. If the Cookie does not exist, then a new one is created
  136. and added to the Request dictionary
  137. Parameters:
  138. varKey VARIANT [in], which parameter to get the value of - Empty means whole collection
  139. pvarReturn VARIANT *, [out] value of the requested parameter
  140. Returns:
  141. S_OK on success, E_FAIL on failure.
  142. ===================================================================*/
  143. HRESULT CResponseCookies::get_Item(VARIANT varKey, VARIANT *pvarReturn)
  144. {
  145. if (FAILED(m_pResponse->CheckForTombstone()))
  146. return E_FAIL;
  147. char *szKey; // ascii value of 'varKey'
  148. CRequestHit *pRequestHit; // pointer to request bucket
  149. DWORD vt = 0; // Variant type of key
  150. CWCharToMBCS convKey;
  151. if (m_pResponse->FHeadersWritten())
  152. {
  153. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  154. return E_FAIL;
  155. }
  156. // Initialize things
  157. //
  158. V_VT(pvarReturn) = VT_DISPATCH;
  159. V_DISPATCH(pvarReturn) = NULL;
  160. VARIANT *pvarKey = &varKey;
  161. HRESULT hrReturn = S_OK;
  162. // BUG 937: VBScript passes VT_VARIANT|VT_BYREF when passing obect
  163. // produced by IEnumVariant
  164. //
  165. // Use VariantResolveDispatch which will:
  166. //
  167. // * Copy BYREF variants for us using VariantCopyInd
  168. // * handle E_OUTOFMEMORY for us
  169. // * get the default value from an IDispatch, which seems
  170. // like an appropriate conversion.
  171. //
  172. VARIANT varKeyCopy;
  173. VariantInit(&varKeyCopy);
  174. vt = V_VT(pvarKey);
  175. if ((vt != VT_BSTR) && (vt != VT_I2) && (vt != VT_I4))
  176. {
  177. if (FAILED(VariantResolveDispatch(&varKeyCopy, &varKey, IID_IRequestDictionary, IDE_REQUEST)))
  178. goto LExit;
  179. pvarKey = &varKeyCopy;
  180. }
  181. vt = V_VT(pvarKey);
  182. switch(vt)
  183. {
  184. // Bug 95201 support all numberic sub-types
  185. case VT_I1: case VT_I2: case VT_I8:
  186. case VT_UI1: case VT_UI2: case VT_UI4: case VT_UI8:
  187. case VT_R4: case VT_R8:
  188. // Coerce all integral types to VT_I4
  189. if (FAILED(hrReturn = VariantChangeType(pvarKey, pvarKey, 0, VT_I4)))
  190. goto LExit;
  191. // fallthru to VT_I4
  192. case VT_I4:
  193. case VT_BSTR:
  194. break;
  195. default:
  196. ExceptionId(IID_IRequestDictionary, IDE_COOKIE, IDE_EXPECTING_STR);
  197. hrReturn = E_FAIL;
  198. goto LExit;
  199. }
  200. if (FAILED(m_pRequest->CheckForTombstone()))
  201. {
  202. hrReturn = E_FAIL;
  203. goto LExit;
  204. }
  205. if (m_pRequest->m_pData->m_fLoadCookies)
  206. {
  207. char *szCookie = m_pRequest->GetIReq()->QueryPszCookie();
  208. if (FAILED(hrReturn = m_pRequest->LoadVariables(COOKIE, szCookie, m_pRequest->GetCodePage())))
  209. goto LExit;
  210. m_pRequest->m_pData->m_fLoadCookies = FALSE;
  211. }
  212. if (vt == VT_BSTR)
  213. {
  214. if (FAILED(hrReturn = convKey.Init(V_BSTR(pvarKey),m_pRequest->GetCodePage()))) {
  215. if (hrReturn == E_OUTOFMEMORY) {
  216. ExceptionId(IID_IResponse, IDE_COOKIE, IDE_OOM);
  217. goto LExit;
  218. }
  219. hrReturn = NO_ERROR;
  220. szKey = "";
  221. }
  222. else {
  223. szKey = convKey.GetString();
  224. }
  225. // Bug 456: Don't allow assignment to DenaliSessionID
  226. if (strncmp(szKey, SZ_SESSION_ID_COOKIE_PREFIX, CCH_SESSION_ID_COOKIE_PREFIX) == 0)
  227. {
  228. ExceptionId(IID_IResponse, IDE_COOKIE, IDE_RESPONSE_MODIFY_SESS_COOKIE);
  229. hrReturn = E_FAIL;
  230. goto LExit;
  231. }
  232. pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->FindElem(szKey, strlen(szKey)));
  233. }
  234. else
  235. {
  236. // Look up item by index
  237. int iCount = 0;
  238. if (vt == VT_I2)
  239. {
  240. iCount = V_I2(pvarKey);
  241. }
  242. else
  243. {
  244. iCount = V_I4(pvarKey);
  245. }
  246. // The Request hits for all cookies are stored with the request object
  247. if ((iCount < 1) || (iCount > (int) m_pRequest->m_pData->m_Cookies.m_dwCount))
  248. {
  249. hrReturn = E_FAIL;
  250. goto LExit;
  251. }
  252. pRequestHit = m_pRequest->m_pData->m_Cookies.m_rgRequestHit[iCount - 1];
  253. }
  254. if (pRequestHit)
  255. {
  256. CCookie *pDictionary = pRequestHit->m_pCookieData;
  257. if (pDictionary == NULL)
  258. goto LNotFound;
  259. if (FAILED(pDictionary->QueryInterface(IID_IWriteCookie, reinterpret_cast<void **>(&V_DISPATCH(pvarReturn)))))
  260. Assert (FALSE);
  261. goto LExit;
  262. }
  263. LNotFound:
  264. // don't allow empty cookie names
  265. //
  266. if (*szKey == '\0')
  267. {
  268. ExceptionId(IID_IResponse, IDE_COOKIE, IDE_COOKIE_NO_NAME);
  269. hrReturn = E_FAIL;
  270. goto LExit;
  271. }
  272. // Create a new RequestHit if there is no key by this name
  273. if (pRequestHit == NULL)
  274. {
  275. pRequestHit = new CRequestHit;
  276. if (pRequestHit == NULL || FAILED(pRequestHit->Init(szKey, TRUE)))
  277. {
  278. if (pRequestHit)
  279. delete pRequestHit;
  280. ExceptionId(IID_IResponse, IDE_COOKIE, IDE_OOM);
  281. hrReturn = E_OUTOFMEMORY;
  282. goto LExit;
  283. }
  284. m_pRequest->GetStrings()->AddElem(pRequestHit);
  285. }
  286. // Create a new cookie, with an initial unassigned value.
  287. if (pRequestHit->m_pCookieData == NULL)
  288. {
  289. pRequestHit->m_pCookieData = new CCookie(m_pResponse->GetIReq(),m_pRequest->GetCodePage());
  290. if (pRequestHit->m_pCookieData == NULL || FAILED(pRequestHit->m_pCookieData->Init()))
  291. {
  292. ExceptionId(IID_IResponse, IDE_COOKIE, IDE_OOM);
  293. hrReturn = E_OUTOFMEMORY;
  294. goto LExit;
  295. }
  296. }
  297. // Add this Request hit to the ResponseCookies array of hits
  298. if (!m_pRequest->m_pData->m_Cookies.AddRequestHit(pRequestHit))
  299. {
  300. return E_OUTOFMEMORY;
  301. }
  302. // Query for IWriteCookie
  303. if (FAILED(pRequestHit->m_pCookieData->QueryInterface(IID_IWriteCookie, reinterpret_cast<void **>(&V_DISPATCH(pvarReturn)))))
  304. {
  305. Assert (FALSE);
  306. }
  307. LExit:
  308. VariantClear(&varKeyCopy);
  309. return hrReturn;
  310. }
  311. /*===================================================================
  312. CResponseCookies::get_Count
  313. Parameters:
  314. pcValues - count is stored in *pcValues
  315. ===================================================================*/
  316. STDMETHODIMP CResponseCookies::get_Count(int *pcValues)
  317. {
  318. if (FAILED(m_pRequest->CheckForTombstone()))
  319. return E_FAIL;
  320. return m_pRequest->m_pData->m_Cookies.get_Count(pcValues);
  321. }
  322. /*===================================================================
  323. CResponseCookies::get_Key
  324. Function called from DispInvoke to get keys from the response cookie collection.
  325. Parameters:
  326. vKey VARIANT [in], which parameter to get the key of
  327. pvarReturn VARIANT *, [out] value of the requested parameter
  328. Returns:
  329. S_OK on success, E_FAIL on failure.
  330. ===================================================================*/
  331. HRESULT CResponseCookies::get_Key(VARIANT varKey, VARIANT *pVar)
  332. {
  333. if (FAILED(m_pRequest->CheckForTombstone()))
  334. return E_FAIL;
  335. return m_pRequest->m_pData->m_Cookies.get_Key(varKey, pVar);
  336. }
  337. /*===================================================================
  338. CResponseCookies::get__NewEnum
  339. Return a new enumerator
  340. ===================================================================*/
  341. HRESULT CResponseCookies::get__NewEnum(IUnknown **ppEnumReturn)
  342. {
  343. if (FAILED(m_pResponse->CheckForTombstone()))
  344. return E_FAIL;
  345. *ppEnumReturn = NULL;
  346. CRequestIterator *pIterator = new CRequestIterator(m_pRequest, COOKIE);
  347. if (pIterator == NULL)
  348. return E_OUTOFMEMORY;
  349. HRESULT hrInit = pIterator->Init();
  350. if (FAILED(hrInit))
  351. {
  352. delete pIterator;
  353. return hrInit;
  354. }
  355. *ppEnumReturn = pIterator;
  356. return S_OK;
  357. }
  358. /*===================================================================
  359. CResponseCookies::QueryHeaderSize
  360. Returns:
  361. returns the number of bytes required for the cookie headers.
  362. ===================================================================*/
  363. size_t CResponseCookies::QueryHeaderSize()
  364. {
  365. if (FAILED(m_pRequest->CheckForTombstone()))
  366. return 0;
  367. int cbHeaders = 0;
  368. for (CRequestHit *pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->Head());
  369. pRequestHit != NULL;
  370. pRequestHit = static_cast<CRequestHit *>(pRequestHit->m_pNext))
  371. {
  372. CCookie *pCookie = pRequestHit->m_pCookieData;
  373. if (pCookie == NULL || !pCookie->IsDirty())
  374. continue;
  375. // add two bytes for '\r\n'
  376. //
  377. // CCookie::GetCookieHeaderSize adds one byte for NUL terminator, so
  378. // just add one byte here.
  379. //
  380. // CResponse::WriteHeaders does not want to know about the NUL yet.
  381. //
  382. cbHeaders += pCookie->GetCookieHeaderSize(reinterpret_cast<char *>(pRequestHit->m_pKey)) + 1;
  383. }
  384. return cbHeaders;
  385. }
  386. /*===================================================================
  387. CResponseCookies::GetHeaders
  388. Parameters:
  389. szBuffer - contains the destination buffer for the cookie header
  390. text
  391. Returns:
  392. return a pointer to the NUL character in the destination
  393. ===================================================================*/
  394. char *CResponseCookies::GetHeaders(char *szBuffer)
  395. {
  396. if (FAILED(m_pRequest->CheckForTombstone()))
  397. {
  398. szBuffer[0] = '\0';
  399. return szBuffer;
  400. }
  401. for (CRequestHit *pRequestHit = static_cast<CRequestHit *>(m_pRequest->GetStrings()->Head());
  402. pRequestHit != NULL;
  403. pRequestHit = static_cast<CRequestHit *>(pRequestHit->m_pNext))
  404. {
  405. CCookie *pCookie = pRequestHit->m_pCookieData;
  406. if (pCookie == NULL || !pCookie->IsDirty())
  407. continue;
  408. szBuffer = pCookie->GetCookieHeader(reinterpret_cast<char *>(pRequestHit->m_pKey), szBuffer);
  409. szBuffer = strcpyExA(szBuffer, "\r\n");
  410. }
  411. return szBuffer;
  412. }
  413. /*
  414. *
  415. *
  416. *
  417. * C R e s p o n s e B u f f e r
  418. *
  419. *
  420. *
  421. */
  422. /*===================================================================
  423. The CResponseBuffer object maintains an array of buffers.
  424. If buffering is turned on, the Response.Write and Response.WriteBlock
  425. methods will write to the buffers in these arrays rather then directly
  426. back to the client. Response.Flush writes the content of the buffers to
  427. the client and then frees the buffers. Response.Clear frees the buffers without
  428. writing to the client
  429. ====================================================================*/
  430. /*===================================================================
  431. CResponseBuffer::CResponseBuffer
  432. Constructor
  433. Parameters:
  434. None
  435. Returns:
  436. Nothing
  437. Side Effects
  438. None
  439. ===================================================================*/
  440. CResponseBuffer::CResponseBuffer()
  441. {
  442. m_pResponse = NULL;
  443. m_rgpchBuffers = &m_pchBuffer0;
  444. m_cBufferPointers = 1;
  445. m_pchBuffer0 = NULL;
  446. m_cBuffers = 0;
  447. m_iCurrentBuffer = 0;
  448. m_cchOffsetInCurrentBuffer = 0;
  449. m_cchTotalBuffered = 0;
  450. m_fInited = FALSE;
  451. }
  452. /*===================================================================
  453. CResponseBuffer::Init
  454. Initializes the CResponseBuffer object
  455. Parameters:
  456. None
  457. Returns:
  458. S_OK Success
  459. E_OUTOFMEMORY Failure
  460. Side Effects
  461. Allocates memory
  462. ===================================================================*/
  463. HRESULT CResponseBuffer::Init(CResponse* pResponse)
  464. {
  465. Assert(pResponse);
  466. // Set the pointer to the enclosing response object
  467. m_pResponse = pResponse;
  468. m_fInited = TRUE;
  469. return S_OK;
  470. }
  471. /*===================================================================
  472. CResponseBuffer::~CResponseBuffer
  473. Destructor
  474. Parameters:
  475. None
  476. Returns:
  477. Nothing
  478. Side Effects
  479. Frees memory
  480. ===================================================================*/
  481. CResponseBuffer::~CResponseBuffer()
  482. {
  483. Assert(m_rgpchBuffers);
  484. // Free all the buffers we've allocated
  485. for (DWORD i = 0; i < m_cBuffers; i++)
  486. {
  487. if (m_rgpchBuffers[i])
  488. {
  489. ACACHE_FSA_FREE(ResponseBuffer, m_rgpchBuffers[i]);
  490. }
  491. }
  492. // Free the array of buffer pointers
  493. // (only if allocated - doesn't point to the member pointer
  494. if (m_cBufferPointers > 1)
  495. free(m_rgpchBuffers);
  496. }
  497. /*===================================================================
  498. CResponseBuffer::GrowBuffers
  499. Increases available buffer space
  500. Parameters:
  501. cchNewRequest count of bytes to be accomodated
  502. Returns:
  503. HRESULT Indicating success or type of failure
  504. Side Effects
  505. May cause memory to be allocated
  506. ===================================================================*/
  507. HRESULT CResponseBuffer::GrowBuffers(DWORD cchNewRequest)
  508. {
  509. Assert(m_fInited);
  510. // Calculate how many more buffers are needed
  511. DWORD cAddBuffers = (cchNewRequest+RESPONSE_BUFFER_SIZE-1)/RESPONSE_BUFFER_SIZE;
  512. // Always at least one must be there already
  513. Assert(m_rgpchBuffers);
  514. Assert(m_cBufferPointers);
  515. // Allocate more buffer pointers if needed
  516. if (cAddBuffers > (m_cBufferPointers - m_cBuffers)) // doesn't fit?
  517. {
  518. char **rgpchTmp;
  519. DWORD cNewBufferPointers = m_cBufferPointers + cAddBuffers + BUFFERS_INCREMENT;
  520. if (m_cBufferPointers == 1)
  521. rgpchTmp = (char **)malloc(cNewBufferPointers*sizeof(char *));
  522. else
  523. rgpchTmp = (char **)realloc(m_rgpchBuffers, cNewBufferPointers*sizeof(char *));
  524. if (!rgpchTmp)
  525. return E_OUTOFMEMORY;
  526. // preserve the first buffer pointer in the special case
  527. // of m_rgpchBuffers initally pointing to a member buffer pointer
  528. if (m_cBufferPointers == 1)
  529. rgpchTmp[0] = m_rgpchBuffers[0];
  530. m_rgpchBuffers = rgpchTmp;
  531. m_cBufferPointers = cNewBufferPointers;
  532. }
  533. // Allocate the new buffers
  534. for (DWORD i = 0; i < cAddBuffers; i++)
  535. {
  536. char *pchTmp = (char *)ACACHE_FSA_ALLOC(ResponseBuffer);
  537. if (!pchTmp)
  538. return E_OUTOFMEMORY;
  539. m_rgpchBuffers[m_cBuffers++] = pchTmp;
  540. }
  541. return S_OK;
  542. }
  543. /*===================================================================
  544. CResponseBuffer::Write
  545. Writes data to the CResponseBuffer object. We first write
  546. a data structure that describes this segment of the buffer.
  547. The data structure identifies which method is doing the
  548. writing, and contains an index to the starting buffer,
  549. the starting offset in that buffer, and the length of the
  550. data. The data itself is then writen to one or more buffers.
  551. New buffers are allocated as needed
  552. Parameters:
  553. szSource pointer to buffer to read into the Response buffer
  554. cch count of bytes to be read into the Response buffer
  555. Returns:
  556. HRESULT Indicating success or type of failure
  557. Side Effects
  558. May cause memory to be allocated
  559. ===================================================================*/
  560. HRESULT CResponseBuffer::Write(char* szSource, DWORD cch)
  561. {
  562. HRESULT hr = S_OK;
  563. Assert(m_fInited);
  564. // Caclulate how much buffer space we have left
  565. DWORD cchBufferRemaining;
  566. if (m_cBuffers)
  567. cchBufferRemaining = RESPONSE_BUFFER_SIZE - m_cchOffsetInCurrentBuffer;
  568. else
  569. cchBufferRemaining = 0;
  570. // Check if enough space is left in the current buffer
  571. if (cch <= cchBufferRemaining)
  572. {
  573. // Enough space available, copy data to buffer
  574. memcpy(m_rgpchBuffers[m_iCurrentBuffer] + m_cchOffsetInCurrentBuffer, szSource, cch);
  575. m_cchOffsetInCurrentBuffer += cch;
  576. m_cchTotalBuffered += cch;
  577. }
  578. else
  579. {
  580. // Not enough space in current buffer, allocate more buffers
  581. hr = GrowBuffers(cch - cchBufferRemaining);
  582. if (FAILED(hr))
  583. {
  584. goto lRet;
  585. }
  586. // Copy data to the buffers, we loop to handle
  587. // the case where the data is larger then the buffer size
  588. while (cch)
  589. {
  590. if (RESPONSE_BUFFER_SIZE == m_cchOffsetInCurrentBuffer)
  591. {
  592. m_iCurrentBuffer++;
  593. m_cchOffsetInCurrentBuffer = 0;
  594. }
  595. DWORD cchToCopy = min(cch, (RESPONSE_BUFFER_SIZE - m_cchOffsetInCurrentBuffer));
  596. memcpy(m_rgpchBuffers[m_iCurrentBuffer] + m_cchOffsetInCurrentBuffer, szSource, cchToCopy);
  597. m_cchOffsetInCurrentBuffer += cchToCopy;
  598. szSource += cchToCopy;
  599. cch -= cchToCopy;
  600. m_cchTotalBuffered += cchToCopy;
  601. }
  602. }
  603. lRet:
  604. return(hr);
  605. }
  606. /*===================================================================
  607. CResponseBuffer::Clear
  608. Deletes all information currently in the buffers, and restores
  609. the buffer array to it's starting state.
  610. Parameters:
  611. None
  612. Returns:
  613. S_OK success
  614. Side Effects
  615. May free memory
  616. ===================================================================*/
  617. HRESULT CResponseBuffer::Clear()
  618. {
  619. Assert(m_fInited);
  620. if (m_cBuffers == 0)
  621. return S_OK;
  622. // Free all but the first of the allocated buffers
  623. for (DWORD i = 1; i < m_cBuffers; i++)
  624. {
  625. ACACHE_FSA_FREE(ResponseBuffer, m_rgpchBuffers[i]);
  626. m_rgpchBuffers[i] = NULL;
  627. }
  628. m_cBuffers = 1;
  629. m_iCurrentBuffer = 0;
  630. m_cchOffsetInCurrentBuffer = 0;
  631. m_cchTotalBuffered = 0;
  632. return S_OK;
  633. }
  634. /*===================================================================
  635. CResponseBuffer::Flush
  636. Writes all data in the buffer to the client. We walk through
  637. the array of descriptions of buffer requests. The description
  638. includes which method was responsible for the request. If WriteBlock
  639. made the request, then we can just hand WriteClient the pointer to the HTML
  640. block and its length. If Write made the request, we find the starting buffer
  641. and offset from the request description, walk through the array of buffers until
  642. all of the data from the request has been writen to the client.
  643. After all data in the buffers has been writen to the client the buffers
  644. are freed.
  645. Parameters:
  646. Pointer to CIsapiReqInfo
  647. Flag indicating whether this is in response to a head request
  648. Returns:
  649. S_OK success
  650. Side Effects
  651. May free memory
  652. ===================================================================*/
  653. HRESULT CResponseBuffer::Flush(CIsapiReqInfo *pIReq)
  654. {
  655. HRESULT hr = S_OK;
  656. Assert(m_fInited);
  657. Assert(pIReq);
  658. // If no buffers are allocated, no point in going any further
  659. if (m_cchTotalBuffered == 0)
  660. return S_OK;
  661. // If this is not a head request transmit the buffer contents to the client
  662. if (!m_pResponse->IsHeadRequest())
  663. {
  664. // Write out all the buffers
  665. for (DWORD i = 0; i <= m_iCurrentBuffer; i++)
  666. {
  667. DWORD cchToWrite = (i == m_iCurrentBuffer) ?
  668. m_cchOffsetInCurrentBuffer : RESPONSE_BUFFER_SIZE;
  669. if (FAILED(CResponse::SyncWrite(pIReq, m_rgpchBuffers[i], cchToWrite)))
  670. {
  671. // Failed to write to the client,
  672. // further ouput to client is futile
  673. m_pResponse->m_pData->m_fWriteClientError = TRUE;
  674. break;
  675. }
  676. }
  677. }
  678. Clear();
  679. return hr;
  680. }
  681. /*
  682. *
  683. *
  684. *
  685. * C D e b u g R e s p o n s e B u f f e r
  686. *
  687. *
  688. *
  689. */
  690. /*===================================================================
  691. CDebugResponseBuffer::AppendRecord
  692. Create client side debugger metadata record and appends it to
  693. the buffer
  694. Parameters:
  695. Returns:
  696. HRESULT Indicating success or type of failure
  697. ===================================================================*/
  698. HRESULT CDebugResponseBuffer::AppendRecord
  699. (
  700. const int cchBlockOffset,
  701. const int cchBlockLength,
  702. const int cchSourceOffset,
  703. const char *pszSourceFile
  704. )
  705. {
  706. HRESULT hr = S_OK;
  707. #define CCH_METADATA_RECORD_MAX 40 // without filename
  708. if (pszSourceFile)
  709. {
  710. char *pszBuf = new char [strlen(pszSourceFile) +
  711. CCH_METADATA_RECORD_MAX + 1];
  712. if (pszBuf)
  713. {
  714. sprintf(pszBuf, "%d,%d,%d,%s\r\n",
  715. cchBlockOffset, cchBlockLength, cchSourceOffset,
  716. pszSourceFile);
  717. hr = Write(pszBuf);
  718. delete [] pszBuf;
  719. }
  720. else
  721. {
  722. hr = E_OUTOFMEMORY;
  723. }
  724. }
  725. else
  726. {
  727. char szBuf[CCH_METADATA_RECORD_MAX+1];
  728. sprintf(szBuf, "%d,%d,%d\r\n",
  729. cchBlockOffset, cchBlockLength, cchSourceOffset);
  730. hr = Write(szBuf);
  731. }
  732. #undef CCH_METADATA_RECORD_MAX
  733. return hr;
  734. }
  735. /*
  736. *
  737. *
  738. *
  739. * C H T T P H e a d e r
  740. *
  741. *
  742. *
  743. */
  744. /*===================================================================
  745. CHTTPHeader::CHTTPHeader
  746. Constructor.
  747. ===================================================================*/
  748. CHTTPHeader::CHTTPHeader()
  749. :
  750. m_fInited(FALSE),
  751. m_fNameAllocated(FALSE), m_fValueAllocated(FALSE),
  752. m_szName(NULL), m_szValue(NULL),
  753. m_cchName(0), m_cchValue(0),
  754. m_pNext(NULL)
  755. {
  756. }
  757. /*===================================================================
  758. CHTTPHeader::~CHTTPHeader
  759. Destructor
  760. ===================================================================*/
  761. CHTTPHeader::~CHTTPHeader()
  762. {
  763. if (m_fNameAllocated)
  764. {
  765. Assert(m_szName);
  766. delete [] m_szName;
  767. }
  768. if (m_fValueAllocated)
  769. {
  770. Assert(m_szValue);
  771. delete [] m_szValue;
  772. }
  773. }
  774. /*===================================================================
  775. HRESULT CHTTPHeader::InitHeader
  776. Functions set the header strings. Agrument types combinations:
  777. BSTR, BSTR
  778. hardcoded char*, BSTR
  779. hardcoded char*, hardcoded char*
  780. hardcoded char*, int
  781. Parameters:
  782. Name, Value
  783. Returns:
  784. S_OK Success
  785. ===================================================================*/
  786. HRESULT CHTTPHeader::InitHeader(BSTR wszName, BSTR wszValue, UINT lCodePage /* CP_ACP */)
  787. {
  788. Assert(!m_fInited);
  789. Assert(wszName);
  790. CWCharToMBCS convStr;
  791. HRESULT hr = S_OK;
  792. // name
  793. if (FAILED(hr = convStr.Init(wszName,lCodePage))) {
  794. if (hr == E_OUTOFMEMORY)
  795. return hr;
  796. m_fNameAllocated = FALSE;
  797. m_szName = "";
  798. }
  799. else {
  800. m_szName = convStr.GetString(TRUE);
  801. m_fNameAllocated = TRUE;
  802. }
  803. m_cchName = strlen(m_szName);
  804. // value
  805. int cch = wszValue ? wcslen(wszValue) : 0;
  806. if (cch > 0)
  807. {
  808. if (FAILED(hr = convStr.Init(wszValue,lCodePage))) {
  809. return hr;
  810. }
  811. m_szValue = convStr.GetString(TRUE);
  812. m_fValueAllocated = TRUE;
  813. m_cchValue = strlen(m_szValue);
  814. }
  815. else
  816. {
  817. m_szValue = NULL;
  818. m_fValueAllocated = FALSE;
  819. m_cchValue = 0;
  820. }
  821. m_fInited = TRUE;
  822. return S_OK;
  823. }
  824. HRESULT CHTTPHeader::InitHeader(char *szName, BSTR wszValue, UINT lCodePage /* = CP_ACP */)
  825. {
  826. Assert(!m_fInited);
  827. Assert(szName);
  828. CWCharToMBCS convStr;
  829. HRESULT hr = S_OK;
  830. m_szName = szName;
  831. m_cchName = strlen(m_szName);
  832. m_fNameAllocated = FALSE;
  833. int cch = wszValue ? wcslen(wszValue) : 0;
  834. if (cch > 0)
  835. {
  836. if (FAILED(hr = convStr.Init(wszValue,lCodePage))) {
  837. return hr;
  838. }
  839. m_szValue = convStr.GetString(TRUE);
  840. m_fValueAllocated = TRUE;
  841. m_cchValue = strlen(m_szValue);
  842. }
  843. else
  844. {
  845. m_szValue = NULL;
  846. m_fValueAllocated = FALSE;
  847. m_cchValue = 0;
  848. }
  849. m_fInited = TRUE;
  850. return S_OK;
  851. }
  852. HRESULT CHTTPHeader::InitHeader(char *szName, char *szValue, BOOL fCopyValue)
  853. {
  854. Assert(!m_fInited);
  855. Assert(szName);
  856. m_szName = szName;
  857. m_cchName = strlen(m_szName);
  858. m_fNameAllocated = FALSE;
  859. if (fCopyValue)
  860. {
  861. int cch = szValue ? strlen(szValue) : 0;
  862. if (cch > 0)
  863. {
  864. m_szValue = new char[cch+1];
  865. if (m_szValue == NULL)
  866. return E_OUTOFMEMORY;
  867. m_fValueAllocated = TRUE;
  868. strcpy(m_szValue, szValue);
  869. m_cchValue = cch;
  870. }
  871. else
  872. {
  873. m_szValue = NULL;
  874. m_fValueAllocated = FALSE;
  875. m_cchValue = 0;
  876. }
  877. }
  878. else
  879. {
  880. m_szValue = szValue;
  881. m_cchValue = strlen(m_szValue);
  882. m_fValueAllocated = FALSE;
  883. }
  884. m_fInited = TRUE;
  885. return S_OK;
  886. }
  887. HRESULT CHTTPHeader::InitHeader(char *szName, long lValue)
  888. {
  889. Assert(!m_fInited);
  890. Assert(szName);
  891. m_szName = szName;
  892. m_cchName = strlen(m_szName);
  893. m_fNameAllocated = FALSE;
  894. ltoa(lValue, m_rgchLtoaBuffer, 10);
  895. m_szValue = m_rgchLtoaBuffer;
  896. m_cchValue = strlen(m_szValue);
  897. m_fValueAllocated = FALSE;
  898. m_fInited = TRUE;
  899. return S_OK;
  900. }
  901. /*===================================================================
  902. CHTTPHeader::Print
  903. Prints the header into a buffer in "Header: Value\r\n" format.
  904. Parameters:
  905. szBuf buffer to fill
  906. ===================================================================*/
  907. void CHTTPHeader::Print
  908. (
  909. char *szBuf
  910. )
  911. {
  912. Assert(m_fInited);
  913. Assert(m_cchName);
  914. Assert(m_szName);
  915. memcpy(szBuf, m_szName, m_cchName);
  916. szBuf += m_cchName;
  917. *szBuf++ = ':';
  918. *szBuf++ = ' ';
  919. if (m_cchValue)
  920. {
  921. Assert(m_szValue);
  922. memcpy(szBuf, m_szValue, m_cchValue);
  923. szBuf += m_cchValue;
  924. }
  925. *szBuf++ = '\r';
  926. *szBuf++ = '\n';
  927. *szBuf = '\0';
  928. }
  929. /*
  930. *
  931. *
  932. *
  933. * C R e s p o n s e D a t a
  934. *
  935. *
  936. *
  937. */
  938. /*===================================================================
  939. CResponseData::CResponseData
  940. Constructor
  941. Parameters:
  942. CResponse *pResponse
  943. Returns:
  944. Nothing.
  945. ===================================================================*/
  946. CResponseData::CResponseData
  947. (
  948. CResponse *pResponse
  949. )
  950. :
  951. m_ISupportErrImp(static_cast<IResponse *>(pResponse), this, IID_IResponse),
  952. m_WriteCookies(pResponse, this),
  953. m_cRefs(1)
  954. {
  955. m_pIReq = NULL;
  956. m_pHitObj = NULL;
  957. m_pTemplate = NULL;
  958. m_pFirstHeader = m_pLastHeader = NULL;
  959. m_fHeadersWritten = FALSE;
  960. m_fResponseAborted = FALSE;
  961. m_fWriteClientError = FALSE;
  962. m_fIgnoreWrites = FALSE;
  963. m_fBufferingOn = FALSE;
  964. m_fFlushed = FALSE;
  965. m_fChunked = FALSE;
  966. m_fClientDebugMode = FALSE;
  967. m_fClientDebugFlushIgnored = FALSE;
  968. m_szCookieVal = NULL;
  969. m_pszDefaultContentType = NULL;
  970. m_pszContentType = NULL;
  971. m_pszCharSet = NULL;
  972. m_pszStatus = NULL;
  973. m_pszCacheControl = NULL;
  974. m_dwVersionMajor = 0;
  975. m_dwVersionMinor = 0;
  976. m_pResponseBuffer = NULL;
  977. m_pClientDebugBuffer = NULL;
  978. m_tExpires = -1;
  979. m_pszDefaultExpires = NULL;
  980. m_pfnGetScript = NULL;
  981. m_pvGetScriptContext = NULL;
  982. }
  983. /*===================================================================
  984. CResponseData::~CResponseData
  985. Destructor
  986. Parameters:
  987. Returns:
  988. Nothing.
  989. ===================================================================*/
  990. CResponseData::~CResponseData()
  991. {
  992. // points to static string - no need to free
  993. // m_pszDefaultContentType = NULL;
  994. // Free any memory associated with the content-type
  995. if (m_pszContentType)
  996. free(m_pszContentType);
  997. // Free any memory associated with the CacheControl
  998. if (m_pszCacheControl)
  999. free(m_pszCacheControl);
  1000. // Free any memory associated with the CharSet
  1001. if (m_pszCharSet)
  1002. free(m_pszCharSet);
  1003. // Free any memory associated with the status
  1004. if (m_pszStatus)
  1005. free(m_pszStatus);
  1006. // Free all headers
  1007. CHTTPHeader *pHeader = m_pFirstHeader;
  1008. while (pHeader)
  1009. {
  1010. CHTTPHeader *pNextHeader = pHeader->PNext();
  1011. delete pHeader;
  1012. pHeader = pNextHeader;
  1013. }
  1014. m_pFirstHeader = m_pLastHeader = NULL;
  1015. // Clean up the Response Buffer
  1016. if (m_pResponseBuffer)
  1017. delete m_pResponseBuffer;
  1018. // Clean up Client Debug Response Buffer
  1019. if (m_pClientDebugBuffer)
  1020. delete m_pClientDebugBuffer;
  1021. }
  1022. /*===================================================================
  1023. CResponseData::Init
  1024. Init
  1025. Parameters:
  1026. CResponse *pResponse
  1027. Returns:
  1028. Nothing.
  1029. ===================================================================*/
  1030. HRESULT CResponseData::Init
  1031. (
  1032. CResponse *pResponse
  1033. )
  1034. {
  1035. HRESULT hr = S_OK;
  1036. m_pIReq = NULL;
  1037. // set the HEAD request flag to 0 un-inited
  1038. m_IsHeadRequest = 0;
  1039. // Initialize header list
  1040. m_pFirstHeader = m_pLastHeader = NULL;
  1041. // Initialize the response buffer
  1042. m_pResponseBuffer = new CResponseBuffer;
  1043. if (m_pResponseBuffer == NULL)
  1044. hr = E_OUTOFMEMORY;
  1045. if (SUCCEEDED(hr))
  1046. hr = m_pResponseBuffer->Init(pResponse);
  1047. return hr;
  1048. }
  1049. /*===================================================================
  1050. CResponseData::QueryInterface
  1051. CResponseData::AddRef
  1052. CResponseData::Release
  1053. IUnknown members for CRequestData object.
  1054. ===================================================================*/
  1055. STDMETHODIMP CResponseData::QueryInterface
  1056. (
  1057. REFIID iid,
  1058. void **ppvObj
  1059. )
  1060. {
  1061. if (iid == IID_IUnknown)
  1062. {
  1063. *ppvObj = this;
  1064. AddRef();
  1065. return S_OK;
  1066. }
  1067. else
  1068. {
  1069. *ppvObj = NULL;
  1070. return E_NOINTERFACE;
  1071. }
  1072. }
  1073. STDMETHODIMP_(ULONG) CResponseData::AddRef()
  1074. {
  1075. return ++m_cRefs;
  1076. }
  1077. STDMETHODIMP_(ULONG) CResponseData::Release(void)
  1078. {
  1079. if (--m_cRefs)
  1080. return m_cRefs;
  1081. delete this;
  1082. return 0;
  1083. }
  1084. /*
  1085. *
  1086. *
  1087. *
  1088. * C R e s p o n s e
  1089. *
  1090. *
  1091. *
  1092. */
  1093. /*===================================================================
  1094. CResponse::CResponse
  1095. Constructor
  1096. Parameters:
  1097. punkOuter object to ref count (can be NULL)
  1098. ===================================================================*/
  1099. CResponse::CResponse(IUnknown *punkOuter)
  1100. :
  1101. m_fInited(FALSE),
  1102. m_fDiagnostics(FALSE),
  1103. m_pData(NULL)
  1104. {
  1105. CDispatch::Init(IID_IResponse);
  1106. if (punkOuter)
  1107. {
  1108. m_punkOuter = punkOuter;
  1109. m_fOuterUnknown = TRUE;
  1110. }
  1111. else
  1112. {
  1113. m_cRefs = 1;
  1114. m_fOuterUnknown = FALSE;
  1115. }
  1116. #ifdef DBG
  1117. m_fDiagnostics = TRUE;
  1118. #endif // DBG
  1119. }
  1120. /*===================================================================
  1121. CResponse::~CResponse
  1122. Destructor
  1123. Parameters:
  1124. None
  1125. Returns:
  1126. Nothing.
  1127. ===================================================================*/
  1128. CResponse::~CResponse()
  1129. {
  1130. Assert(!m_fInited);
  1131. Assert(m_fOuterUnknown || m_cRefs == 0); // must have 0 ref count
  1132. }
  1133. /*===================================================================
  1134. CResponse::CleanUp
  1135. Deallocates members and removes m_pData
  1136. Parameters:
  1137. None
  1138. Returns:
  1139. HRESULT (S_OK)
  1140. ===================================================================*/
  1141. HRESULT CResponse::CleanUp()
  1142. {
  1143. if (m_pData)
  1144. {
  1145. m_pData->Release();
  1146. m_pData = NULL;
  1147. }
  1148. return S_OK;
  1149. }
  1150. /*===================================================================
  1151. CResponse::Init
  1152. Allocates m_pData
  1153. Performs any intiailization of a CResponse that's prone to failure
  1154. that we also use internally before exposing the object outside.
  1155. Parameters:
  1156. None
  1157. Returns:
  1158. S_OK on success.
  1159. ===================================================================*/
  1160. HRESULT CResponse::Init()
  1161. {
  1162. if (m_fInited)
  1163. return S_OK; // already inited
  1164. Assert(!m_pData);
  1165. m_pData = new CResponseData(this);
  1166. if (!m_pData)
  1167. return E_OUTOFMEMORY;
  1168. HRESULT hr = m_pData->Init(this);
  1169. if (SUCCEEDED(hr))
  1170. m_fInited = TRUE;
  1171. else
  1172. CleanUp();
  1173. return hr;
  1174. }
  1175. /*===================================================================
  1176. CResponse::UnInit
  1177. Remove m_pData. Back to UnInited state
  1178. Parameters:
  1179. None
  1180. Returns:
  1181. HRESULT
  1182. ===================================================================*/
  1183. HRESULT CResponse::UnInit()
  1184. {
  1185. if (!m_fInited)
  1186. return S_OK; // already uninited
  1187. Assert(m_pData);
  1188. CleanUp();
  1189. Assert(!m_pData);
  1190. m_fInited = FALSE;
  1191. return S_OK;
  1192. }
  1193. /*===================================================================
  1194. CResponse::ReInitTemplate
  1195. This function is used to set the template member. It should only
  1196. be used for an ordinary script file's template, not for global.asa template.
  1197. Parameters:
  1198. Pointer to template
  1199. Returns:
  1200. S_OK on success.
  1201. ===================================================================*/
  1202. HRESULT CResponse::ReInitTemplate
  1203. (
  1204. CTemplate* pTemplate,
  1205. const char *szCookieVal
  1206. )
  1207. {
  1208. Assert(m_fInited);
  1209. Assert(m_pData);
  1210. Assert(pTemplate != NULL);
  1211. Assert(m_pData->m_pTemplate == NULL);
  1212. m_pData->m_pTemplate = pTemplate;
  1213. m_pData->m_pTemplate->AddRef(); // We release the template in FinalFlush
  1214. m_pData->m_szCookieVal = szCookieVal;
  1215. return(S_OK);
  1216. }
  1217. /*===================================================================
  1218. CResponse::SwapTemplate
  1219. Temporary substitutes Template in response
  1220. Used in child request execution
  1221. Parameters:
  1222. Pointer to the new template
  1223. Returns:
  1224. Pointer to the old template
  1225. ===================================================================*/
  1226. CTemplate *CResponse::SwapTemplate
  1227. (
  1228. CTemplate* pNewTemplate
  1229. )
  1230. {
  1231. Assert(m_fInited);
  1232. Assert(m_pData);
  1233. CTemplate *pOldTemplate = m_pData->m_pTemplate;
  1234. m_pData->m_pTemplate = pNewTemplate;
  1235. return pOldTemplate;
  1236. }
  1237. /*===================================================================
  1238. CResponse::ReInit
  1239. Each Request we service will have a new CIsapiReqInfo.
  1240. This function is used to set the value of the CIsapiReqInfo.
  1241. Parameters:
  1242. Pointer to CIsapiReqInfo
  1243. Returns:
  1244. S_OK on success.
  1245. ===================================================================*/
  1246. HRESULT CResponse::ReInit
  1247. (
  1248. CIsapiReqInfo *pIReq,
  1249. const char *szCookieVal,
  1250. CRequest *pRequest,
  1251. PFNGETSCRIPT pfnGetScript,
  1252. void *pvGetScriptContext,
  1253. CHitObj *pHitObj
  1254. )
  1255. {
  1256. Assert(m_fInited);
  1257. Assert(m_pData);
  1258. CHTTPHeader *pCurr;
  1259. CLinkElem *pT;
  1260. CLinkElem *pNext;
  1261. // set the HEAD request flag to 0 un-inited
  1262. m_pData->m_IsHeadRequest = 0;
  1263. // ReInitialize the WriteCookie dictionary
  1264. if (FAILED(m_pData->m_WriteCookies.ReInit(pRequest)))
  1265. return E_FAIL;
  1266. // points to static string - no need to free
  1267. m_pData->m_pszDefaultContentType = NULL;
  1268. // Free any memory associated with the content type
  1269. if (m_pData->m_pszContentType != NULL)
  1270. {
  1271. free(m_pData->m_pszContentType);
  1272. m_pData->m_pszContentType = NULL;
  1273. }
  1274. // Free any memory associated with the content type
  1275. if (m_pData->m_pszCharSet != NULL)
  1276. {
  1277. free(m_pData->m_pszCharSet);
  1278. m_pData->m_pszCharSet = NULL;
  1279. }
  1280. // Free any memory associated with the status
  1281. if (m_pData->m_pszStatus != NULL)
  1282. {
  1283. free(m_pData->m_pszStatus);
  1284. m_pData->m_pszStatus = NULL;
  1285. }
  1286. // Free all headers
  1287. CHTTPHeader *pHeader = m_pData->m_pFirstHeader;
  1288. while (pHeader)
  1289. {
  1290. CHTTPHeader *pNextHeader = pHeader->PNext();
  1291. delete pHeader;
  1292. pHeader = pNextHeader;
  1293. }
  1294. m_pData->m_pFirstHeader = m_pData->m_pLastHeader = NULL;
  1295. m_pData->m_fHeadersWritten = FALSE;
  1296. m_pData->m_fResponseAborted = FALSE;
  1297. m_pData->m_fWriteClientError = FALSE;
  1298. m_pData->m_fIgnoreWrites = FALSE;
  1299. m_pData->m_pIReq = pIReq;
  1300. m_pData->m_szCookieVal = szCookieVal;
  1301. m_pData->m_pszDefaultContentType = NULL;
  1302. m_pData->m_pszContentType = NULL;
  1303. m_pData->m_pszCharSet = NULL;
  1304. m_pData->m_pszStatus = NULL;
  1305. m_pData->m_pfnGetScript = pfnGetScript;
  1306. m_pData->m_pvGetScriptContext = pvGetScriptContext;
  1307. m_pData->m_pHitObj = pHitObj;
  1308. m_pData->m_tExpires = -1;
  1309. m_pData->m_pszDefaultExpires = NULL;
  1310. // Ask for the HTTP version of the client
  1311. GetClientVerison();
  1312. // Set the default content type
  1313. if (m_pData->m_pIReq)
  1314. m_pData->m_pszDefaultContentType = GetResponseMimeType(m_pData->m_pIReq);
  1315. // Set the default Expires Header
  1316. // NOTE - removed per discussions on proper header settings with IE/IIS
  1317. // teams.
  1318. #if 0
  1319. if (m_pData->m_pIReq)
  1320. m_pData->m_pszDefaultExpires = m_pData->m_pIReq->QueryPszExpires();
  1321. #endif
  1322. // Set the buffering flag to the global value
  1323. m_pData->m_fBufferingOn = (pHitObj->QueryAppConfig())->fBufferingOn();
  1324. // Buffering always on for client code debug
  1325. if (pHitObj && pHitObj->FClientCodeDebug())
  1326. {
  1327. m_pData->m_fBufferingOn = TRUE;
  1328. m_pData->m_fClientDebugMode = TRUE;
  1329. m_pData->m_fClientDebugFlushIgnored = FALSE;
  1330. }
  1331. else
  1332. {
  1333. m_pData->m_fClientDebugMode = FALSE;
  1334. m_pData->m_fClientDebugFlushIgnored = FALSE;
  1335. }
  1336. HRESULT hr = S_OK;
  1337. if (m_pData->m_fClientDebugMode)
  1338. {
  1339. // Create and init client debug buffer if needed
  1340. if (m_pData->m_pClientDebugBuffer)
  1341. {
  1342. hr = m_pData->m_pClientDebugBuffer->ClearAndStart();
  1343. }
  1344. else
  1345. {
  1346. m_pData->m_pClientDebugBuffer = new CDebugResponseBuffer;
  1347. if (m_pData->m_pClientDebugBuffer)
  1348. hr = m_pData->m_pClientDebugBuffer->InitAndStart(this);
  1349. else
  1350. hr = E_OUTOFMEMORY;
  1351. }
  1352. }
  1353. return hr;
  1354. }
  1355. /*===================================================================
  1356. CResponse::QueryInterface
  1357. CResponse::AddRef
  1358. CResponse::Release
  1359. IUnknown members for CResponse object.
  1360. ===================================================================*/
  1361. STDMETHODIMP CResponse::QueryInterface
  1362. (
  1363. REFIID riid,
  1364. PPVOID ppv
  1365. )
  1366. {
  1367. *ppv = NULL;
  1368. /*
  1369. * The only calls for IUnknown are either in a nonaggregated
  1370. * case or when created in an aggregation, so in either case
  1371. * always return our IUnknown for IID_IUnknown.
  1372. */
  1373. // BUG FIX 683 added IID_IDenaliIntrinsic to prevent the user from
  1374. // storing intrinsic objects in the application and session object
  1375. if (IID_IUnknown == riid || IID_IDispatch == riid || IID_IResponse == riid || IID_IDenaliIntrinsic == riid)
  1376. *ppv = static_cast<IResponse *>(this);
  1377. // Support IStream for ADO/XML
  1378. else if (IID_IStream == riid)
  1379. *ppv = static_cast<IStream *>(this);
  1380. //Indicate that we support error information
  1381. else if (IID_ISupportErrorInfo == riid)
  1382. {
  1383. if (m_pData)
  1384. *ppv = &(m_pData->m_ISupportErrImp);
  1385. }
  1386. else if (IID_IMarshal == riid)
  1387. {
  1388. *ppv = static_cast<IMarshal *>(this);
  1389. }
  1390. //AddRef any interface we'll return.
  1391. if (NULL != *ppv)
  1392. {
  1393. ((LPUNKNOWN)*ppv)->AddRef();
  1394. return S_OK;
  1395. }
  1396. return ResultFromScode(E_NOINTERFACE);
  1397. }
  1398. STDMETHODIMP_(ULONG) CResponse::AddRef(void)
  1399. {
  1400. if (m_fOuterUnknown)
  1401. return m_punkOuter->AddRef();
  1402. return InterlockedIncrement((LPLONG)&m_cRefs);
  1403. }
  1404. STDMETHODIMP_(ULONG) CResponse::Release(void)
  1405. {
  1406. if (m_fOuterUnknown)
  1407. return m_punkOuter->Release();
  1408. DWORD cRefs = InterlockedDecrement((LPLONG)&m_cRefs);
  1409. if (cRefs)
  1410. return cRefs;
  1411. delete this;
  1412. return 0;
  1413. }
  1414. /*===================================================================
  1415. CResponse::GetIDsOfNames
  1416. Special-case implementation for Response.WriteBlock and
  1417. Response.Write
  1418. Parameters:
  1419. riid REFIID reserved. Must be IID_NULL.
  1420. rgszNames OLECHAR ** pointing to the array of names to be mapped.
  1421. cNames UINT number of names to be mapped.
  1422. lcid LCID of the locale.
  1423. rgDispID DISPID * caller allocated array containing IDs
  1424. corresponging to those names in rgszNames.
  1425. Return Value:
  1426. HRESULT S_OK or a general error code.
  1427. ===================================================================*/
  1428. STDMETHODIMP CResponse::GetIDsOfNames
  1429. (
  1430. REFIID riid,
  1431. OLECHAR **rgszNames,
  1432. UINT cNames,
  1433. LCID lcid,
  1434. DISPID *rgDispID
  1435. )
  1436. {
  1437. const DISPID dispidWrite = 0x60020013;
  1438. const DISPID dispidWriteBlock = 0x60020014;
  1439. if (cNames == 1)
  1440. {
  1441. // first char 'W'
  1442. if (rgszNames[0][0] == L'w' || rgszNames[0][0] == L'W')
  1443. {
  1444. // swtich on strlen
  1445. switch (wcslen(rgszNames[0]))
  1446. {
  1447. case 5:
  1448. // case insensitive because user can type either way
  1449. if (wcsicmp(rgszNames[0], L"write") == 0)
  1450. {
  1451. *rgDispID = dispidWrite;
  1452. return S_OK;
  1453. }
  1454. break;
  1455. case 10:
  1456. // case sensitive because only we generate WriteBlock
  1457. if (wcscmp(rgszNames[0], L"WriteBlock") == 0)
  1458. {
  1459. *rgDispID = dispidWriteBlock;
  1460. return S_OK;
  1461. }
  1462. break;
  1463. }
  1464. }
  1465. }
  1466. // default to CDispatch's implementation
  1467. return CDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispID);
  1468. }
  1469. /*===================================================================
  1470. CResponse::CheckForTombstone
  1471. Tombstone stub for IResponse methods. If the object is
  1472. tombstone, does ExceptionId and fails.
  1473. Parameters:
  1474. Returns:
  1475. HRESULT E_FAIL if Tombstone
  1476. S_OK if not
  1477. ===================================================================*/
  1478. HRESULT CResponse::CheckForTombstone()
  1479. {
  1480. if (m_fInited)
  1481. {
  1482. // inited - good object
  1483. Assert(m_pData); // must be present for inited objects
  1484. return S_OK;
  1485. }
  1486. ExceptionId
  1487. (
  1488. IID_IResponse,
  1489. IDE_RESPONSE,
  1490. IDE_INTRINSIC_OUT_OF_SCOPE
  1491. );
  1492. return E_FAIL;
  1493. }
  1494. /*===================================================================
  1495. CResponse::SyncWrite
  1496. Static method. Sends data until either everything is sent
  1497. or there's an error.
  1498. Parameters:
  1499. pIReq CIsapiReqInfo to send
  1500. pchBuf pointer to buffer to send
  1501. cchBuf number of bytes to send (0 means do strlen())
  1502. Returns:
  1503. HRESULT
  1504. ===================================================================*/
  1505. HRESULT CResponse::SyncWrite
  1506. (
  1507. CIsapiReqInfo *pIReq,
  1508. char *pchBuf,
  1509. DWORD cchBuf
  1510. )
  1511. {
  1512. Assert(pchBuf);
  1513. if (cchBuf == 0)
  1514. cchBuf = strlen(pchBuf);
  1515. while (cchBuf)
  1516. {
  1517. // never send out more then MAX_RESPONSE bytes at one shot
  1518. DWORD cchSend = (cchBuf < MAX_RESPONSE) ? cchBuf : MAX_RESPONSE;
  1519. if (!pIReq->SyncWriteClient(pchBuf, &cchSend))
  1520. return E_FAIL;
  1521. AddtoTotalByteOut(cchSend);
  1522. cchBuf -= cchSend;
  1523. pchBuf += cchSend;
  1524. }
  1525. return S_OK;
  1526. }
  1527. /*===================================================================
  1528. CResponse::SyncWriteFile
  1529. Static method.
  1530. Sends entire response as a content of the file
  1531. Parameters:
  1532. pIReq CIsapiReqInfo to send
  1533. szFile file name
  1534. szMimeType mime type
  1535. szStatus HTTP status
  1536. szExtraHeaders additional HTTP headers to send
  1537. Returns:
  1538. HRESULT
  1539. ===================================================================*/
  1540. HRESULT CResponse::SyncWriteFile
  1541. (
  1542. CIsapiReqInfo *pIReq,
  1543. TCHAR *szFile,
  1544. char *szMimeType,
  1545. char *szStatus,
  1546. char *szExtraHeaders
  1547. )
  1548. {
  1549. HRESULT hr = S_OK;
  1550. HANDLE hFile = INVALID_HANDLE_VALUE;
  1551. HANDLE hMap = NULL;
  1552. BYTE *pbBytes = NULL;
  1553. DWORD dwSize = 0;
  1554. // open the file
  1555. if (SUCCEEDED(hr)) {
  1556. hFile = CreateFile(
  1557. szFile,
  1558. GENERIC_READ, // access (read-write) mode
  1559. FILE_SHARE_READ, // share mode
  1560. NULL, // pointer to security descriptor
  1561. OPEN_EXISTING, // how to create
  1562. FILE_ATTRIBUTE_NORMAL, // file attributes
  1563. NULL // handle to file with attributes to copy
  1564. );
  1565. if (hFile == INVALID_HANDLE_VALUE) {
  1566. #if UNICODE
  1567. DBGERROR((DBG_CONTEXT, "Could not open \"%S\". Win32 Error = %u\n", szFile, GetLastError()));
  1568. #else
  1569. DBGERROR((DBG_CONTEXT, "Could not open \"%s\". Win32 Error = %u\n", szFile, GetLastError()));
  1570. #endif
  1571. hr = E_FAIL;
  1572. }
  1573. }
  1574. // get file size
  1575. if (SUCCEEDED(hr))
  1576. {
  1577. dwSize = GetFileSize(hFile, NULL);
  1578. if (dwSize == 0 || dwSize == 0xFFFFFFFF)
  1579. hr = E_FAIL;
  1580. }
  1581. // create mapping
  1582. if (SUCCEEDED(hr))
  1583. {
  1584. hMap = CreateFileMapping(
  1585. hFile, // handle to file to map
  1586. NULL, // optional security attributes
  1587. PAGE_READONLY, // protection for mapping object
  1588. 0, // high-order 32 bits of object size
  1589. 0, // low-order 32 bits of object size
  1590. NULL // name of file-mapping object
  1591. );
  1592. if (hMap == NULL)
  1593. hr = E_FAIL;
  1594. }
  1595. // map the file
  1596. if (SUCCEEDED(hr))
  1597. {
  1598. pbBytes = (BYTE *)MapViewOfFile(
  1599. hMap, // file-mapping object to map into address space
  1600. FILE_MAP_READ, // access mode
  1601. 0, // high-order 32 bits of file offset
  1602. 0, // low-order 32 bits of file offset
  1603. 0 // number of bytes to map
  1604. );
  1605. if (pbBytes == NULL)
  1606. hr = E_FAIL;
  1607. }
  1608. // send the response
  1609. if (SUCCEEDED(hr))
  1610. {
  1611. hr = SyncWriteBlock(pIReq, pbBytes, dwSize, szMimeType, szStatus, szExtraHeaders);
  1612. }
  1613. // cleanup
  1614. if (pbBytes != NULL)
  1615. UnmapViewOfFile(pbBytes);
  1616. if (hMap != NULL)
  1617. CloseHandle(hMap);
  1618. if (hFile != INVALID_HANDLE_VALUE)
  1619. CloseHandle(hFile);
  1620. return hr;
  1621. }
  1622. /*===================================================================
  1623. CResponse::SyncWriteScriptlessTemplate
  1624. Static method.
  1625. Sends entire response as a content of the [scriptless] template.
  1626. Parameters:
  1627. pIReq CIsapiReqInfo to send
  1628. pTemplate template
  1629. Returns:
  1630. HRESULT
  1631. ===================================================================*/
  1632. HRESULT CResponse::SyncWriteScriptlessTemplate
  1633. (
  1634. CIsapiReqInfo *pIReq,
  1635. CTemplate *pTemplate
  1636. )
  1637. {
  1638. Assert(pTemplate && pTemplate->FScriptless());
  1639. char* pbHTML = NULL;
  1640. ULONG cbHTML = 0;
  1641. ULONG cbSrcOffset = 0;
  1642. char* pbIncSrcFileName = NULL;
  1643. HRESULT hr = pTemplate->GetHTMLBlock(0, &pbHTML, &cbHTML, &cbSrcOffset, &pbIncSrcFileName);
  1644. if (FAILED(hr))
  1645. return hr;
  1646. if (pbHTML == NULL || cbHTML == 0)
  1647. return E_FAIL;
  1648. SyncWriteBlock(pIReq, pbHTML, cbHTML);
  1649. return S_OK;
  1650. }
  1651. /*===================================================================
  1652. CResponse::SyncWriteBlocks
  1653. Static method.
  1654. Sends entire response as a content of a set of memory blocks.
  1655. Parameters:
  1656. pIReq CIsapiReqInfo to send
  1657. cBlocks number of blocks
  1658. cbTotal total length of all blocks
  1659. rgpvBlock array of pointers to buffers
  1660. rgcbBlock array of block sizes
  1661. szMimeType mime type
  1662. szStatus HTTP status
  1663. szExtraHeaders additional HTTP headers to send
  1664. Returns:
  1665. HRESULT
  1666. ===================================================================*/
  1667. HRESULT CResponse::SyncWriteBlocks
  1668. (
  1669. CIsapiReqInfo *pIReq,
  1670. DWORD cBlocks,
  1671. DWORD cbTotal,
  1672. void **rgpvBlock,
  1673. DWORD *rgcbBlock,
  1674. char *szMimeType,
  1675. char *szStatus,
  1676. char *szExtraHeaders
  1677. )
  1678. {
  1679. BOOL fCacheControlPrivate = FALSE;
  1680. STACK_BUFFER( tempContent, 1024);
  1681. // defaut mime type and status
  1682. if (szMimeType == NULL)
  1683. szMimeType = (char *)GetResponseMimeType(pIReq);
  1684. if (szStatus == NULL)
  1685. {
  1686. szStatus = (char *)s_szDefaultStatus;
  1687. fCacheControlPrivate = TRUE;
  1688. }
  1689. else
  1690. {
  1691. fCacheControlPrivate = (strcmp(szStatus, s_szDefaultStatus) == 0);
  1692. }
  1693. // extra headers size
  1694. DWORD cbExtra = (szExtraHeaders != NULL) ? strlen(szExtraHeaders) : 0;
  1695. // send the header
  1696. char szLength[20];
  1697. ltoa(cbTotal, szLength, 10);
  1698. DWORD cchContentHeader = (DWORD)(0
  1699. + sizeof(s_szContentTypeHeader)-1 // Content-Type:
  1700. + strlen(szMimeType) // text/html
  1701. + 2 // \r\n
  1702. + cbExtra // Extra headers
  1703. + sizeof(s_szContentLengthHeader)-1 // Content-Length:
  1704. + strlen(szLength) // <length>
  1705. + 4 // \r\n\r\n
  1706. + 1); // '\0'
  1707. if (fCacheControlPrivate)
  1708. cchContentHeader += sizeof(s_szCacheControlPrivate)-1;
  1709. if (!tempContent.Resize(cchContentHeader)) {
  1710. return E_OUTOFMEMORY;
  1711. }
  1712. char *szContentHeader = (char *)tempContent.QueryPtr();
  1713. char *szBuf = szContentHeader;
  1714. szBuf = strcpyExA(szBuf, s_szContentTypeHeader);
  1715. szBuf = strcpyExA(szBuf, szMimeType);
  1716. szBuf = strcpyExA(szBuf, "\r\n");
  1717. if (cbExtra > 0)
  1718. szBuf = strcpyExA(szBuf, szExtraHeaders);
  1719. if (fCacheControlPrivate)
  1720. szBuf = strcpyExA(szBuf, s_szCacheControlPrivate);
  1721. szBuf = strcpyExA(szBuf, s_szContentLengthHeader);
  1722. szBuf = strcpyExA(szBuf, szLength);
  1723. szBuf = strcpyExA(szBuf, "\r\n\r\n");
  1724. BOOL fRet = pIReq->SendHeader
  1725. (
  1726. szStatus,
  1727. strlen(szStatus) + 1,
  1728. szContentHeader,
  1729. cchContentHeader,
  1730. FALSE
  1731. );
  1732. if (!fRet)
  1733. return E_FAIL;
  1734. // send the data
  1735. for (DWORD i = 0; i < cBlocks; i++)
  1736. {
  1737. if (FAILED(SyncWrite(pIReq, (char *)rgpvBlock[i], rgcbBlock[i])))
  1738. return E_FAIL;
  1739. }
  1740. return S_OK;
  1741. }
  1742. //IResponse interface functions
  1743. /*===================================================================
  1744. CResponse::WriteHeaders
  1745. Write out standard HTTP headers and any user created headers.
  1746. If transmission of the headers to the client fails, we still
  1747. want the calling script to finish execution, so we will return
  1748. S_OK, but set the m_fWriteClientError flag. If we are unable
  1749. to build the needed headers we will return E_FAIL.
  1750. Parameters:
  1751. BOOL fSendEntireResponse - send headers and body all at once?
  1752. Returns:
  1753. HRESULT S_OK on success
  1754. E_FAIL if unable to build expires headers
  1755. E_OUTOFMEMORY if memory failure
  1756. ===================================================================*/
  1757. HRESULT CResponse::WriteHeaders(
  1758. BOOL fSendEntireResponse)
  1759. {
  1760. if (FAILED(CheckForTombstone()))
  1761. return E_FAIL;
  1762. if (FDontWrite())
  1763. return S_OK;
  1764. HRESULT hr = S_OK;
  1765. CHAR *szBuff;
  1766. DWORD cch = 0;
  1767. BOOL fContentTypeFound = FALSE;
  1768. // Static cookie buffer should be enough to fit:
  1769. // cookie name 20 chars
  1770. // cookie value 24 chars
  1771. // decorations 28 = strlen("Set-Cookie: C=V; secure; path=/;\r\n")
  1772. #define CCH_STATIC_COOKIE_BUF 88
  1773. char szCookieBuff[CCH_STATIC_COOKIE_BUF];
  1774. DWORD cchCookieBuff = 0;
  1775. STACK_BUFFER( tempHeaders, 2048 );
  1776. STACK_BUFFER( tempWSABUFs, 128 );
  1777. AssertValid();
  1778. // Loop through any headers counting up the length
  1779. CHTTPHeader *pHeader = m_pData->m_pFirstHeader;
  1780. while (pHeader) {
  1781. cch += pHeader->CchLength();
  1782. pHeader = pHeader->PNext();
  1783. }
  1784. // Add the content-type tag
  1785. cch += sizeof(s_szContentTypeHeader)-1;
  1786. cch += strlen(PContentType())+2;
  1787. // Add the Character set tag
  1788. if (m_pData->m_pszCharSet) {
  1789. cch += sizeof(s_szCharSetHTML)-1;
  1790. cch += strlen(m_pData->m_pszCharSet);
  1791. }
  1792. // Add the Expires tag
  1793. if ((m_pData->m_tExpires != -1) || (m_pData->m_pszDefaultExpires != NULL))
  1794. cch += DATE_STRING_SIZE + 11; // DATE_STRING_SIZE + length("Expires: \r\n")
  1795. // Add the cookies that we will send
  1796. cch += m_pData->m_WriteCookies.QueryHeaderSize()+2;
  1797. // Account for space required by headers we always send back.
  1798. // Prepare cookie if any.
  1799. if (m_pData->m_szCookieVal) {
  1800. char *pchEnd = strcpyExA(szCookieBuff, "Set-Cookie: ");
  1801. pchEnd = strcpyExA(pchEnd, g_szSessionIDCookieName);
  1802. pchEnd = strcpyExA(pchEnd, "=");
  1803. pchEnd = strcpyExA(pchEnd, m_pData->m_szCookieVal);
  1804. // If we keep secure sessions secure, and this connection is secure, add flag to cookie
  1805. if ((m_pData->m_pHitObj->QueryAppConfig()->fKeepSessionIDSecure()) &&
  1806. (m_pData->m_pHitObj->FSecure()))
  1807. {
  1808. pchEnd = strcpyExA(pchEnd,"; secure");
  1809. }
  1810. pchEnd = strcpyExA(pchEnd, "; path=/\r\n");
  1811. cchCookieBuff = strlen(szCookieBuff);
  1812. cch += cchCookieBuff;
  1813. Assert(cchCookieBuff < CCH_STATIC_COOKIE_BUF);
  1814. }
  1815. else {
  1816. szCookieBuff[0] = '\0';
  1817. cchCookieBuff = 0;
  1818. }
  1819. // Will len of cache control header
  1820. if (m_pData->m_pszCacheControl) {
  1821. cch += sizeof(s_szCacheControl)-1;
  1822. cch += strlen(m_pData->m_pszCacheControl)+2;
  1823. }
  1824. else {
  1825. cch += sizeof(s_szCacheControlPrivate)-1;
  1826. }
  1827. // If using HTTP/1.1 and not buffering add length ofTransfer-Encoding headers
  1828. if ((m_pData->m_dwVersionMinor >= 1) && (m_pData->m_dwVersionMajor >= 1) &&
  1829. (m_pData->m_fBufferingOn == FALSE) &&
  1830. !GetIReq()->IsChild()) { // don't chunk child request output
  1831. // UNDONE: Temporary setting to turn off chuncked encoding
  1832. if (Glob(fEnableChunkedEncoding))
  1833. m_pData->m_fChunked = TRUE;
  1834. }
  1835. if (m_pData->m_fChunked)
  1836. cch += sizeof(s_szTransferEncoding)-1;
  1837. // Will terminate with \r\n. Leave extra space
  1838. cch += 2;
  1839. /*
  1840. * We know how big; allocate memory and build the string.
  1841. */
  1842. if (!tempHeaders.Resize(cch + 1)) {
  1843. return E_OUTOFMEMORY;
  1844. }
  1845. szBuff = (LPSTR)tempHeaders.QueryPtr();
  1846. *szBuff = '\0';
  1847. char *szTmpBuf = szBuff;
  1848. pHeader = m_pData->m_pFirstHeader;
  1849. while (pHeader) {
  1850. pHeader->Print(szTmpBuf);
  1851. szTmpBuf += pHeader->CchLength();
  1852. pHeader = pHeader->PNext();
  1853. }
  1854. // Send the content-type tag
  1855. szTmpBuf = strcpyExA(szTmpBuf, s_szContentTypeHeader);
  1856. szTmpBuf = strcpyExA(szTmpBuf, PContentType());
  1857. // Send the CharSet tag if exists
  1858. if (m_pData->m_pszCharSet) {
  1859. szTmpBuf = strcpyExA(szTmpBuf, s_szCharSetHTML);
  1860. szTmpBuf = strcpyExA(szTmpBuf, m_pData->m_pszCharSet);
  1861. }
  1862. szTmpBuf = strcpyExA(szTmpBuf, "\r\n");
  1863. // Send the Expires tag
  1864. if ((m_pData->m_tExpires != -1) || (m_pData->m_pszDefaultExpires != NULL)) {
  1865. // if the script set an expires value, than use it
  1866. if (m_pData->m_tExpires != -1) {
  1867. szTmpBuf = strcpyExA(szTmpBuf, "Expires: ");
  1868. if (FAILED(CTimeToStringGMT(&m_pData->m_tExpires, szTmpBuf)))
  1869. return E_FAIL;
  1870. szTmpBuf += strlen(szTmpBuf);
  1871. szTmpBuf = strcpyExA(szTmpBuf, "\r\n");
  1872. }
  1873. // else, use the default value in the metabase. Note that it already
  1874. // includes the Expires: prefix and \r\n suffix
  1875. else {
  1876. szTmpBuf = strcpyExA(szTmpBuf, m_pData->m_pszDefaultExpires);
  1877. }
  1878. }
  1879. // send the cookies
  1880. m_pData->m_WriteCookies.GetHeaders(szTmpBuf);
  1881. szTmpBuf += strlen(szTmpBuf);
  1882. // Send the required headers: session id cookie and cache control
  1883. szTmpBuf = strcpyExA(szTmpBuf, szCookieBuff);
  1884. // Send the cache-control tag
  1885. if (m_pData->m_pszCacheControl) {
  1886. szTmpBuf = strcpyExA(szTmpBuf, s_szCacheControl);
  1887. szTmpBuf = strcpyExA(szTmpBuf, m_pData->m_pszCacheControl);
  1888. szTmpBuf = strcpyExA(szTmpBuf, "\r\n");
  1889. }
  1890. else {
  1891. szTmpBuf = strcpyExA(szTmpBuf, s_szCacheControlPrivate);
  1892. }
  1893. // Chunked encoding
  1894. if (m_pData->m_fChunked)
  1895. szTmpBuf = strcpyExA(szTmpBuf, s_szTransferEncoding);
  1896. // Add trailing \r\n to terminate headers
  1897. szTmpBuf = strcpyExA(szTmpBuf, "\r\n");
  1898. Assert(strlen(szBuff) <= cch);
  1899. // Output the headers
  1900. // Failure is not a fatal error, so we still return success
  1901. // but set the m_fWriteClient flag
  1902. CHAR *szStatus = m_pData->m_pszStatus ? m_pData->m_pszStatus
  1903. : (CHAR *)s_szDefaultStatus;
  1904. BOOL fKeepConnected =
  1905. (m_pData->m_fBufferingOn && !m_pData->m_fFlushed) || m_pData->m_fChunked;
  1906. BOOL fHeaderSent;
  1907. DWORD cchStatus = strlen(szStatus) + 1;
  1908. DWORD cchHeader = strlen(szBuff) + 1;
  1909. if ( !fSendEntireResponse ) {
  1910. fHeaderSent = GetIReq()->SendHeader
  1911. (
  1912. szStatus,
  1913. cchStatus,
  1914. szBuff,
  1915. cchHeader,
  1916. fKeepConnected
  1917. );
  1918. }
  1919. else {
  1920. BOOL fSendDebugBuffers = (m_pData->m_fClientDebugMode && m_pData->m_pClientDebugBuffer);
  1921. // if we are sending both headers and body at once, we put
  1922. // all of our content into an optimized struct which we pass
  1923. // to an optimized api
  1924. HSE_SEND_ENTIRE_RESPONSE_INFO tempHseResponseInfo;
  1925. HSE_SEND_ENTIRE_RESPONSE_INFO * pHseResponseInfo = &tempHseResponseInfo;
  1926. // populate struct with header info
  1927. pHseResponseInfo->HeaderInfo.pszStatus = szStatus;
  1928. pHseResponseInfo->HeaderInfo.cchStatus = cchStatus;
  1929. pHseResponseInfo->HeaderInfo.pszHeader = szBuff;
  1930. pHseResponseInfo->HeaderInfo.cchHeader = cchHeader;
  1931. pHseResponseInfo->HeaderInfo.fKeepConn = fKeepConnected;
  1932. // populate struct with response body buffers (and, if required, debug buffers)
  1933. //
  1934. // NOTE: To send an entire response whose data (body)
  1935. // is contained in N buffers, caller must allocate N+1 buffers
  1936. // and fill buffers 1 through N with its data buffers.
  1937. // IIS will fill the extra buffer (buffer 0) with header info.
  1938. //
  1939. CResponseBuffer * pResponseBuffer = m_pData->m_pResponseBuffer;
  1940. CResponseBuffer * pClientDebugBuffer = (fSendDebugBuffers
  1941. ? m_pData->m_pClientDebugBuffer
  1942. : NULL);
  1943. DWORD cResponseBuffers = pResponseBuffer->CountOfBuffers();
  1944. DWORD cClientDebugBuffers = ( fSendDebugBuffers
  1945. ? pClientDebugBuffer->CountOfBuffers()
  1946. : 0);
  1947. Assert(cResponseBuffers);
  1948. pHseResponseInfo->cWsaBuf = 1 + cResponseBuffers + cClientDebugBuffers;
  1949. if (!tempWSABUFs.Resize(pHseResponseInfo->cWsaBuf * sizeof(WSABUF))) {
  1950. return E_OUTOFMEMORY;
  1951. }
  1952. pHseResponseInfo->rgWsaBuf = static_cast<WSABUF *>(tempWSABUFs.QueryPtr());
  1953. // zero-out the empty array slot - allows IIS to assert that it is "available"
  1954. pHseResponseInfo->rgWsaBuf[0].len = 0;
  1955. pHseResponseInfo->rgWsaBuf[0].buf = NULL;
  1956. UINT i;
  1957. // fill buffers 1 through N with our buffers 0 through N-1 (see NOTE above)
  1958. // buffers 1 through N will be debug buffers, if required, followed by response body buffers
  1959. for ( i = 0; i < cClientDebugBuffers; i++ ) {
  1960. pHseResponseInfo->rgWsaBuf[i+1].len = pClientDebugBuffer->GetBufferSize(i);
  1961. pHseResponseInfo->rgWsaBuf[i+1].buf = pClientDebugBuffer->GetBuffer(i);
  1962. }
  1963. for ( i = 0; i < cResponseBuffers; i++ ) {
  1964. pHseResponseInfo->rgWsaBuf[i + 1 + cClientDebugBuffers].len = pResponseBuffer->GetBufferSize(i);
  1965. pHseResponseInfo->rgWsaBuf[i + 1 + cClientDebugBuffers].buf = pResponseBuffer->GetBuffer(i);
  1966. }
  1967. // send entire response (headers and body) at once
  1968. if( g_fOOP ) {
  1969. fHeaderSent = GetIReq()->SendEntireResponseOop(pHseResponseInfo);
  1970. }
  1971. else {
  1972. fHeaderSent = GetIReq()->SendEntireResponse(pHseResponseInfo);
  1973. }
  1974. }
  1975. if (!fHeaderSent) {
  1976. m_pData->m_fWriteClientError = TRUE;
  1977. }
  1978. else {
  1979. m_pData->m_fHeadersWritten = TRUE;
  1980. //PERF ADD-ON
  1981. AddtoTotalByteOut(cch);
  1982. }
  1983. return(hr);
  1984. }
  1985. /*===================================================================
  1986. CResponse::Write
  1987. Writes a string to the client.
  1988. It accepts a variant as an argument and attempts to coerce that
  1989. variant into a BSTR. We convert that BSTR into an ANSI string,
  1990. and then hand that ANSI string to Response::WriteSz which sends
  1991. it back to the client.
  1992. Normally a VT_NULL variant cannot be coerced to a BSTR, but we want
  1993. VT_NULL to be a valid input, therefore we explictly handle the case of
  1994. variants of type VT_NULL. If the type of the input variant is VT_NULL
  1995. we return S_OK, but don't send anything back to the client.
  1996. If we are handed a VT_DISPATCH variant, we resolve it by repeatedly calling
  1997. Dispatch on the associated pdispVal, until we get back a variant that is not
  1998. a VT_DISPATCH. VariantChangeType would ordinarily handle this for us, but the
  1999. final resulting variant might be a VT_NULL which VariantChangeType would not
  2000. coerce into a BSTR. This is why we have to handle walking down the VT_DISPATC
  2001. variants outselves.
  2002. Parameters:
  2003. VARIANT varInput, value: the variant to be converted to string
  2004. and written to the client
  2005. Returns:
  2006. S_OK on success. E_FAIL on failure.
  2007. ===================================================================*/
  2008. STDMETHODIMP CResponse::Write(VARIANT varInput)
  2009. {
  2010. if (FAILED(CheckForTombstone()))
  2011. return E_FAIL;
  2012. HRESULT hr = S_OK;
  2013. DWORD cch;
  2014. LPSTR szT;
  2015. BSTR bstr;
  2016. VARIANT varResolved;
  2017. static char szTrue[MAX_MESSAGE_LENGTH];
  2018. static char szFalse[MAX_MESSAGE_LENGTH];
  2019. AssertValid();
  2020. // If we've already had an error writing to the client
  2021. // there is no point in proceding, so we immediately return
  2022. // with no error
  2023. if (FDontWrite())
  2024. goto lRet2;
  2025. // If already BSTR (directly or as VARIANT by ref)
  2026. bstr = VariantGetBSTR(&varInput);
  2027. if (bstr != NULL)
  2028. {
  2029. hr = WriteBSTR(bstr);
  2030. goto lRet2;
  2031. }
  2032. // If the variant passed in is a VT_DISPATCH, get its default property
  2033. if (FAILED(hr = VariantResolveDispatch(&varResolved, &varInput, IID_IResponse, IDE_RESPONSE)))
  2034. goto lRet2;
  2035. // Check if the variant in is VT_NULL
  2036. if (V_VT(&varResolved) == VT_NULL)
  2037. goto lRet; // S_OK, but don't send anything to the client
  2038. // Check if the variant in is VT_BOOL
  2039. if(V_VT(&varResolved) == VT_BOOL)
  2040. {
  2041. if (V_BOOL(&varResolved) == VARIANT_TRUE)
  2042. {
  2043. if (szTrue[0] == '\0')
  2044. cch = CchLoadStringOfId(IDS_TRUE, szTrue, MAX_MESSAGE_LENGTH);
  2045. szT = szTrue;
  2046. }
  2047. else
  2048. {
  2049. if(szFalse[0] == '\0')
  2050. cch = CchLoadStringOfId(IDS_FALSE, szFalse, MAX_MESSAGE_LENGTH);
  2051. szT = szFalse;
  2052. }
  2053. cch = strlen(szT);
  2054. if (FAILED(hr = WriteSz(szT, cch)))
  2055. {
  2056. if (E_OUTOFMEMORY == hr)
  2057. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2058. else
  2059. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2060. }
  2061. goto lRet;
  2062. }
  2063. // Coerce the variant into a bstr if necessary
  2064. if (V_VT(&varResolved) != VT_BSTR)
  2065. {
  2066. if (FAILED(hr = VariantChangeTypeEx(&varResolved, &varResolved, m_pData->m_pHitObj->GetLCID(), 0, VT_BSTR)))
  2067. {
  2068. switch (GetScode(hr))
  2069. {
  2070. case E_OUTOFMEMORY:
  2071. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2072. break;
  2073. case DISP_E_OVERFLOW:
  2074. hr = E_FAIL;
  2075. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_UNABLE_TO_CONVERT);
  2076. break;
  2077. case DISP_E_TYPEMISMATCH:
  2078. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_TYPE_MISMATCH);
  2079. break;
  2080. default:
  2081. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2082. }
  2083. goto lRet;
  2084. }
  2085. }
  2086. hr = WriteBSTR(V_BSTR(&varResolved));
  2087. lRet:
  2088. #ifdef DBG
  2089. hr =
  2090. #endif // DBG
  2091. VariantClear(&varResolved);
  2092. Assert(SUCCEEDED(hr));
  2093. lRet2:
  2094. return(hr);
  2095. }
  2096. /*===================================================================
  2097. CResponse::BinaryWrite
  2098. Function called from DispInvoke to invoke the BinaryWrite method.
  2099. Parameters:
  2100. varInput Variant which must resolve to an array of unsigned bytes
  2101. Returns:
  2102. S_OK on success. E_FAIL on failure.
  2103. ===================================================================*/
  2104. STDMETHODIMP CResponse::BinaryWrite(VARIANT varInput)
  2105. {
  2106. if (FAILED(CheckForTombstone()))
  2107. return E_FAIL;
  2108. HRESULT hr = S_OK;
  2109. DWORD nDim = 0;
  2110. long lLBound = 0;
  2111. long lUBound = 0;
  2112. void *lpData = NULL;
  2113. DWORD cch = 0;
  2114. VARIANT varResolved;
  2115. SAFEARRAY* pvarBuffer;
  2116. AssertValid();
  2117. // If we've already had an error writing to the client
  2118. // there is no point in proceding, so we immediately return
  2119. // with no error
  2120. if (FDontWrite())
  2121. goto lRet2;
  2122. // De-reference and de-dispatch the variant
  2123. if (FAILED(hr = VariantResolveDispatch(&varResolved, &varInput, IID_IResponse, IDE_RESPONSE)))
  2124. goto lRet2;
  2125. // Coerce the result into an array of VT_UI1 if needed
  2126. if (V_VT(&varResolved) != (VT_ARRAY|VT_UI1))
  2127. {
  2128. if (FAILED(hr = VariantChangeType(&varResolved, &varResolved, 0, VT_ARRAY|VT_UI1)))
  2129. {
  2130. switch (GetScode(hr))
  2131. {
  2132. case E_OUTOFMEMORY:
  2133. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2134. break;
  2135. case DISP_E_OVERFLOW:
  2136. hr = E_FAIL;
  2137. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_UNABLE_TO_CONVERT);
  2138. break;
  2139. case DISP_E_TYPEMISMATCH:
  2140. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_TYPE_MISMATCH);
  2141. break;
  2142. default:
  2143. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2144. }
  2145. goto lRet;
  2146. }
  2147. }
  2148. // We got here, so we must have a variant containing a safe array of UI1 in varResolved
  2149. pvarBuffer = V_ARRAY(&varResolved);
  2150. nDim = SafeArrayGetDim(pvarBuffer);
  2151. if (nDim != 1)
  2152. {
  2153. hr = E_INVALIDARG;
  2154. goto lRet;
  2155. }
  2156. if (FAILED(SafeArrayGetLBound(pvarBuffer, 1, &lLBound)))
  2157. {
  2158. hr = E_INVALIDARG;
  2159. goto lRet;
  2160. }
  2161. if (FAILED(SafeArrayGetUBound(pvarBuffer, 1, &lUBound)))
  2162. {
  2163. hr = E_INVALIDARG;
  2164. goto lRet;
  2165. }
  2166. if (FAILED(SafeArrayAccessData(pvarBuffer, &lpData)))
  2167. {
  2168. hr = E_INVALIDARG;
  2169. goto lRet;
  2170. }
  2171. cch = lUBound - lLBound + 1;
  2172. if (m_pData->m_fBufferingOn)
  2173. {
  2174. // Buffering is on
  2175. if (FAILED(hr = m_pData->m_pResponseBuffer->Write((char *) lpData, cch)))
  2176. {
  2177. // We can't buffer the ouput, so quit
  2178. SafeArrayUnaccessData(pvarBuffer);
  2179. if (E_OUTOFMEMORY == hr)
  2180. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2181. else
  2182. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2183. goto lRet;
  2184. }
  2185. }
  2186. else
  2187. {
  2188. // Buffering is off
  2189. // If this is the first write response, then output the headers first
  2190. if (!FHeadersWritten()) // bug 512: write hdrs even if global.asa
  2191. {
  2192. if (FAILED(hr = WriteHeaders()))
  2193. {
  2194. if (E_OUTOFMEMORY == hr)
  2195. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2196. else
  2197. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2198. }
  2199. }
  2200. // If this is not a head request we transmit the string to the clienet
  2201. if (SUCCEEDED(hr) && !IsHeadRequest() && !FDontWrite())
  2202. WriteClient((BYTE *) lpData, cch);
  2203. }
  2204. hr = SafeArrayUnaccessData(pvarBuffer);
  2205. lRet:
  2206. VariantClear(&varResolved);
  2207. lRet2:
  2208. return(hr);
  2209. }
  2210. /*===================================================================
  2211. CResponse::WriteSz
  2212. Support routine for the Write method to write the string. Unlike
  2213. CResponse::Write(), this routine takes an Ascii string, and is
  2214. not intended to be exposed as a method
  2215. Parameters:
  2216. sz - String to write as an Ascii string
  2217. cch - Length of string to write
  2218. Returns:
  2219. S_OK on success.
  2220. ===================================================================*/
  2221. HRESULT CResponse::WriteSz(CHAR *sz, DWORD cch)
  2222. {
  2223. if (FAILED(CheckForTombstone()))
  2224. return E_FAIL;
  2225. HRESULT hr = S_OK;
  2226. AssertValid();
  2227. // If we've already had an error writing to the client
  2228. // there is no point in proceding, so we immediately return
  2229. // with no error
  2230. if (FDontWrite())
  2231. goto lRet;
  2232. if (m_pData->m_fBufferingOn)
  2233. {
  2234. // Buffering is on
  2235. hr = m_pData->m_pResponseBuffer->Write(sz, cch);
  2236. }
  2237. else
  2238. {
  2239. // Buffering is off
  2240. if (!FHeadersWritten()) // bug 512: write hdrs even if global.asa
  2241. {
  2242. // This is the first response, then output the headers first
  2243. if (FAILED(hr = WriteHeaders()))
  2244. goto lRet;
  2245. }
  2246. // If this is not a head request we transmit the string to the client
  2247. if (!IsHeadRequest() && !FDontWrite())
  2248. WriteClient((BYTE *) sz, cch);
  2249. }
  2250. lRet:
  2251. return(hr);
  2252. }
  2253. /*===================================================================
  2254. CResponse::WriteBSTR
  2255. Support routine for the Write method
  2256. Parameters:
  2257. BSTR - String to write as an Ascii string
  2258. Returns:
  2259. S_OK on success.
  2260. ===================================================================*/
  2261. HRESULT CResponse::WriteBSTR(BSTR bstr)
  2262. {
  2263. CWCharToMBCS convStr;
  2264. HRESULT hr = NO_ERROR;
  2265. if (FAILED(hr = convStr.Init(bstr, m_pData->m_pHitObj->GetCodePage())));
  2266. else hr = WriteSz(convStr.GetString(), convStr.GetStringLen());
  2267. if (FAILED(hr))
  2268. {
  2269. if (E_OUTOFMEMORY == hr)
  2270. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2271. else
  2272. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2273. }
  2274. return hr;
  2275. }
  2276. /*===================================================================
  2277. CResponse::WriteBlock
  2278. Function called from DispInvoke to invoke the WriteBlock method.
  2279. Parameters:
  2280. Identifier for the HTML block
  2281. Returns:
  2282. S_OK on success. E_FAIL on failure.
  2283. ===================================================================*/
  2284. HRESULT CResponse::WriteBlock(short iBlockNumber)
  2285. {
  2286. if (FAILED(CheckForTombstone()))
  2287. return E_FAIL;
  2288. HRESULT hr = S_OK;
  2289. char* pbHTML = NULL;
  2290. ULONG cbHTML = 0;
  2291. ULONG cbSrcOffset = 0;
  2292. char* pbIncSrcFileName = NULL;
  2293. AssertValid();
  2294. // If we've already had an error writing to the client
  2295. // there is no point in proceding, so we immediately return
  2296. // with no error
  2297. if (FDontWrite())
  2298. goto lRet;
  2299. // bail out (and assert) if template is null
  2300. Assert(m_pData->m_pTemplate != NULL);
  2301. if (m_pData->m_pTemplate == NULL)
  2302. {
  2303. hr = E_FAIL;
  2304. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2305. goto lRet;
  2306. }
  2307. /*
  2308. get ptr and byte count for html block from template
  2309. NOTE: by design, this public template call cannot fail because we give it a block id
  2310. generated during template compilation (instead we assert within and after the call)
  2311. I added the return HRESULT to catch for invalid user access of this method, if a user
  2312. attempts to access this method and passes an invalid array offset it will return the
  2313. error IDE_BAD_ARRAY_INDEX, this really should not happen except for the case of a user
  2314. attempting to access this hidden method.
  2315. */
  2316. hr = m_pData->m_pTemplate->GetHTMLBlock(iBlockNumber, &pbHTML, &cbHTML, &cbSrcOffset, &pbIncSrcFileName);
  2317. if ( hr != S_OK )
  2318. {
  2319. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_BAD_ARRAY_INDEX);
  2320. goto lRet;
  2321. }
  2322. Assert(pbHTML);
  2323. Assert(cbHTML > 0);
  2324. if (m_pData->m_fBufferingOn)
  2325. {
  2326. hr = S_OK;
  2327. // Take care of Client Debugger issues
  2328. if (m_pData->m_fClientDebugMode && m_pData->m_pClientDebugBuffer)
  2329. {
  2330. if (cbSrcOffset) // only if source info is known
  2331. {
  2332. // Write a METADATA line corresponding to this block
  2333. // into ClientDebugBuffer
  2334. ULONG cbPos = m_pData->m_pResponseBuffer->BytesBuffered() + 1;
  2335. ULONG cbLen = cbHTML;
  2336. hr = m_pData->m_pClientDebugBuffer->AppendRecord(
  2337. cbPos, cbLen, cbSrcOffset, pbIncSrcFileName);
  2338. }
  2339. }
  2340. // Write the actual data
  2341. if (SUCCEEDED(hr))
  2342. hr = m_pData->m_pResponseBuffer->Write(pbHTML, cbHTML);
  2343. // Buffering is on
  2344. if (FAILED(hr))
  2345. {
  2346. if (E_OUTOFMEMORY == hr)
  2347. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2348. else
  2349. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2350. }
  2351. }
  2352. else
  2353. {
  2354. // Buffering is off
  2355. if (!FHeadersWritten()) // bug 512: write hdrs even if global.asa
  2356. {
  2357. // This is the first response.write, so output the headers
  2358. if (FAILED(hr = WriteHeaders()))
  2359. {
  2360. if (E_OUTOFMEMORY == hr)
  2361. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2362. else
  2363. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  2364. goto lRet;
  2365. }
  2366. }
  2367. // If this is not a head request we transmit the string to the clienet
  2368. if (!IsHeadRequest() && !FDontWrite())
  2369. {
  2370. WriteClient((BYTE * ) pbHTML, cbHTML);
  2371. }
  2372. }
  2373. lRet:
  2374. return(hr);
  2375. }
  2376. /*===================================================================
  2377. CResponse::GetClientVersion
  2378. Uses GetServerVariable to determine the HTTP version of the client.
  2379. Borrowed from simiarl code in w3 server Httpreq.cxx, OnVersion()
  2380. Parameters:
  2381. None
  2382. Returns:
  2383. None
  2384. ===================================================================*/
  2385. VOID CResponse::GetClientVerison()
  2386. {
  2387. if (FAILED(CheckForTombstone()))
  2388. return;
  2389. if (m_pData->m_pIReq)
  2390. {
  2391. m_pData->m_dwVersionMajor = (BYTE)m_pData->m_pIReq->QueryHttpVersionMajor();
  2392. m_pData->m_dwVersionMinor = (BYTE)m_pData->m_pIReq->QueryHttpVersionMinor();
  2393. }
  2394. else
  2395. {
  2396. // Assume version 0.9
  2397. m_pData->m_dwVersionMajor = 0;
  2398. m_pData->m_dwVersionMinor = 9;
  2399. }
  2400. }
  2401. /*===================================================================
  2402. CResponse::WriteClient
  2403. Wrapper for the ISAPI WriteClient method. Use for non-buffered responses
  2404. by the WriteSz, BinaryWrite, and WriteBlock methods.
  2405. Parameters:
  2406. sz - pointer to buffer to write
  2407. cch - Length of buffer
  2408. Returns:
  2409. HRESULT S_OK if ok
  2410. ===================================================================*/
  2411. HRESULT CResponse::WriteClient(BYTE *pb, DWORD cb)
  2412. {
  2413. HRESULT hr = S_OK;
  2414. if (m_pData->m_fChunked)
  2415. return WriteClientChunked(pb, cb);
  2416. if (FAILED(CResponse::SyncWrite(GetIReq(), (char *)pb, cb)))
  2417. m_pData->m_fWriteClientError = TRUE;
  2418. return hr;
  2419. }
  2420. /*===================================================================
  2421. CResponse::WriteClientChunked
  2422. Wrapper for the ISAPI WriteClient method using chunk transfer encoding.
  2423. Use for non-buffered responses by the WriteSz, BinaryWrite, and WriteBlock
  2424. methods if using HTTP 1.1
  2425. Each chunk is prefaced with the size of the chunk and terminated with a CRLF
  2426. Parameters:
  2427. sz - pointer to buffer to write
  2428. cch - Length of buffer
  2429. Returns:
  2430. HRESULT S_OK if ok
  2431. ===================================================================*/
  2432. HRESULT CResponse::WriteClientChunked(BYTE *pb, DWORD cb)
  2433. {
  2434. HRESULT hr = S_OK;
  2435. if (cb == 0)
  2436. return hr;
  2437. char *pchBuf = NULL;
  2438. const int cchMaxChunkOverhead = 16;
  2439. if (cb + cchMaxChunkOverhead < ALLOCA_LIMIT) {
  2440. TRY
  2441. pchBuf = (char *)_alloca(cb + cchMaxChunkOverhead);
  2442. CATCH(hrException)
  2443. // if exception occurred, just make sure that pchBuf is NULL and
  2444. // let the routine continue
  2445. pchBuf = NULL;
  2446. END_TRY
  2447. }
  2448. if (pchBuf)
  2449. {
  2450. // fill in new buffer and do one single send
  2451. char *pch = pchBuf;
  2452. // chunk length
  2453. _itoa(cb, pch, 16);
  2454. pch += strlen(pch);
  2455. // CR LF
  2456. *pch++ = '\r';
  2457. *pch++ = '\n';
  2458. // buffer
  2459. memcpy(pch, pb, cb);
  2460. pch += cb;
  2461. // CR LF
  2462. *pch++ = '\r';
  2463. *pch++ = '\n';
  2464. *pch = '\0';
  2465. // Send
  2466. if (FAILED(CResponse::SyncWrite(GetIReq(), pchBuf, DIFF(pch-pchBuf))))
  2467. m_pData->m_fWriteClientError = TRUE;
  2468. }
  2469. else
  2470. {
  2471. // do multiple sends
  2472. char szLen[12];
  2473. _itoa(cb, szLen, 16);
  2474. DWORD cchLen = strlen(szLen);
  2475. szLen[cchLen++] = '\r';
  2476. szLen[cchLen++] = '\n';
  2477. szLen[cchLen] = '\0';
  2478. // send length
  2479. if (FAILED(CResponse::SyncWrite(GetIReq(), szLen, cchLen)))
  2480. m_pData->m_fWriteClientError = TRUE;
  2481. // send buffer
  2482. else if (FAILED(CResponse::SyncWrite(GetIReq(), (char *)pb, cb)))
  2483. m_pData->m_fWriteClientError = TRUE;
  2484. // send trailing CR LF
  2485. else if (FAILED(CResponse::SyncWrite(GetIReq(), "\r\n", 2)))
  2486. m_pData->m_fWriteClientError = TRUE;
  2487. }
  2488. return hr;
  2489. }
  2490. /*===================================================================
  2491. CResponse::AppendHeader
  2492. Functions to add headers of different kind
  2493. (hardcoded and user-supplied)
  2494. Parameters:
  2495. Name, Value
  2496. Returns:
  2497. HRESULT S_OK if ok
  2498. ===================================================================*/
  2499. HRESULT CResponse::AppendHeader(BSTR wszName, BSTR wszValue)
  2500. {
  2501. CHTTPHeader *pHeader = new CHTTPHeader;
  2502. if (!pHeader)
  2503. return E_OUTOFMEMORY;
  2504. if (FAILED(pHeader->InitHeader(wszName, wszValue,m_pData->m_pHitObj->GetCodePage())))
  2505. {
  2506. delete pHeader;
  2507. return E_FAIL;
  2508. }
  2509. m_pData->AppendHeaderToList(pHeader);
  2510. return S_OK;
  2511. }
  2512. HRESULT CResponse::AppendHeader(char *szName, BSTR wszValue)
  2513. {
  2514. CHTTPHeader *pHeader = new CHTTPHeader;
  2515. if (!pHeader)
  2516. return E_OUTOFMEMORY;
  2517. if (FAILED(pHeader->InitHeader(szName, wszValue,m_pData->m_pHitObj->GetCodePage())))
  2518. {
  2519. delete pHeader;
  2520. return E_FAIL;
  2521. }
  2522. m_pData->AppendHeaderToList(pHeader);
  2523. return S_OK;
  2524. }
  2525. HRESULT CResponse::AppendHeader(char *szName, char *szValue, BOOL fCopyValue)
  2526. {
  2527. CHTTPHeader *pHeader = new CHTTPHeader;
  2528. if (!pHeader)
  2529. return E_OUTOFMEMORY;
  2530. if (FAILED(pHeader->InitHeader(szName, szValue, fCopyValue)))
  2531. {
  2532. delete pHeader;
  2533. return E_FAIL;
  2534. }
  2535. m_pData->AppendHeaderToList(pHeader);
  2536. return S_OK;
  2537. }
  2538. HRESULT CResponse::AppendHeader(char *szName, long lValue)
  2539. {
  2540. CHTTPHeader *pHeader = new CHTTPHeader;
  2541. if (!pHeader)
  2542. return E_OUTOFMEMORY;
  2543. if (FAILED(pHeader->InitHeader(szName, lValue)))
  2544. {
  2545. delete pHeader;
  2546. return E_FAIL;
  2547. }
  2548. m_pData->AppendHeaderToList(pHeader);
  2549. return S_OK;
  2550. }
  2551. /*===================================================================
  2552. CResponse::get_ContentType
  2553. Functions called from DispInvoke to return the ContentType property.
  2554. Parameters:
  2555. pbstrContentTypeRet BSTR FAR *, return value: pointer to the ContentType as a string
  2556. Returns:
  2557. HRESULT S_OK if ok
  2558. ===================================================================*/
  2559. STDMETHODIMP CResponse::get_ContentType
  2560. (
  2561. BSTR FAR * pbstrContentTypeRet
  2562. )
  2563. {
  2564. if (FAILED(CheckForTombstone()))
  2565. return E_FAIL;
  2566. HRESULT hr = S_OK;
  2567. hr = SysAllocStringFromSz((char *)PContentType(), 0, pbstrContentTypeRet);
  2568. if (FAILED(hr))
  2569. {
  2570. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2571. hr = E_FAIL;
  2572. }
  2573. return(hr);
  2574. }
  2575. /*===================================================================
  2576. CResponse::put_ContentType
  2577. Functions called from DispInvoke to set the ContentType property.
  2578. Parameters:
  2579. bstrContentType BSTR, value: the ContentType as a string
  2580. Returns:
  2581. HRESULT S_OK if ok
  2582. ===================================================================*/
  2583. STDMETHODIMP CResponse::put_ContentType
  2584. (
  2585. BSTR bstrContentType
  2586. )
  2587. {
  2588. if (FAILED(CheckForTombstone()))
  2589. return E_FAIL;
  2590. HRESULT hr = S_OK;
  2591. CWCharToMBCS convStr;
  2592. if (FHeadersWritten())
  2593. {
  2594. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  2595. hr = E_FAIL;
  2596. goto lRet;
  2597. }
  2598. if (m_pData->m_pszContentType) {
  2599. free(m_pData->m_pszContentType);
  2600. m_pData->m_pszContentType = NULL;
  2601. }
  2602. if (FAILED(hr = convStr.Init(bstrContentType)));
  2603. else if ((m_pData->m_pszContentType = convStr.GetString(TRUE)) == NULL) {
  2604. hr = E_OUTOFMEMORY;
  2605. }
  2606. lRet:
  2607. if (hr == E_OUTOFMEMORY) {
  2608. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2609. SetLastError((DWORD)E_OUTOFMEMORY);
  2610. }
  2611. return(hr);
  2612. }
  2613. /*===================================================================
  2614. CResponse::get_Status
  2615. Function called from DispInvoke to return the Status property.
  2616. Parameters:
  2617. pbstrStatusRet BSTR FAR *, return value: pointer to the Status as a string
  2618. Returns:
  2619. HRESULT S_OK if ok
  2620. ===================================================================*/
  2621. STDMETHODIMP CResponse::get_Status
  2622. (
  2623. BSTR FAR * pbstrStatusRet
  2624. )
  2625. {
  2626. if (FAILED(CheckForTombstone()))
  2627. return E_FAIL;
  2628. HRESULT hr = S_OK;
  2629. if (m_pData->m_pszStatus)
  2630. hr = SysAllocStringFromSz(m_pData->m_pszStatus, 0, pbstrStatusRet);
  2631. else
  2632. hr = SysAllocStringFromSz((CHAR *)s_szDefaultStatus, 0, pbstrStatusRet);
  2633. if (FAILED(hr))
  2634. {
  2635. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2636. hr = E_FAIL;
  2637. }
  2638. return(hr);
  2639. }
  2640. /*===================================================================
  2641. CResponse::put_Status
  2642. Function called from DispInvoke to set the ContentType property.
  2643. Parameters:
  2644. bstrStatus BSTR, value: the status as a string
  2645. Returns:
  2646. HRESULT S_OK if ok
  2647. ===================================================================*/
  2648. STDMETHODIMP CResponse::put_Status
  2649. (
  2650. BSTR bstrStatus
  2651. )
  2652. {
  2653. DWORD dwStatus = 200;
  2654. if (FAILED(CheckForTombstone()))
  2655. return E_FAIL;
  2656. HRESULT hr = S_OK;
  2657. CWCharToMBCS convStr;
  2658. if (FHeadersWritten())
  2659. {
  2660. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  2661. hr = E_FAIL;
  2662. goto lRet;
  2663. }
  2664. if (m_pData->m_pszStatus) {
  2665. free(m_pData->m_pszStatus);
  2666. m_pData->m_pszStatus = NULL;
  2667. }
  2668. if (FAILED(hr = convStr.Init(bstrStatus)));
  2669. else if ((m_pData->m_pszStatus = convStr.GetString(TRUE)) == NULL) {
  2670. hr = E_OUTOFMEMORY;
  2671. }
  2672. else {
  2673. dwStatus = atol(m_pData->m_pszStatus);
  2674. GetIReq()->SetDwHttpStatusCode(dwStatus);
  2675. }
  2676. lRet:
  2677. if (hr == E_OUTOFMEMORY) {
  2678. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2679. SetLastError((DWORD)E_OUTOFMEMORY);
  2680. }
  2681. return(hr);
  2682. }
  2683. /*===================================================================
  2684. CResponse::get_Expires
  2685. Function called from DispInvoke to return the Expires property.
  2686. Parameters:
  2687. plExpiresTimeRet long *, return value: pointer to number of minutes until response expires
  2688. Returns:
  2689. HRESULT S_OK if ok
  2690. ===================================================================*/
  2691. STDMETHODIMP CResponse::get_Expires
  2692. (
  2693. VARIANT * pvarExpiresTimeRet
  2694. )
  2695. {
  2696. if (FAILED(CheckForTombstone()))
  2697. return E_FAIL;
  2698. // return early if we can
  2699. //
  2700. if (m_pData->m_tExpires == -1)
  2701. {
  2702. V_VT(pvarExpiresTimeRet) = VT_NULL;
  2703. return S_OK;
  2704. }
  2705. // get the current time
  2706. //
  2707. time_t tNow;
  2708. time(&tNow);
  2709. // get the time difference and round to the nearest minute
  2710. //
  2711. V_VT(pvarExpiresTimeRet) = VT_I4;
  2712. V_I4(pvarExpiresTimeRet) = long((difftime(m_pData->m_tExpires, tNow) / 60) + 0.5);
  2713. return S_OK;
  2714. }
  2715. /*===================================================================
  2716. CResponse::put_Expires
  2717. Functions called from DispInvoke to set the expires property.
  2718. Parameters:
  2719. iValue int, value: the number of minutes until response should expire
  2720. Returns:
  2721. HRESULT S_OK if ok
  2722. ===================================================================*/
  2723. STDMETHODIMP CResponse::put_Expires
  2724. (
  2725. long lExpiresMinutes
  2726. )
  2727. {
  2728. if (FAILED(CheckForTombstone()))
  2729. return E_FAIL;
  2730. if (FHeadersWritten())
  2731. {
  2732. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  2733. return E_FAIL;
  2734. }
  2735. // get the current time
  2736. //
  2737. time_t tNow;
  2738. time(&tNow);
  2739. time_t tRelativeTime;
  2740. // add the number of minuites. (must convert to seconds first)
  2741. //
  2742. tRelativeTime = lExpiresMinutes * 60;
  2743. if ((lExpiresMinutes < 0 && tRelativeTime > 0)
  2744. || (lExpiresMinutes > 0 && tRelativeTime < 0))
  2745. {
  2746. // overflow, tRelativeTime could be a small positive integer if lExpiresMinutes is
  2747. // some value like 0x80000010
  2748. // tNow will be overflowed if tRelativeTime is negative
  2749. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_COOKIE_BAD_EXPIRATION);
  2750. return E_FAIL;
  2751. }
  2752. tNow += tRelativeTime;
  2753. // Store the date if
  2754. // a. No date was stored previously
  2755. // b. This date comes before the previously set date.
  2756. //
  2757. if (m_pData->m_tExpires == -1 || tNow < m_pData->m_tExpires)
  2758. {
  2759. struct tm *ptmGMT = gmtime(&tNow);
  2760. if (ptmGMT == NULL)
  2761. {
  2762. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_COOKIE_BAD_EXPIRATION);
  2763. return E_FAIL;
  2764. }
  2765. m_pData->m_tExpires = tNow;
  2766. }
  2767. // convert time to GMT
  2768. return S_OK;
  2769. }
  2770. /*===================================================================
  2771. CResponse::get_ExpiresAbsolute
  2772. Function called from DispInvoke to return the ExpiresAbsolute property.
  2773. Parameters:
  2774. pbstrTimeRet BSTR *, return value: pointer to string that will contain
  2775. the time response should expire
  2776. Returns:
  2777. HRESULT S_OK if ok
  2778. ===================================================================*/
  2779. STDMETHODIMP CResponse::get_ExpiresAbsolute
  2780. (
  2781. VARIANT *pvarTimeRet
  2782. )
  2783. {
  2784. if (FAILED(CheckForTombstone()))
  2785. return E_FAIL;
  2786. V_VT(pvarTimeRet) = VT_DATE;
  2787. CTimeToVariantDate(&m_pData->m_tExpires, &V_DATE(pvarTimeRet));
  2788. return S_OK;
  2789. }
  2790. /*===================================================================
  2791. CResponse::put_ExpiresAbsolute
  2792. Function called from DispInvoke to set the ExpiresAbsolute property.
  2793. Parameters:
  2794. pbstrTime BSTR, value: time response should expire
  2795. Returns:
  2796. HRESULT S_OK if ok
  2797. ===================================================================*/
  2798. STDMETHODIMP CResponse::put_ExpiresAbsolute
  2799. (
  2800. DATE dtExpires
  2801. )
  2802. {
  2803. if (FAILED(CheckForTombstone()))
  2804. return E_FAIL;
  2805. if (FHeadersWritten())
  2806. {
  2807. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  2808. return E_FAIL;
  2809. }
  2810. if (int(dtExpires) == 0) // time specified but no date (assume today)
  2811. {
  2812. time_t tToday; // get the date and time now
  2813. DATE dtToday;
  2814. time(&tToday);
  2815. struct tm *tmToday = localtime(&tToday);
  2816. tmToday->tm_hour = tmToday->tm_min = tmToday->tm_sec = 0; // reset to midnight
  2817. tToday = mktime(tmToday);
  2818. if (FAILED(CTimeToVariantDate(&tToday, &dtToday)))
  2819. return E_FAIL;
  2820. dtExpires += dtToday;
  2821. }
  2822. time_t tExpires;
  2823. if (FAILED(VariantDateToCTime(dtExpires, &tExpires)))
  2824. {
  2825. ExceptionId(IID_IWriteCookie, IDE_RESPONSE, IDE_COOKIE_BAD_EXPIRATION);
  2826. return E_FAIL;
  2827. }
  2828. if (m_pData->m_tExpires == -1 || tExpires < m_pData->m_tExpires)
  2829. {
  2830. m_pData->m_tExpires = tExpires;
  2831. }
  2832. return S_OK;
  2833. }
  2834. /*===================================================================
  2835. CResponse::put_Buffer
  2836. Function called from DispInvoke to set the Buffer property.
  2837. Parameters:
  2838. fIsBuffering VARIANT_BOOL, if true turn on buffering of HTML output
  2839. Returns:
  2840. HRESULT S_OK if ok
  2841. Side Effects:
  2842. Turning buffering on will cause memory to be allocated.
  2843. ===================================================================*/
  2844. STDMETHODIMP CResponse::put_Buffer
  2845. (
  2846. VARIANT_BOOL fIsBuffering
  2847. )
  2848. {
  2849. if (FAILED(CheckForTombstone()))
  2850. return E_FAIL;
  2851. // Assume TRUE if not 0
  2852. if (fIsBuffering != VARIANT_FALSE)
  2853. fIsBuffering = VARIANT_TRUE;
  2854. // Ignore no change requests
  2855. if ((fIsBuffering == VARIANT_TRUE) && m_pData->m_fBufferingOn)
  2856. return S_OK;
  2857. if ((fIsBuffering == VARIANT_FALSE) && !m_pData->m_fBufferingOn)
  2858. return S_OK;
  2859. // Ignore if change is not allowed to change (Client Dedug)
  2860. if (m_pData->m_fClientDebugMode)
  2861. return S_OK;
  2862. // Set the new value (error if cannot change)
  2863. if (fIsBuffering == VARIANT_TRUE)
  2864. {
  2865. if (FHeadersWritten())
  2866. {
  2867. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  2868. return E_FAIL;
  2869. }
  2870. m_pData->m_fBufferingOn = TRUE;
  2871. }
  2872. else // if (fIsBuffering == VARIANT_FALSE)
  2873. {
  2874. if ((m_pData->m_pResponseBuffer->BytesBuffered() > 0) ||
  2875. FHeadersWritten())
  2876. {
  2877. // If we already buffered some output it is too late to go back
  2878. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_CANT_STOP_BUFFER);
  2879. return E_FAIL;
  2880. }
  2881. m_pData->m_fBufferingOn = FALSE;
  2882. }
  2883. return S_OK;
  2884. }
  2885. /*===================================================================
  2886. CResponse::get_Buffer
  2887. Function called from DispInvoke to get the Buffer property.
  2888. Parameters:
  2889. fIsBuffering VARIANT_BOOL, value: if true turn buffering of HTML output
  2890. is turned on
  2891. Returns:
  2892. HRESULT S_OK if ok
  2893. Side Effects:
  2894. None
  2895. ===================================================================*/
  2896. STDMETHODIMP CResponse::get_Buffer
  2897. (
  2898. VARIANT_BOOL *fIsBuffering
  2899. )
  2900. {
  2901. if (FAILED(CheckForTombstone()))
  2902. return E_FAIL;
  2903. HRESULT hr = S_OK;
  2904. if (m_pData->m_fBufferingOn)
  2905. *fIsBuffering = VARIANT_TRUE;
  2906. else
  2907. *fIsBuffering = VARIANT_FALSE;
  2908. return(hr);
  2909. }
  2910. /*===================================================================
  2911. CResponse::Redirect
  2912. Function called from DispInvoke to invoke the Redirect method.
  2913. Parameters:
  2914. bstrURL Unicode BSTR Value: URL to rediect to
  2915. Returns:
  2916. S_OK on success. E_FAIL on failure.
  2917. ===================================================================*/
  2918. STDMETHODIMP CResponse::Redirect(BSTR bstrURL)
  2919. {
  2920. if (FAILED(CheckForTombstone()))
  2921. return E_FAIL;
  2922. AssertValid();
  2923. HRESULT hr = S_OK;
  2924. DWORD cch = 0;
  2925. DWORD cchURL = 0;
  2926. DWORD cchMessage = 0;
  2927. char *szURL = NULL;
  2928. char *szMessage = NULL;
  2929. DWORD cchEncodedURL;
  2930. char *pszEncodedURL = NULL;
  2931. char *pszURL = NULL;
  2932. CWCharToMBCS convURL;
  2933. STACK_BUFFER( tempURL, 256 );
  2934. STACK_BUFFER( tempMessage, 256 + 512 );
  2935. // Insist that we have a non-zero length URL
  2936. if (bstrURL)
  2937. cchURL = wcslen(bstrURL);
  2938. if (cchURL == 0)
  2939. {
  2940. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_NO_URL);
  2941. hr = E_FAIL;
  2942. goto lRet;
  2943. }
  2944. // Check that we haven't already passed data back to the client
  2945. if (FHeadersWritten())
  2946. {
  2947. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  2948. hr = E_FAIL;
  2949. goto lRet;
  2950. }
  2951. // If buffering is on, clear any pending output
  2952. if (m_pData->m_fBufferingOn)
  2953. Clear();
  2954. // turn buffering on for this response.
  2955. m_pData->m_fBufferingOn = TRUE;
  2956. // BUGBUG - should this be 65001?
  2957. if (FAILED(hr = convURL.Init(bstrURL, m_pData->m_pHitObj->GetCodePage()))) {
  2958. if (hr == E_OUTOFMEMORY)
  2959. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2960. goto lRet;
  2961. }
  2962. pszURL = convURL.GetString();
  2963. cchEncodedURL = URLPathEncodeLen(pszURL);
  2964. if (!tempURL.Resize(cchEncodedURL)) {
  2965. hr = E_OUTOFMEMORY;
  2966. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2967. goto lRet;
  2968. }
  2969. pszEncodedURL = (CHAR *)tempURL.QueryPtr();
  2970. URLPathEncode(pszEncodedURL, pszURL);
  2971. // We need to alloccate memory to build the body redirection message.
  2972. // If our memory requirement is small we allocate memory from the stack,
  2973. // otherwise we allocate from the heap.
  2974. cchMessage = strlen(pszEncodedURL);
  2975. cchMessage += 512; // Allow space for sub-strings coming from resource file.
  2976. if (!tempMessage.Resize(cchMessage)) {
  2977. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  2978. hr = E_OUTOFMEMORY;
  2979. goto lRet;
  2980. }
  2981. szMessage = (char *)tempMessage.QueryPtr();
  2982. // Build the body redirection message
  2983. // Redirect(URL), URL must be a valid URL, that is, no DBCS string.
  2984. cch = CchLoadStringOfId(IDE_RESPONSE_REDIRECT1, szMessage, cchMessage);
  2985. strcpy(szMessage + cch, pszEncodedURL);
  2986. cch += strlen(pszEncodedURL);
  2987. cch += CchLoadStringOfId(IDE_RESPONSE_REDIRECT2, szMessage + cch, cchMessage - cch);
  2988. // Set the status to redirect
  2989. put_Status(L"302 Object moved");
  2990. // Add the location header
  2991. AppendHeader("Location", pszEncodedURL, TRUE);
  2992. // Transmit redirect text to the client, headers will be
  2993. // sent automatically
  2994. if (FAILED(WriteSz(szMessage, cch)))
  2995. {
  2996. hr = E_FAIL;
  2997. goto lRet;
  2998. }
  2999. // No further processing of the script
  3000. End();
  3001. lRet:
  3002. return(hr);
  3003. }
  3004. /*===================================================================
  3005. CResponse::Add
  3006. Functions called from DispInvoke to Add a header.
  3007. This is a compatibility for the ISBU controls.
  3008. Parameters:
  3009. bstrHeaderValue Unicode BSTR, value: the value of header
  3010. bstrHeaderName Unicode BSTR, value: the name of the header
  3011. Returns:
  3012. HRESULT S_OK if ok
  3013. ===================================================================*/
  3014. STDMETHODIMP CResponse::Add
  3015. (
  3016. BSTR bstrHeaderValue,
  3017. BSTR bstrHeaderName
  3018. )
  3019. {
  3020. if (FAILED(CheckForTombstone()))
  3021. return E_FAIL;
  3022. return AddHeader(bstrHeaderName, bstrHeaderValue);
  3023. }
  3024. /*===================================================================
  3025. CResponse::AddHeader
  3026. Functions called from DispInvoke to Add a header.
  3027. Parameters:
  3028. bstrHeaderName Unicode BSTR, value: the name of the header
  3029. bstrHeaderValue Unicode BSTR, value: the value of header
  3030. Returns:
  3031. HRESULT S_OK if ok
  3032. ===================================================================*/
  3033. STDMETHODIMP CResponse::AddHeader
  3034. (
  3035. BSTR bstrHeaderName,
  3036. BSTR bstrHeaderValue
  3037. )
  3038. {
  3039. if (FAILED(CheckForTombstone()))
  3040. return E_FAIL;
  3041. AssertValid();
  3042. if (FHeadersWritten())
  3043. {
  3044. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  3045. return E_FAIL;
  3046. }
  3047. if (bstrHeaderName == NULL || wcslen(bstrHeaderName) == 0)
  3048. {
  3049. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_EXPECTING_STR);
  3050. return E_FAIL;
  3051. }
  3052. if (FAILED(AppendHeader(bstrHeaderName, bstrHeaderValue)))
  3053. {
  3054. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3055. SetLastError((DWORD)E_OUTOFMEMORY);
  3056. return E_OUTOFMEMORY;
  3057. }
  3058. return S_OK;
  3059. }
  3060. /*===================================================================
  3061. CResponse::Clear
  3062. Erases all output waiting in buffer.
  3063. Parameters
  3064. None
  3065. Returns:
  3066. HRESULT S_OK if ok
  3067. ===================================================================*/
  3068. STDMETHODIMP CResponse::Clear()
  3069. {
  3070. if (FAILED(CheckForTombstone()))
  3071. return E_FAIL;
  3072. HRESULT hr = S_OK;
  3073. if (m_pData->m_fClientDebugMode && m_pData->m_fClientDebugFlushIgnored)
  3074. {
  3075. // Clear after flush in ClientDebugMode is an error
  3076. hr = E_FAIL;
  3077. ExceptionId(IID_IResponse, IDE_RESPONSE,
  3078. IDE_RESPONSE_CLEAR_AFTER_FLUSH_IN_DEBUG);
  3079. }
  3080. else if (!m_pData->m_fBufferingOn)
  3081. {
  3082. hr = E_FAIL;
  3083. ExceptionId(IID_IResponse, IDE_RESPONSE,
  3084. IDE_RESPONSE_BUFFER_NOT_ON);
  3085. }
  3086. else
  3087. {
  3088. AssertValid();
  3089. hr = m_pData->m_pResponseBuffer->Clear();
  3090. if (SUCCEEDED(hr))
  3091. {
  3092. if (m_pData->m_fClientDebugMode && m_pData->m_pClientDebugBuffer)
  3093. hr = m_pData->m_pClientDebugBuffer->ClearAndStart();
  3094. }
  3095. if (FAILED(hr))
  3096. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  3097. }
  3098. return(hr);
  3099. }
  3100. /*===================================================================
  3101. CResponse::Flush
  3102. Sends out all HTML waiting in the buffer.
  3103. Returns:
  3104. HRESULT S_OK if ok
  3105. ===================================================================*/
  3106. STDMETHODIMP CResponse::Flush()
  3107. {
  3108. if (FAILED(CheckForTombstone()))
  3109. return E_FAIL;
  3110. HRESULT hr = S_OK;
  3111. AssertValid();
  3112. if (!m_pData->m_fBufferingOn)
  3113. {
  3114. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_BUFFER_NOT_ON);
  3115. hr = E_FAIL;
  3116. goto lRet;
  3117. }
  3118. // Ignore Response.Flush() in Client Debug Mode
  3119. if (m_pData->m_fClientDebugMode)
  3120. {
  3121. m_pData->m_fClientDebugFlushIgnored = TRUE;
  3122. goto lRet;
  3123. }
  3124. // We mark this response as having had flush called so
  3125. // that we won't try to do keep-alive
  3126. m_pData->m_fFlushed = TRUE;
  3127. if (!FHeadersWritten()) // bug 512: write hdrs even if global.asa
  3128. {
  3129. if (FAILED(WriteHeaders()))
  3130. {
  3131. if (E_OUTOFMEMORY == hr)
  3132. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3133. else
  3134. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  3135. goto lRet;
  3136. }
  3137. }
  3138. if (FAILED(hr = m_pData->m_pResponseBuffer->Flush(GetIReq())))
  3139. {
  3140. if (E_OUTOFMEMORY == hr)
  3141. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3142. else
  3143. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_UNEXPECTED);
  3144. }
  3145. lRet:
  3146. return(hr);
  3147. }
  3148. /*===================================================================
  3149. CResponse::FinalFlush
  3150. FinalFlush is called if a script terminates without having yet sent
  3151. the response headers. This means we can use the Content-Length and
  3152. Connection: Keep-Alive headers to increase efficiency. We add those headers,
  3153. then send all headers, and all waiting output.
  3154. Returns:
  3155. HRESULT S_OK if ok
  3156. ===================================================================*/
  3157. HRESULT CResponse::FinalFlush(HRESULT hr_Status)
  3158. {
  3159. if (FAILED(CheckForTombstone()))
  3160. return E_FAIL;
  3161. HRESULT hr = S_OK;
  3162. DWORD dwRequestStatus = (FAILED(hr_Status) ? HSE_STATUS_ERROR : HSE_STATUS_SUCCESS);
  3163. BOOL fWriteBodyWithHeaders = FALSE; // append body to headers?
  3164. AssertValid();
  3165. // If the headers have not yet been sent, send them now
  3166. if (!FHeadersWritten() && !FDontWrite())
  3167. {
  3168. DWORD dwLength = m_pData->m_fBufferingOn ? m_pData->m_pResponseBuffer->BytesBuffered() : 0;
  3169. // If there was an error and and nothing is buffered,
  3170. // send "server error" instead of an empty 200 OK response
  3171. if (FAILED(hr_Status) && dwLength == 0)
  3172. {
  3173. Handle500Error(IDE_500_SERVER_ERROR, GetIReq());
  3174. goto lRet;
  3175. }
  3176. // If buffering, add the Content: Keep-Alive, and Content-Length headers
  3177. if (m_pData->m_fBufferingOn)
  3178. {
  3179. // If client is less then HTTP 1.1 add Keep-Alive header
  3180. if (!(m_pData->m_dwVersionMinor >= 1 && m_pData->m_dwVersionMajor >=1))
  3181. {
  3182. AppendHeader("Connection", "Keep-Alive");
  3183. }
  3184. if (m_pData->m_fClientDebugMode && m_pData->m_pClientDebugBuffer)
  3185. {
  3186. // end the buffer with end of METADATA
  3187. m_pData->m_pClientDebugBuffer->End();
  3188. dwLength += m_pData->m_pClientDebugBuffer->BytesBuffered();
  3189. }
  3190. AppendHeader("Content-Length", (LONG)dwLength);
  3191. dwRequestStatus = HSE_STATUS_SUCCESS_AND_KEEP_CONN;
  3192. // if
  3193. // buffering is on,
  3194. // we are in-proc,
  3195. // headers are not yet written, and
  3196. // there is body
  3197. // then we can optimize by sending both headers and body at once
  3198. //
  3199. if (!IsHeadRequest() && dwLength)
  3200. {
  3201. fWriteBodyWithHeaders = TRUE;
  3202. }
  3203. }
  3204. // If buffering, tell WriteHeaders to append response body
  3205. if (FAILED(hr = WriteHeaders(fWriteBodyWithHeaders)))
  3206. {
  3207. dwRequestStatus = HSE_STATUS_ERROR;
  3208. goto lRet;
  3209. }
  3210. }
  3211. // if we sent body with headers, we are done
  3212. if (fWriteBodyWithHeaders)
  3213. {
  3214. goto lRet;
  3215. }
  3216. // If we have a response buffer, and we have not yet
  3217. // had a write client error, flush the buffer
  3218. // CONSIDER: Rearrange the tests for greater efficiency
  3219. if (m_pData->m_fBufferingOn && !FDontWrite() && !IsHeadRequest())
  3220. {
  3221. // client debug buffer goes first
  3222. if (m_pData->m_fClientDebugMode && m_pData->m_pClientDebugBuffer)
  3223. {
  3224. if (FAILED(hr = m_pData->m_pClientDebugBuffer->Flush(GetIReq())))
  3225. {
  3226. dwRequestStatus = HSE_STATUS_ERROR;
  3227. goto lRet;
  3228. }
  3229. }
  3230. // response buffer follows
  3231. if (FAILED(hr = m_pData->m_pResponseBuffer->Flush(GetIReq())))
  3232. {
  3233. dwRequestStatus = HSE_STATUS_ERROR;
  3234. goto lRet;
  3235. }
  3236. }
  3237. else if (m_pData->m_fBufferingOn)
  3238. {
  3239. // If we have a response buffer and have had a write client
  3240. // error, or this is a head request, we clear the buffers
  3241. if (m_pData->m_pClientDebugBuffer)
  3242. m_pData->m_pClientDebugBuffer->Clear();
  3243. m_pData->m_pResponseBuffer->Clear();
  3244. }
  3245. else if (m_pData->m_fChunked && !FDontWrite() && !IsHeadRequest())
  3246. {
  3247. // Send closing 0-length chunk, we don't use any entity headers
  3248. // so this is just 0 followed by two CRLF
  3249. if (FAILED(CResponse::SyncWrite(GetIReq(), "0\r\n\r\n", 5)))
  3250. {
  3251. m_pData->m_fWriteClientError = TRUE;
  3252. }
  3253. }
  3254. lRet:
  3255. // Done with this request
  3256. if (m_pData->m_fWriteClientError)
  3257. dwRequestStatus = HSE_STATUS_ERROR; // We had a write error at some point
  3258. GetIReq()->ServerSupportFunction(
  3259. HSE_REQ_DONE_WITH_SESSION,
  3260. &dwRequestStatus,
  3261. 0,
  3262. NULL);
  3263. if (m_pData->m_pHitObj)
  3264. m_pData->m_pHitObj->SetDoneWithSession();
  3265. // We no longer need access to the template
  3266. // which we AddRef'd in Response::ReinitTemplate
  3267. if (m_pData->m_pTemplate)
  3268. {
  3269. m_pData->m_pTemplate->Release();
  3270. m_pData->m_pTemplate = NULL;
  3271. }
  3272. return(hr);
  3273. }
  3274. /*===================================================================
  3275. CResponse::End
  3276. Stops all further template processing, and returns the current response
  3277. Returns:
  3278. HRESULT S_OK if ok
  3279. ===================================================================*/
  3280. STDMETHODIMP CResponse::End()
  3281. {
  3282. if (FAILED(CheckForTombstone()))
  3283. return E_FAIL;
  3284. if (m_pData->m_pfnGetScript != NULL && m_pData->m_pvGetScriptContext != NULL)
  3285. {
  3286. int i = 0;
  3287. CScriptEngine* pEngine;
  3288. while (NULL != (pEngine = (*m_pData->m_pfnGetScript)(i, m_pData->m_pvGetScriptContext)))
  3289. {
  3290. pEngine->InterruptScript(/*fAbnormal*/ FALSE);
  3291. i++;
  3292. }
  3293. }
  3294. m_pData->m_fResponseAborted = TRUE;
  3295. return S_OK;
  3296. }
  3297. /*===================================================================
  3298. CResponse::AppendToLog
  3299. Append a string to the current log entry.
  3300. Parameters
  3301. bstrLogEntry Unicode BSTR, value: string to add to log entry
  3302. Returns:
  3303. HRESULT S_OK if ok
  3304. Side Effects:
  3305. NONE
  3306. ===================================================================*/
  3307. STDMETHODIMP CResponse::AppendToLog(BSTR bstrLogEntry)
  3308. {
  3309. if (FAILED(CheckForTombstone()))
  3310. return E_FAIL;
  3311. AssertValid();
  3312. HRESULT hr = S_OK;
  3313. CWCharToMBCS convEntry;
  3314. // BUGBUG - should this be 65001?
  3315. if (FAILED(hr = convEntry.Init(bstrLogEntry, m_pData->m_pHitObj->GetCodePage())));
  3316. else hr = GetIReq()->AppendLogParameter(convEntry.GetString());
  3317. if (FAILED(hr)) {
  3318. if (hr == E_OUTOFMEMORY) {
  3319. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3320. }
  3321. else {
  3322. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_LOG_FAILURE);
  3323. }
  3324. }
  3325. return(hr);
  3326. }
  3327. /*===================================================================
  3328. CResponse::get_Cookies
  3329. Return the write-only response cookie dictionary
  3330. Parameters
  3331. bstrLogEntry Unicode BSTR, value: string to add to log entry
  3332. Returns:
  3333. HRESULT S_OK if ok
  3334. ===================================================================*/
  3335. STDMETHODIMP CResponse::get_Cookies(IRequestDictionary **ppDictReturn)
  3336. {
  3337. if (FAILED(CheckForTombstone()))
  3338. return E_FAIL;
  3339. AssertValid();
  3340. return m_pData->m_WriteCookies.QueryInterface(IID_IRequestDictionary, reinterpret_cast<void **>(ppDictReturn));
  3341. }
  3342. #ifdef DBG
  3343. /*===================================================================
  3344. CResponse::AssertValid
  3345. Test to make sure that the CResponse object is currently correctly formed
  3346. and assert if it is not.
  3347. Returns:
  3348. Side effects:
  3349. None.
  3350. ===================================================================*/
  3351. VOID CResponse::AssertValid() const
  3352. {
  3353. Assert(m_fInited);
  3354. Assert(m_pData);
  3355. Assert(m_pData->m_pResponseBuffer);
  3356. Assert(m_pData->m_pIReq);
  3357. }
  3358. #endif // DBG
  3359. /*===================================================================
  3360. IsHeadRequest
  3361. This function will check the REQUEST_METHOD and set a BOOL flag in
  3362. the class. If the request method is HEAD the flag will be set to
  3363. true.
  3364. Also the flag must be reset with each init/reinit call
  3365. m_IsHeadRequest == 0 // HEAD request status not set
  3366. m_IsHeadRequest == 1 // Not a HEAD request
  3367. m_IsHeadRequest == 2 // is a HEAD request
  3368. Parameters
  3369. Returns:
  3370. void
  3371. Side effects:
  3372. sets status flag m_IsHeadRequest
  3373. ===================================================================*/
  3374. BOOL CResponse::IsHeadRequest(void)
  3375. {
  3376. if (FAILED(CheckForTombstone()))
  3377. return FALSE;
  3378. AssertValid();
  3379. if (m_pData->m_IsHeadRequest != 0)
  3380. return ( m_pData->m_IsHeadRequest == 2);
  3381. if (stricmp(GetIReq()->QueryPszMethod(), "HEAD") == 0 )
  3382. m_pData->m_IsHeadRequest = 2;
  3383. else
  3384. m_pData->m_IsHeadRequest = 1;
  3385. return ( m_pData->m_IsHeadRequest == 2);
  3386. }
  3387. /*===================================================================
  3388. IsClientConnected
  3389. This function will return the status of the last attempt to write to
  3390. the client. If Ok, it check the connection using new CIsapiReqInfo method
  3391. Parameters
  3392. none
  3393. Returns:
  3394. VARIANT_BOOL reflecting the status of the client connection
  3395. ===================================================================*/
  3396. STDMETHODIMP CResponse::IsClientConnected(VARIANT_BOOL* fIsClientConnected)
  3397. {
  3398. if (FAILED(CheckForTombstone()))
  3399. return E_FAIL;
  3400. if (m_pData->m_fWriteClientError)
  3401. {
  3402. *fIsClientConnected = VARIANT_FALSE;
  3403. }
  3404. else
  3405. {
  3406. // assume connected
  3407. BOOL fConnected = TRUE;
  3408. // test
  3409. if (m_pData->m_pIReq)
  3410. m_pData->m_pIReq->TestConnection(&fConnected);
  3411. *fIsClientConnected = fConnected ? VARIANT_TRUE : VARIANT_FALSE;
  3412. }
  3413. return(S_OK);
  3414. }
  3415. /*===================================================================
  3416. CResponse::get_CharSet
  3417. Functions called from DispInvoke to return the CarSet property.
  3418. Parameters:
  3419. pbstrCharSetRet BSTR FAR *, return value: pointer to the CharSet as a string
  3420. Returns:
  3421. HRESULT S_OK if ok
  3422. ===================================================================*/
  3423. STDMETHODIMP CResponse::get_CharSet
  3424. (
  3425. BSTR FAR * pbstrCharSetRet
  3426. )
  3427. {
  3428. if (FAILED(CheckForTombstone()))
  3429. return E_FAIL;
  3430. HRESULT hr = S_OK;
  3431. if (m_pData->m_pszCharSet)
  3432. hr = SysAllocStringFromSz(m_pData->m_pszCharSet, 0, pbstrCharSetRet);
  3433. else
  3434. *pbstrCharSetRet = NULL;
  3435. if (FAILED(hr))
  3436. {
  3437. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3438. hr = E_FAIL;
  3439. }
  3440. return(hr);
  3441. }
  3442. /*===================================================================
  3443. CResponse::put_CharSet
  3444. Functions called from DispInvoke to set the CharSet property.
  3445. This function takses a string, which identifies the name of the
  3446. character set of the current page, and appends the name of the
  3447. character set (for example, " ISO-LATIN-7") specified by the
  3448. charsetname to the content-type header in the response object
  3449. Notes:
  3450. * this function inserts any string in the header, whether or not
  3451. it represents a valis charcter set.
  3452. * if a single page contains multiple tags contianing response.charset,
  3453. each response.charset will replace the cahrset by the previous entry.
  3454. As a result, the charset will be set to the value specified by the
  3455. last instance of response.charset on a page.
  3456. * this command must also be invoked before the first response.write
  3457. operation unless buffering is turned on.
  3458. Parameters:
  3459. bstrContentType BSTR, value: the ContentType as a string
  3460. Returns:
  3461. HRESULT S_OK if ok
  3462. ===================================================================*/
  3463. STDMETHODIMP CResponse::put_CharSet
  3464. (
  3465. BSTR bstrCharSet
  3466. )
  3467. {
  3468. if (FAILED(CheckForTombstone()))
  3469. return E_FAIL;
  3470. HRESULT hr = S_OK;
  3471. CWCharToMBCS convStr;
  3472. if (FHeadersWritten())
  3473. {
  3474. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  3475. hr = E_FAIL;
  3476. goto lRet;
  3477. }
  3478. if (m_pData->m_pszCharSet) {
  3479. free(m_pData->m_pszCharSet);
  3480. m_pData->m_pszCharSet = NULL;
  3481. }
  3482. if (FAILED(hr = convStr.Init(bstrCharSet)));
  3483. else if ((m_pData->m_pszCharSet = convStr.GetString(TRUE)) == NULL) {
  3484. hr = E_OUTOFMEMORY;
  3485. }
  3486. lRet:
  3487. if (hr == E_OUTOFMEMORY) {
  3488. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3489. SetLastError((DWORD)E_OUTOFMEMORY);
  3490. }
  3491. return(hr);
  3492. }
  3493. /*===================================================================
  3494. CResponse::Pics
  3495. Functions called from DispInvoke to Add a pics header.
  3496. Parameters:
  3497. bstrHeaderValue Unicode BSTR, value: the value of pics header
  3498. Takes a string, which is the properly formatted PICS label, and adds
  3499. the value specified by the picslabel to the pics-label field of the response header.
  3500. Note:
  3501. * this function inserts any string in the header, whether or not it
  3502. represents a valid PICS lavel.
  3503. * current implimentation is a wraper on the addheader method.
  3504. Returns:
  3505. HRESULT S_OK if ok
  3506. ===================================================================*/
  3507. STDMETHODIMP CResponse::Pics
  3508. (
  3509. BSTR bstrHeaderValue
  3510. )
  3511. {
  3512. if (FAILED(CheckForTombstone()))
  3513. return E_FAIL;
  3514. if (FHeadersWritten())
  3515. {
  3516. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  3517. return E_FAIL;
  3518. }
  3519. if (FAILED(AppendHeader("pics-label", bstrHeaderValue)))
  3520. {
  3521. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3522. SetLastError((DWORD)E_OUTOFMEMORY);
  3523. return E_OUTOFMEMORY;
  3524. }
  3525. return S_OK;
  3526. }
  3527. /*===================================================================
  3528. CResponse::get_CacheControl
  3529. Functions called from DispInvoke to return the CacheControl property.
  3530. Parameters:
  3531. pbstrCacheControl BSTR FAR *, return value: pointer to the CacheControl as a string
  3532. Returns:
  3533. HRESULT S_OK if ok
  3534. ===================================================================*/
  3535. STDMETHODIMP CResponse::get_CacheControl
  3536. (
  3537. BSTR FAR * pbstrCacheControl
  3538. )
  3539. {
  3540. if (FAILED(CheckForTombstone()))
  3541. return E_FAIL;
  3542. HRESULT hr = S_OK;
  3543. if (m_pData->m_pszCacheControl)
  3544. hr = SysAllocStringFromSz(m_pData->m_pszCacheControl, 0, pbstrCacheControl);
  3545. else
  3546. hr = SysAllocStringFromSz( "private", 0, pbstrCacheControl);
  3547. if (FAILED(hr))
  3548. {
  3549. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3550. hr = E_FAIL;
  3551. }
  3552. return(hr);
  3553. }
  3554. /*===================================================================
  3555. CResponse::put_CacheControl
  3556. Functions called from DispInvoke to set the CacheControl property.
  3557. Parameters:
  3558. bstrCacheControl BSTR, value: the CacheControl as a string
  3559. Returns:
  3560. HRESULT S_OK if ok
  3561. ===================================================================*/
  3562. STDMETHODIMP CResponse::put_CacheControl
  3563. (
  3564. BSTR bstrCacheControl
  3565. )
  3566. {
  3567. if (FAILED(CheckForTombstone()))
  3568. return E_FAIL;
  3569. HRESULT hr = S_OK;
  3570. CWCharToMBCS convStr;
  3571. if (FHeadersWritten())
  3572. {
  3573. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_RESPONSE_HEADERS_WRITTEN);
  3574. hr = E_FAIL;
  3575. goto lRet;
  3576. }
  3577. if (m_pData->m_pszCacheControl) {
  3578. free(m_pData->m_pszCacheControl);
  3579. m_pData->m_pszCacheControl = NULL;
  3580. }
  3581. if (FAILED(hr = convStr.Init(bstrCacheControl)));
  3582. else if ((m_pData->m_pszCacheControl = convStr.GetString(TRUE)) == NULL) {
  3583. hr = E_OUTOFMEMORY;
  3584. }
  3585. lRet:
  3586. if (hr == E_OUTOFMEMORY) {
  3587. ExceptionId(IID_IResponse, IDE_RESPONSE, IDE_OOM);
  3588. SetLastError((DWORD)E_OUTOFMEMORY);
  3589. }
  3590. return(hr);
  3591. }
  3592. /*===================================================================
  3593. CResponse::get_CodePage
  3594. Returns the current code page value for the response
  3595. Parameters:
  3596. long *plVar [out] code page value
  3597. Returns:
  3598. HRESULT S_OK on success
  3599. ===================================================================*/
  3600. STDMETHODIMP CResponse::get_CodePage
  3601. (
  3602. long *plVar
  3603. )
  3604. {
  3605. Assert(m_pData);
  3606. Assert(m_pData->m_pHitObj);
  3607. *plVar = m_pData->m_pHitObj->GetCodePage();
  3608. // If code page is 0, look up default ANSI code page
  3609. if (*plVar == 0) {
  3610. *plVar = (long) GetACP();
  3611. }
  3612. return S_OK;
  3613. }
  3614. /*===================================================================
  3615. CResponse::put_CodePage
  3616. Sets the current code page value for the response
  3617. Parameters:
  3618. long lVar code page to assign to this response
  3619. Returns:
  3620. HRESULT S_OK on success
  3621. ===================================================================*/
  3622. STDMETHODIMP CResponse::put_CodePage
  3623. (
  3624. long lVar
  3625. )
  3626. {
  3627. Assert(m_pData);
  3628. Assert(m_pData->m_pHitObj);
  3629. // set code page member variable
  3630. HRESULT hr = m_pData->m_pHitObj->SetCodePage(lVar);
  3631. if (FAILED(hr)) {
  3632. ExceptionId
  3633. (
  3634. IID_IResponse,
  3635. IDE_RESPONSE,
  3636. IDE_SESSION_INVALID_CODEPAGE
  3637. );
  3638. return E_FAIL;
  3639. }
  3640. return S_OK;
  3641. }
  3642. /*===================================================================
  3643. CResponse::get_LCID
  3644. Returns the current LCID value for the response
  3645. Parameters:
  3646. long *plVar [out] LCID value
  3647. Returns:
  3648. HRESULT S_OK on success
  3649. ===================================================================*/
  3650. STDMETHODIMP CResponse::get_LCID
  3651. (
  3652. long *plVar
  3653. )
  3654. {
  3655. Assert(m_pData);
  3656. Assert(m_pData->m_pHitObj);
  3657. *plVar = m_pData->m_pHitObj->GetLCID();
  3658. // If code page is 0, look up default ANSI code page
  3659. if (*plVar == LOCALE_SYSTEM_DEFAULT) {
  3660. *plVar = (long) GetSystemDefaultLCID();
  3661. }
  3662. return S_OK;
  3663. }
  3664. /*===================================================================
  3665. CResponse::put_LCID
  3666. Sets the current LCID value for the response
  3667. Parameters:
  3668. long lVar LCID to assign to this response
  3669. Returns:
  3670. HRESULT S_OK on success
  3671. ===================================================================*/
  3672. STDMETHODIMP CResponse::put_LCID
  3673. (
  3674. long lVar
  3675. )
  3676. {
  3677. Assert(m_pData);
  3678. Assert(m_pData->m_pHitObj);
  3679. // set code page member variable
  3680. HRESULT hr = m_pData->m_pHitObj->SetLCID(lVar);
  3681. if (FAILED(hr)) {
  3682. ExceptionId
  3683. (
  3684. IID_IResponse,
  3685. IDE_RESPONSE,
  3686. IDE_TEMPLATE_BAD_LCID
  3687. );
  3688. return E_FAIL;
  3689. }
  3690. return S_OK;
  3691. }
  3692. /*===================================================================
  3693. IStream implementation for ADO/XML
  3694. ===================================================================*/
  3695. STDMETHODIMP CResponse::Read(
  3696. void *pv,
  3697. ULONG cb,
  3698. ULONG *pcbRead)
  3699. {
  3700. return E_NOTIMPL;
  3701. }
  3702. STDMETHODIMP CResponse::Write(
  3703. const void *pv,
  3704. ULONG cb,
  3705. ULONG *pcbWritten)
  3706. {
  3707. if (pcbWritten != NULL)
  3708. *pcbWritten = cb;
  3709. return WriteSz((CHAR*) pv, cb);
  3710. }
  3711. STDMETHODIMP CResponse::Seek(
  3712. LARGE_INTEGER dlibMove,
  3713. DWORD dwOrigin,
  3714. ULARGE_INTEGER *plibNewPosition)
  3715. {
  3716. return E_NOTIMPL;
  3717. }
  3718. STDMETHODIMP CResponse::SetSize(
  3719. ULARGE_INTEGER libNewSize)
  3720. {
  3721. return E_NOTIMPL;
  3722. }
  3723. STDMETHODIMP CResponse::CopyTo(
  3724. IStream *pstm,
  3725. ULARGE_INTEGER cb,
  3726. ULARGE_INTEGER *pcbRead,
  3727. ULARGE_INTEGER *pcbWritten)
  3728. {
  3729. return E_NOTIMPL;
  3730. }
  3731. STDMETHODIMP CResponse::Commit(
  3732. DWORD grfCommitFlags)
  3733. {
  3734. return E_NOTIMPL;
  3735. }
  3736. STDMETHODIMP CResponse::Revert()
  3737. {
  3738. return E_NOTIMPL;
  3739. }
  3740. STDMETHODIMP CResponse::LockRegion(
  3741. ULARGE_INTEGER libOffset,
  3742. ULARGE_INTEGER cb,
  3743. DWORD dwLockType)
  3744. {
  3745. return E_NOTIMPL;
  3746. }
  3747. STDMETHODIMP CResponse::UnlockRegion(
  3748. ULARGE_INTEGER libOffset,
  3749. ULARGE_INTEGER cb,
  3750. DWORD dwLockType)
  3751. {
  3752. return E_NOTIMPL;
  3753. }
  3754. STDMETHODIMP CResponse::Stat(
  3755. STATSTG *pstatstg,
  3756. DWORD grfStatFlag)
  3757. {
  3758. return E_NOTIMPL;
  3759. }
  3760. STDMETHODIMP CResponse::Clone(
  3761. IStream **ppstm)
  3762. {
  3763. return E_NOTIMPL;
  3764. }