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.

9376 lines
292 KiB

  1. // This is a part of the Active Template Library.
  2. // Copyright (C) 1996-2001 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Active Template Library Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Active Template Library product.
  10. #ifndef __ATLSTENCIL_H__
  11. #include <atlstencil.h>
  12. #endif
  13. #ifndef __ATLISAPI_H__
  14. #define __ATLISAPI_H__
  15. #pragma once
  16. #include <atlbase.h>
  17. #include <time.h> // needed for cookie support
  18. #include <httpext.h> // needed for ECB and IIS support
  19. #include <atlspriv.h>
  20. #include <atlserr.h>
  21. #include <atlbase.inl>
  22. #include <atlfile.h>
  23. #include <atlstr.h>
  24. #include <atldbcli.h>
  25. #include <atlutil.h>
  26. #include <atlcache.h>
  27. #include <atlsrvres.h>
  28. #include <atlsiface.h>
  29. #include <objbase.h>
  30. #include <atlsecurity.h>
  31. #ifndef ATL_NO_SOAP
  32. #include <msxml2.h>
  33. #endif
  34. #ifndef ATL_NO_ACLAPI
  35. #include <aclapi.h>
  36. #endif
  37. #ifndef ATL_NO_MMSYS
  38. #pragma warning(push)
  39. #pragma warning(disable:4201)
  40. #include <mmsystem.h>
  41. #pragma warning(pop)
  42. #ifndef _ATL_NO_DEFAULT_LIBS
  43. #pragma comment(lib, "winmm.lib")
  44. #ifndef ATL_NO_SOAP
  45. #pragma comment(lib, "msxml2.lib")
  46. #endif
  47. #endif // !_ATL_NO_DEFAULT_LIBS
  48. #endif
  49. #pragma warning(push)
  50. #pragma warning(disable: 4291) // allow placement new
  51. #pragma warning(disable: 4127)
  52. #pragma warning(disable: 4511) // copy constructor could not be generated
  53. #pragma warning(disable: 4512) // assignment operator could not be generated
  54. #pragma warning(disable: 4702) // unreachable code
  55. /* REVIEW: Remove these at some point in the future */
  56. #include <initguid.h>
  57. #include <dbgautoattach.h>
  58. #ifndef SESSION_COOKIE_NAME
  59. #define SESSION_COOKIE_NAME "SESSIONID"
  60. #endif
  61. #ifndef ATLS_MAX_HTTP_DATE
  62. #define ATLS_MAX_HTTP_DATE 64
  63. #endif
  64. // This function is used in CValidateObject to determine if an empty
  65. // request parameter really should be empty. You can
  66. // specialize this function in your own code such as
  67. // the following specialization for type long:
  68. // template <>
  69. // inline bool IsNullByType<long>(long type) throw()
  70. // {
  71. // return type == 0;
  72. // }
  73. // You should provide your own specialization for this
  74. // function if the comparison of type==0 is not adequate
  75. // to discover whether or not your type is 0.
  76. template <class TComp>
  77. inline bool IsNullByType(TComp type) throw()
  78. {
  79. return type == 0;
  80. }
  81. namespace ATL {
  82. // Default file extension for server response files
  83. #ifndef ATL_DEFAULT_STENCIL_EXTENSION
  84. #define ATL_DEFAULT_STENCIL_EXTENSION ".srf"
  85. #endif
  86. __declspec(selectany) LPCSTR c_AtlSRFExtension = ATL_DEFAULT_STENCIL_EXTENSION;
  87. __declspec(selectany) LPCTSTR c_tAtlSRFExtension = _T(ATL_DEFAULT_STENCIL_EXTENSION);
  88. #define ATLS_EXTENSION_LEN (sizeof(ATL_DEFAULT_STENCIL_EXTENSION)-2)
  89. // maximum handler name length
  90. #ifndef ATL_MAX_HANDLER_NAME_LEN
  91. #define ATL_MAX_HANDLER_NAME_LEN 64
  92. #endif
  93. // maximum timeout for async guard mutex
  94. #ifndef ATLS_ASYNC_MUTEX_TIMEOUT
  95. #define ATLS_ASYNC_MUTEX_TIMEOUT 10000
  96. #endif
  97. #if defined(_M_IA64)
  98. #define ATLS_FUNCID_INITIALIZEHANDLERS "InitializeAtlHandlers"
  99. #define ATLS_FUNCID_GETATLHANDLERBYNAME "GetAtlHandlerByName"
  100. #define ATLS_FUNCID_UNINITIALIZEHANDLERS "UninitializeAtlHandlers"
  101. #else
  102. #define ATLS_FUNCID_INITIALIZEHANDLERS "_InitializeAtlHandlers@8"
  103. #define ATLS_FUNCID_GETATLHANDLERBYNAME "_GetAtlHandlerByName@12"
  104. #define ATLS_FUNCID_UNINITIALIZEHANDLERS "_UninitializeAtlHandlers@0"
  105. #endif
  106. #define ATL_MAX_COOKIE_LEN 2048
  107. #define ATL_MAX_COOKIE_ELEM 1024
  108. // Defines a small value used for comparing the equality of floating point numbers.
  109. #ifndef ATL_EPSILON
  110. #define ATL_EPSILON .0001
  111. #endif
  112. #ifndef ATL_DEFAULT_PRECISION
  113. #define ATL_DEFAULT_PRECISION 6
  114. #endif
  115. // Returns the number of instances of a particular character between the specified start and end points inclusive.
  116. inline int CountOf(CHAR c, LPCSTR pBegin, LPCSTR pEnd) throw()
  117. {
  118. int nCount = 0;
  119. while (pBegin < pEnd && *pBegin)
  120. {
  121. if (*pBegin == c)
  122. nCount++;
  123. pBegin++;
  124. }
  125. return nCount;
  126. }
  127. // Call this function to copy a substring to a CString reference and ensure nul-termination.
  128. ATL_NOINLINE inline bool CopyToCString(CStringA& string, LPCSTR pStart, LPCSTR pEnd) throw()
  129. {
  130. _ATLTRY
  131. {
  132. if (pStart > pEnd)
  133. return true; //nothing to do
  134. int nLen = ((int)(pEnd-pStart))+1;
  135. LPSTR pBuff = string.GetBuffer(nLen+1);
  136. if (pBuff)
  137. {
  138. memcpy(pBuff, pStart, nLen);
  139. pBuff[nLen]=0;
  140. string.ReleaseBuffer(nLen);
  141. }
  142. return true;
  143. }
  144. _ATLCATCHALL()
  145. {
  146. return false;
  147. }
  148. }
  149. // Call this function to URL-encode a buffer and have the result appended to a CString passed by reference.
  150. //
  151. // A space in the input string is encoded as a plus sign (+).
  152. // Other unsafe characters (as determined by AtlIsUnsafeUrlChar) are encoded as escaped octets.
  153. // An escaped octet is a percent sign (%) followed by two digits representing the hexadecimal code of the character.
  154. //
  155. // string A CStringA reference to which will be appended the encoded version of szBuf.
  156. //
  157. // szBuf The string to be URL-encoded.
  158. ATL_NOINLINE inline bool EscapeToCString(CStringA& string, LPCSTR szBuf) throw()
  159. {
  160. ATLASSERT( szBuf != NULL );
  161. _ATLTRY
  162. {
  163. CHAR szEscaped[512];
  164. LPSTR pszStr = szEscaped;
  165. DWORD dwLen = 0;
  166. while (*szBuf)
  167. {
  168. if (dwLen+4 >= 512)
  169. {
  170. *pszStr = '\0';
  171. string+= szEscaped;
  172. pszStr = szEscaped;
  173. dwLen = 0;
  174. }
  175. if (AtlIsUnsafeUrlChar(*szBuf))
  176. {
  177. if (*szBuf == ' ')
  178. {
  179. dwLen++;
  180. *pszStr++ = '+';
  181. }
  182. else
  183. {
  184. DWORD dwEsc = sprintf(pszStr, "%%%.2X", (unsigned char)*szBuf);
  185. pszStr+= dwEsc;
  186. dwLen+= dwEsc;
  187. }
  188. }
  189. else
  190. {
  191. *pszStr++ = *szBuf;
  192. dwLen++;
  193. }
  194. szBuf++;
  195. }
  196. *pszStr = '\0';
  197. string+= szEscaped;
  198. }
  199. _ATLCATCHALL()
  200. {
  201. return false;
  202. }
  203. return true;
  204. }
  205. // UNICODE overload for EscapeToCString
  206. // follow specifications detailed in RFC document on
  207. // Internationalized Uniform Resource Identifiers (IURI)
  208. inline bool EscapeToCString(CStringA& string, LPCWSTR wszBuf) throw()
  209. {
  210. _ATLTRY
  211. {
  212. // convert string to UTF8
  213. CFixedStringT<CStringA, 2048> strConvert;
  214. // get the required length for conversion
  215. int nLen = WideCharToMultiByte(CP_UTF8, 0, wszBuf, -1, NULL, 0, NULL, NULL);
  216. if (!nLen)
  217. return false; // error -- most likely CP_UTF8 not supported on the OS (e.g. Win98)
  218. // allocate MBCS conversion string
  219. LPSTR sz = strConvert.GetBuffer(nLen);
  220. if (!sz)
  221. return false;
  222. // do the UNICODE to UTF8 conversion
  223. nLen = WideCharToMultiByte(CP_UTF8, 0, wszBuf, -1, sz, nLen, NULL, NULL);
  224. if (!nLen)
  225. return false;
  226. // null-terminate
  227. sz[nLen] = '\0';
  228. // delegate to ANSI version of EscapeToCString
  229. if (!EscapeToCString(string, sz))
  230. return false;
  231. strConvert.ReleaseBuffer();
  232. }
  233. _ATLCATCHALL()
  234. {
  235. return false;
  236. }
  237. return true;
  238. }
  239. struct CDefaultErrorProvider
  240. {
  241. struct HTTP_ERROR_TEXT
  242. {
  243. UINT uHttpError; // the Http Error value
  244. UINT uHttpSubError; // Allows for customization of error text based on srf specific errors.
  245. LPCSTR szHeader; // the string that should appear in the http response header
  246. UINT uResId; // the resource id of the string to send back as the body
  247. };
  248. // GetErrorText retrieves the http response header string
  249. // and a resource id of the response body for a given
  250. // http error code
  251. // uError: Http error code to retrieve information for
  252. // ppszHeader: pointer to LPCSTR that receives the response header string
  253. // ppszHeader is optional
  254. // puResId: pointer to UINT that receives the response body resource id
  255. // puResId is optional
  256. static BOOL GetErrorText(UINT uError, UINT uSubErr, LPCSTR *ppszHeader, UINT *puResId) throw()
  257. {
  258. static const HTTP_ERROR_TEXT s_Errors[] =
  259. {
  260. { 200, SUBERR_NONE, "OK", 0 },
  261. { 201, SUBERR_NONE, "Created", 0 },
  262. { 202, SUBERR_NONE, "Accepted", 0 },
  263. { 203, SUBERR_NONE, "Non-Authoritative Information", 0 },
  264. { 204, SUBERR_NONE, "No Content", 0 },
  265. { 204, DBG_SUBERR_ALREADY_DEBUGGING, "Already being debugged by another user", 0},
  266. { 204, DBG_SUBERR_NOT_DEBUGGING, "Not currently debugging a process", 0},
  267. { 204, DBG_SUBERR_INVALID_SESSION, "Requested DebugSessionID does not match current DebugSessionID", 0},
  268. { 204, DBG_SUBERR_BAD_ID, "DebugSessionID corrupted or not provided", 0 },
  269. { 204, DBG_SUBERR_COCREATE, "Could not CoCreate the debugger", 0 },
  270. { 204, DBG_SUBERR_ATTACH, "Could not attach to process", 0 },
  271. { 205, SUBERR_NONE, "Reset Content", 0 },
  272. { 206, SUBERR_NONE, "Partial Content", 0 },
  273. { 300, SUBERR_NONE, "Multiple Choices", 0 },
  274. { 301, SUBERR_NONE, "Moved Permanently", 0 },
  275. { 302, SUBERR_NONE, "Found", 0 },
  276. { 303, SUBERR_NONE, "See Other", 0 },
  277. { 304, SUBERR_NONE, "Not Modified", 0 },
  278. { 305, SUBERR_NONE, "Use Proxy", 0 },
  279. { 306, SUBERR_NONE, "(Unused)", 0 },
  280. { 307, SUBERR_NONE, "Temporary Redirect", 0 },
  281. { 400, SUBERR_NONE, "Bad Request", IDS_ATLSRV_BAD_REQUEST },
  282. { 401, SUBERR_NONE, "Unauthorized", IDS_ATLSRV_AUTH_REQUIRED },
  283. { 402, SUBERR_NONE, "Payment Required", 0 },
  284. { 403, SUBERR_NONE, "Forbidden", IDS_ATLSRV_FORBIDDEN },
  285. { 404, SUBERR_NONE, "Not Found", IDS_ATLSRV_NOT_FOUND },
  286. { 405, SUBERR_NONE, "Method Not Allowed", 0 },
  287. { 406, SUBERR_NONE, "Not Acceptable", 0 },
  288. { 407, SUBERR_NONE, "Proxy Authentication Required", 0 },
  289. { 408, SUBERR_NONE, "Request Timeout", 0 },
  290. { 409, SUBERR_NONE, "Conflict", 0 },
  291. { 410, SUBERR_NONE, "Gone", 0 },
  292. { 411, SUBERR_NONE, "Length Required", 0 },
  293. { 412, SUBERR_NONE, "Precondition Failed", 0 },
  294. { 413, SUBERR_NONE, "Request Entity Too Long", 0 },
  295. { 414, SUBERR_NONE, "Request-URI Too Long", 0 },
  296. { 415, SUBERR_NONE, "Unsupported Media Type", 0 },
  297. { 416, SUBERR_NONE, "Requested Range Not Satisfiable", 0 },
  298. { 417, SUBERR_NONE, "Expectation Failed", 0 },
  299. { 500, SUBERR_NONE, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR },
  300. { 500, ISE_SUBERR_BADSRF, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_BADSRF },
  301. { 500, ISE_SUBERR_HNDLFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HNDLFAIL },
  302. { 500, ISE_SUBERR_SYSOBJFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL},
  303. { 500, ISE_SUBERR_READFILEFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_READFILEFAIL},
  304. { 500, ISE_SUBERR_LOADFILEFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL},
  305. { 500, ISE_SUBERR_LOADLIB, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LOADLIB},
  306. { 500, ISE_SUBERR_HANDLERIF, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HANDLERIF},
  307. { 500, ISE_SUBERR_OUTOFMEM, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_OUTOFMEM},
  308. { 500, ISE_SUBERR_UNEXPECTED, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_UNEXPECTED},
  309. { 500, ISE_SUBERR_STENCIL_PARSE_FAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL},
  310. { 500, ISE_SUBERR_STENCIL_LOAD_FAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL},
  311. { 500, ISE_SUBERR_HANDLER_NOT_FOUND, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND},
  312. { 500, ISE_SUBERR_BAD_HANDLER_TAG, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG},
  313. { 500, ISE_SUBERR_LONGMETHODNAME, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME},
  314. { 500, ISE_SUBERR_LONGHANDLERNAME, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME},
  315. { 500, ISE_SUBERR_NO_HANDLER_TAG, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG},
  316. { 500, ISE_SUBERR_IMPERSONATIONFAILED, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED},
  317. { 500, ISE_SUBERR_ISAPISTARTUPFAILED, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED},
  318. { 501, SUBERR_NONE, "Not Implemented", IDS_ATLSRV_NOT_IMPLEMENTED },
  319. { 502, SUBERR_NONE, "Bad Gateway", IDS_ATLSRV_BAD_GATEWAY },
  320. { 503, SUBERR_NONE, "Service Unavailable", IDS_ATLSRV_SERVICE_NOT_AVAILABLE },
  321. { 504, SUBERR_NONE, "Gateway Timeout", 0 },
  322. { 505, SUBERR_NONE, "HTTP Version Not Supported", 0 },
  323. };
  324. // look for the error
  325. for (int i=0; i<sizeof(s_Errors)/sizeof(s_Errors[0]); i++)
  326. {
  327. if ((s_Errors[i].uHttpError == uError) && (s_Errors[i].uHttpSubError == uSubErr))
  328. {
  329. if (ppszHeader)
  330. *ppszHeader = s_Errors[i].szHeader;
  331. if (puResId)
  332. *puResId = s_Errors[i].uResId;
  333. return TRUE;
  334. }
  335. }
  336. // not found
  337. return FALSE;
  338. }
  339. }; // CDefaultErrorProvider
  340. template<class HttpUserErrorTextProvider>
  341. CStringA GetStatusHeader(DWORD dwStatus, DWORD dwSubStatus, HttpUserErrorTextProvider* pErrorProvider, UINT *puResId = NULL) throw(...)
  342. {
  343. pErrorProvider;
  344. LPCSTR szHeadErr = NULL;
  345. // First, we check for the error text in the extension's user error text provider
  346. BOOL bRet = pErrorProvider->GetErrorText(dwStatus, dwSubStatus, &szHeadErr, puResId);
  347. if (!bRet)
  348. szHeadErr = "";
  349. CStringA strStatus;
  350. strStatus.Format("%d %s", dwStatus, szHeadErr);
  351. return strStatus;
  352. }
  353. template<class HttpUserErrorTextProvider>
  354. void RenderError(IHttpServerContext *pServerContext, DWORD dwStatus, DWORD dwSubStatus, HttpUserErrorTextProvider* pErrorProvider) throw()
  355. {
  356. _ATLTRY
  357. {
  358. UINT uResId = 0;
  359. CStringA strStatus = GetStatusHeader(dwStatus, dwSubStatus, pErrorProvider, &uResId);
  360. pServerContext->SendResponseHeader(NULL, strStatus, FALSE);
  361. CStringA strBody = strStatus;
  362. if (uResId)
  363. {
  364. // load the body string from a resource
  365. CStringA strTemp;
  366. if (strTemp.LoadString(uResId))
  367. {
  368. strBody = strTemp;
  369. }
  370. }
  371. DWORD dwBodyLen = strBody.GetLength();
  372. pServerContext->WriteClient((void *) (LPCSTR) strBody, &dwBodyLen);
  373. }
  374. _ATLCATCHALL()
  375. {
  376. // last resort message when low on memory
  377. LPCSTR szError;
  378. BOOL bRes;
  379. bRes = CDefaultErrorProvider::GetErrorText(dwStatus, dwSubStatus, &szError, 0);
  380. if (!bRes)
  381. bRes = CDefaultErrorProvider::GetErrorText(dwStatus, SUBERR_NONE, &szError, 0);
  382. if (!bRes)
  383. bRes = CDefaultErrorProvider::GetErrorText(500, SUBERR_NONE, &szError, 0);
  384. DWORD dwBodyLen = (DWORD) strlen(szError);
  385. pServerContext->WriteClient((void *) szError, &dwBodyLen);
  386. }
  387. }
  388. // Call this function to retrieve the full canonical physical path
  389. // of a file relative to the current script.
  390. //
  391. // Returns TRUE on success, FALSE on error.
  392. //
  393. // szFile A file path relative to the current script directory for which
  394. // you are trying to retrieve the full path.
  395. //
  396. // szFullFileName A caller-allocated buffer of at least MAX_PATH characters in length.
  397. // On success, contains the the full canonical path of szFile.
  398. //
  399. // pServerContext The context for the current request. The context is used to obtain the
  400. // current script directory.
  401. inline BOOL GetScriptFullFileName(
  402. LPCSTR szFile,
  403. LPSTR szFullFileName,
  404. IHttpServerContext* pServerContext) throw()
  405. {
  406. ATLASSERT(szFile != NULL);
  407. ATLASSERT(szFullFileName != NULL);
  408. char szTmpScriptPath[MAX_PATH+1];
  409. LPCSTR szTmp = pServerContext->GetScriptPathTranslated();
  410. if (!szTmp)
  411. {
  412. return FALSE;
  413. }
  414. strcpy(szTmpScriptPath, szTmp);
  415. CHAR *szScriptPath = szTmpScriptPath;
  416. LPSTR szBackslash;
  417. if (*szFile != '\\')
  418. {
  419. szBackslash = strrchr(szScriptPath, '\\');
  420. if (szBackslash)
  421. szBackslash++;
  422. }
  423. else
  424. {
  425. // handle case where szFile is of the form \directory\etc\etc
  426. szBackslash = strchr(szScriptPath, '\\');
  427. }
  428. if (szBackslash)
  429. *szBackslash = '\0';
  430. int nScriptPathLen = (int)(szBackslash ? strlen(szScriptPath) : 0);
  431. int nFileLen = (int) strlen(szFile);
  432. if (nScriptPathLen + nFileLen > MAX_PATH)
  433. {
  434. return FALSE;
  435. }
  436. CHAR szTemp[MAX_PATH + 1];
  437. if (nScriptPathLen)
  438. memcpy(szTemp, szScriptPath, nScriptPathLen);
  439. memcpy(szTemp + nScriptPathLen, szFile, nFileLen);
  440. *(szTemp + nScriptPathLen + nFileLen) = 0;
  441. PathCanonicalizeA(szFullFileName, szTemp);
  442. return TRUE;
  443. }
  444. interface IStencilCache;
  445. enum ATLSRV_STATE
  446. {
  447. ATLSRV_STATE_BEGIN, // The request has just arrived, and the type has not been determined
  448. ATLSRV_STATE_CONTINUE, // The request is a continuation of an async request
  449. ATLSRV_STATE_DONE, // The request is a continuation of an async request, but the server is done with it
  450. ATLSRV_STATE_CACHE_DONE // The request is the callback of a cached page
  451. };
  452. enum ATLSRV_REQUESTTYPE
  453. {
  454. ATLSRV_REQUEST_UNKNOWN=-1, // The request type isn't known yet
  455. ATLSRV_REQUEST_STENCIL, // The request is for a .srf file
  456. ATLSRV_REQUEST_DLL // The request is for a .dll file
  457. };
  458. // Flags the InitRequest can return in dwStatus
  459. #define ATLSRV_INIT_USECACHE 1
  460. #define ATLSRV_INIT_USEASYNC 2
  461. #define ATLSRV_INIT_USEASYNC_EX 4 // required for use of NOFLUSH status
  462. typedef HTTP_CODE (IRequestHandler::*PFnHandleRequest)(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider);
  463. typedef void (*PFnAsyncComplete)(AtlServerRequest *pRequestInfo, DWORD cbIO, DWORD dwError);
  464. struct AtlServerRequest
  465. {
  466. DWORD cbSize; // For future compatibility
  467. IHttpServerContext *pServerContext; // Necessary because it wraps the ECB
  468. ATLSRV_REQUESTTYPE dwRequestType; // See the ATLSRV variables above
  469. // Indicates whether it was called through an .srf file or through a .dll file
  470. ATLSRV_STATE dwRequestState; // See the ATLSRV variables above
  471. // Indicates what state of completion the request is in
  472. IRequestHandler *pHandler; // Necessary because the callback (for async calls) must know where to
  473. // route the request
  474. HINSTANCE hInstDll; // Necessary in order to release the dll properly (for async calls)
  475. IIsapiExtension *pExtension; // Necessary to requeue the request (for async calls)
  476. IDllCache* pDllCache; // Necessary to release the dll in async callback
  477. HANDLE hFile;
  478. HCACHEITEM hEntry;
  479. IFileCache* pFileCache;
  480. HANDLE m_hMutex; // necessary to syncronize calls to HandleRequest
  481. // if HandleRequest could potientially make an
  482. // async call before returning. only used
  483. // if indicated with ATLSRV_INIT_USEASYNC_EX
  484. DWORD dwStartTicks; // Tick count when the request was received
  485. EXTENSION_CONTROL_BLOCK *pECB;
  486. PFnHandleRequest pfnHandleRequest;
  487. PFnAsyncComplete pfnAsyncComplete;
  488. LPCSTR pszBuffer; // buffer to be flushed asyncronously
  489. DWORD dwBufferLen; // length of data in pszBuffer
  490. void* pUserData; // value that can be used to pass user data between parent and child handlers
  491. };
  492. inline void _ReleaseAtlServerRequest(AtlServerRequest* pRequest) throw()
  493. {
  494. if (pRequest->pHandler)
  495. pRequest->pHandler->Release();
  496. if (pRequest->pServerContext)
  497. pRequest->pServerContext->Release();
  498. if (pRequest->pDllCache && pRequest->hInstDll)
  499. pRequest->pDllCache->ReleaseModule(pRequest->hInstDll);
  500. if (pRequest->m_hMutex)
  501. CloseHandle(pRequest->m_hMutex);
  502. }
  503. typedef BOOL (__stdcall *GETATLHANDLERBYNAME)(LPCSTR szHandlerName, IIsapiExtension *pExtension, IUnknown **ppHandler);
  504. typedef BOOL (__stdcall *INITIALIZEATLHANDLERS)(IHttpServerContext*, IIsapiExtension*);
  505. typedef void (__stdcall *UNINITIALIZEATLHANDLERS)();
  506. // initial size of thread worker heap (per thread)
  507. // The heap is growable. The default initial is 16KB
  508. #ifndef ATLS_WORKER_HEAP_SIZE
  509. #define ATLS_WORKER_HEAP_SIZE 16384
  510. #endif
  511. class CIsapiWorker
  512. {
  513. public:
  514. typedef AtlServerRequest* RequestType;
  515. HANDLE m_hHeap;
  516. #ifndef ATL_NO_SOAP
  517. CComPtr<ISAXXMLReader> m_spReader;
  518. #endif
  519. CIsapiWorker() throw()
  520. {
  521. m_hHeap = NULL;
  522. }
  523. ~CIsapiWorker() throw()
  524. {
  525. ATLASSERT(m_hHeap == NULL);
  526. }
  527. virtual BOOL Initialize(void *pvParam) throw(...)
  528. {
  529. IIsapiExtension* pExtension = (IIsapiExtension*) pvParam;
  530. ATLASSERT(pExtension);
  531. if (!(pExtension->OnThreadAttach()))
  532. return FALSE;
  533. m_hHeap = HeapCreate(HEAP_NO_SERIALIZE, ATLS_WORKER_HEAP_SIZE, 0);
  534. if (!m_hHeap)
  535. return FALSE;
  536. #ifndef ATL_NO_SOAP
  537. if (FAILED(m_spReader.CoCreateInstance(__uuidof(SAXXMLReader30))))
  538. {
  539. return FALSE;
  540. }
  541. #endif
  542. return pExtension->SetThreadWorker(this);
  543. }
  544. virtual void Terminate(void* pvParam) throw()
  545. {
  546. if (m_hHeap)
  547. {
  548. if (HeapDestroy(m_hHeap))
  549. m_hHeap = NULL;
  550. else
  551. {
  552. ATLASSERT(FALSE);
  553. }
  554. }
  555. #ifndef ATL_NO_SOAP
  556. m_spReader.Release();
  557. #endif
  558. (static_cast<IIsapiExtension*>(pvParam))->OnThreadTerminate();
  559. }
  560. void Execute(AtlServerRequest *pRequestInfo, void *pvParam, OVERLAPPED *pOverlapped) throw()
  561. {
  562. ATLASSERT(pRequestInfo != NULL);
  563. ATLASSERT(pvParam != NULL);
  564. pOverlapped; // unused
  565. ATLASSERT(m_hHeap != NULL);
  566. // any exceptions thrown at this point should have been caught in an
  567. // override of DispatchStencilCall. They will not be thrown out of this
  568. // function.
  569. _ATLTRY
  570. {
  571. (static_cast<IIsapiExtension*>(pvParam))->DispatchStencilCall(pRequestInfo);
  572. }
  573. _ATLCATCHALL()
  574. {
  575. ATLTRACE(_T("Warning. An uncaught exception was thrown from DispatchStencilCall\n"));
  576. ATLASSERT(FALSE);
  577. }
  578. }
  579. virtual BOOL GetWorkerData(DWORD /*dwParam*/, void ** /*ppvData*/) throw()
  580. {
  581. return FALSE;
  582. }
  583. };
  584. inline void _AtlGetScriptPathTranslated(
  585. LPCSTR szPathTranslated,
  586. CFixedStringT<CStringA, MAX_PATH>& strScriptPathTranslated) throw()
  587. {
  588. LPCSTR szEnd = szPathTranslated;
  589. while (TRUE)
  590. {
  591. while (*szEnd != '.' && *szEnd != '\0')
  592. szEnd++;
  593. if (*szEnd == '\0')
  594. break;
  595. szEnd++;
  596. size_t nLen(0);
  597. if (!_strnicmp(szEnd, "dll", sizeof("dll")-sizeof('\0')))
  598. nLen = 3;
  599. else if (!_strnicmp(szEnd, c_AtlSRFExtension+1, ATLS_EXTENSION_LEN))
  600. nLen = ATLS_EXTENSION_LEN;
  601. if (nLen)
  602. {
  603. szEnd += nLen;
  604. if (!*szEnd || *szEnd == '/' || *szEnd == '\\' || *szEnd == '?' || *szEnd == '#')
  605. break;
  606. }
  607. }
  608. DWORD dwResult = (DWORD)(szEnd - szPathTranslated);
  609. char *szScriptPathTranslated = strScriptPathTranslated.GetBuffer(dwResult);
  610. if (szScriptPathTranslated)
  611. {
  612. memcpy(szScriptPathTranslated, szPathTranslated, dwResult);
  613. szScriptPathTranslated[dwResult] = '\0';
  614. strScriptPathTranslated.ReleaseBuffer(dwResult);
  615. }
  616. }
  617. struct CStencilState
  618. {
  619. CStencilState() throw()
  620. {
  621. dwIndex = 0;
  622. locale = CP_ACP;
  623. pIncludeInfo = NULL;
  624. pParentInfo = NULL;
  625. }
  626. DWORD dwIndex;
  627. LCID locale;
  628. AtlServerRequest* pIncludeInfo;
  629. AtlServerRequest* pParentInfo;
  630. };
  631. class CWrappedServerContext:
  632. public IHttpServerContext
  633. {
  634. public:
  635. CComPtr<IHttpServerContext> m_spParent;
  636. CWrappedServerContext() throw()
  637. {
  638. }
  639. CWrappedServerContext(IHttpServerContext *pParent) throw()
  640. {
  641. m_spParent = pParent;
  642. }
  643. LPCSTR GetRequestMethod() throw()
  644. {
  645. ATLASSERT(m_spParent);
  646. return m_spParent->GetRequestMethod();
  647. }
  648. LPCSTR GetQueryString() throw()
  649. {
  650. ATLASSERT(m_spParent);
  651. return m_spParent->GetQueryString();
  652. }
  653. LPCSTR GetPathInfo() throw()
  654. {
  655. ATLASSERT(m_spParent);
  656. return m_spParent->GetPathInfo();
  657. }
  658. LPCSTR GetScriptPathTranslated() throw()
  659. {
  660. ATLASSERT(m_spParent);
  661. return m_spParent->GetScriptPathTranslated();
  662. }
  663. LPCSTR GetPathTranslated() throw()
  664. {
  665. ATLASSERT(m_spParent);
  666. return m_spParent->GetPathTranslated();
  667. }
  668. DWORD GetTotalBytes() throw()
  669. {
  670. ATLASSERT(m_spParent);
  671. return m_spParent->GetTotalBytes();
  672. }
  673. DWORD GetAvailableBytes() throw()
  674. {
  675. ATLASSERT(m_spParent);
  676. return m_spParent->GetAvailableBytes();
  677. }
  678. BYTE *GetAvailableData() throw()
  679. {
  680. ATLASSERT(m_spParent);
  681. return m_spParent->GetAvailableData();
  682. }
  683. LPCSTR GetContentType() throw()
  684. {
  685. ATLASSERT(m_spParent);
  686. return m_spParent->GetContentType();
  687. }
  688. BOOL GetServerVariable(LPCSTR pszVariableName, LPSTR pvBuffer, DWORD *pdwSize) throw()
  689. {
  690. ATLASSERT(m_spParent);
  691. return m_spParent->GetServerVariable(pszVariableName, pvBuffer, pdwSize);
  692. }
  693. BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
  694. {
  695. ATLASSERT(m_spParent);
  696. return m_spParent->WriteClient(pvBuffer, pdwBytes);
  697. }
  698. BOOL AsyncWriteClient(void * pvBuffer, DWORD * pdwBytes) throw()
  699. {
  700. ATLASSERT(m_spParent);
  701. return m_spParent->AsyncWriteClient(pvBuffer, pdwBytes);
  702. }
  703. BOOL ReadClient(void * pvBuffer, DWORD * pdwSize) throw()
  704. {
  705. ATLASSERT(m_spParent);
  706. return m_spParent->ReadClient(pvBuffer, pdwSize);
  707. }
  708. BOOL AsyncReadClient(void * pvBuffer, DWORD * pdwSize) throw()
  709. {
  710. ATLASSERT(m_spParent);
  711. return m_spParent->AsyncReadClient(pvBuffer, pdwSize);
  712. }
  713. BOOL SendRedirectResponse(LPCSTR pszRedirectUrl) throw()
  714. {
  715. ATLASSERT(m_spParent);
  716. return m_spParent->SendRedirectResponse(pszRedirectUrl);
  717. }
  718. BOOL GetImpersonationToken(HANDLE * pToken) throw()
  719. {
  720. ATLASSERT(m_spParent);
  721. return m_spParent->GetImpersonationToken(pToken);
  722. }
  723. BOOL SendResponseHeader(LPCSTR pszHeader, LPCSTR pszStatusCode, BOOL fKeepConn) throw()
  724. {
  725. ATLASSERT(m_spParent);
  726. return m_spParent->SendResponseHeader(pszHeader, pszStatusCode, fKeepConn);
  727. }
  728. BOOL DoneWithSession(DWORD dwHttpStatusCode) throw()
  729. {
  730. ATLASSERT(m_spParent);
  731. return m_spParent->DoneWithSession(dwHttpStatusCode);
  732. }
  733. BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION pfn, DWORD * pdwContext) throw()
  734. {
  735. ATLASSERT(m_spParent);
  736. return m_spParent->RequestIOCompletion(pfn, pdwContext);
  737. }
  738. BOOL TransmitFile(HANDLE hFile, PFN_HSE_IO_COMPLETION pfn, void * pContext,
  739. LPCSTR szStatusCode, DWORD dwBytesToWrite, DWORD dwOffset, void * pvHead,
  740. DWORD dwHeadLen, void * pvTail, DWORD dwTailLen, DWORD dwFlags) throw()
  741. {
  742. ATLASSERT(m_spParent);
  743. return m_spParent->TransmitFile(hFile, pfn, pContext, szStatusCode,
  744. dwBytesToWrite, dwOffset, pvHead, dwHeadLen, pvTail, dwTailLen,
  745. dwFlags);
  746. }
  747. BOOL AppendToLog(LPCSTR szMessage, DWORD* pdwLen) throw()
  748. {
  749. ATLASSERT(m_spParent);
  750. return m_spParent->AppendToLog(szMessage, pdwLen);
  751. }
  752. BOOL MapUrlToPathEx(LPCSTR szLogicalPath, DWORD dwLen, HSE_URL_MAPEX_INFO *pumInfo) throw()
  753. {
  754. ATLASSERT(m_spParent);
  755. return m_spParent->MapUrlToPathEx(szLogicalPath, dwLen, pumInfo);
  756. }
  757. };
  758. // Wraps the EXTENSION_CONTROL_BLOCK structure used by IIS to provide
  759. // an ISAPI extension with information about the current request and
  760. // access to the web server's functionality.
  761. class CServerContext :
  762. public CComObjectRootEx<CComMultiThreadModel>,
  763. public IHttpServerContext
  764. {
  765. public:
  766. BEGIN_COM_MAP(CServerContext)
  767. COM_INTERFACE_ENTRY(IHttpServerContext)
  768. END_COM_MAP()
  769. // The constructor.
  770. CServerContext() throw()
  771. {
  772. m_pECB = NULL;
  773. m_bHeadersHaveBeenSent = false;
  774. }
  775. void Initialize(EXTENSION_CONTROL_BLOCK *pECB) throw()
  776. {
  777. ATLASSERT(pECB);
  778. m_pECB = pECB;
  779. // Initialize the translated script path
  780. _AtlGetScriptPathTranslated(GetPathTranslated(), m_strScriptPathTranslated);
  781. }
  782. // Returns a nul-terminated string that contains the HTTP method of the current request.
  783. // Examples of common HTTP methods include "GET" and "POST".
  784. // Equivalent to the REQUEST_METHOD server variable or EXTENSION_CONTROL_BLOCK::lpszMethod.
  785. LPCSTR GetRequestMethod() throw()
  786. {
  787. ATLASSERT(m_pECB);
  788. return m_pECB->lpszMethod;
  789. }
  790. // Returns a nul-terminated string that contains the query information.
  791. // This is the part of the URL that appears after the question mark (?).
  792. // Equivalent to the QUERY_STRING server variable or EXTENSION_CONTROL_BLOCK::lpszQueryString.
  793. LPCSTR GetQueryString() throw()
  794. {
  795. ATLASSERT(m_pECB);
  796. return m_pECB->lpszQueryString;
  797. }
  798. // Returns a nul-terminated string that contains the path of the current request.
  799. // This is the part of the URL that appears after the server name, but before the query string.
  800. // Equivalent to the PATH_INFO server variable or EXTENSION_CONTROL_BLOCK::lpszPathInfo.
  801. LPCSTR GetPathInfo() throw()
  802. {
  803. ATLASSERT(m_pECB);
  804. return m_pECB->lpszPathInfo;
  805. }
  806. // Call this function to retrieve a nul-terminated string containing the physical path of the script.
  807. //
  808. // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
  809. //
  810. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  811. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the
  812. // buffer (including the nul-terminating byte).
  813. // The script path is the same as GetPathTranslated up to the first .srf or .dll.
  814. // For example, if GetPathTranslated returns "c:\inetpub\vcisapi\hello.srf\goodmorning",
  815. // then this function returns "c:\inetpub\vcisapi\hello.srf".
  816. LPCSTR GetScriptPathTranslated() throw()
  817. {
  818. ATLASSERT(m_pECB);
  819. return m_strScriptPathTranslated;
  820. }
  821. // Returns a nul-terminated string that contains the translated path of the requested resource.
  822. // This is the path of the resource on the local server.
  823. // Equivalent to the PATH_TRANSLATED server variable or EXTENSION_CONTROL_BLOCK::lpszPathTranslated.
  824. LPCSTR GetPathTranslated() throw()
  825. {
  826. ATLASSERT(m_pECB);
  827. return m_pECB->lpszPathTranslated;
  828. }
  829. // Returns the total number of bytes to be received from the client.
  830. // If this value is 0xffffffff, then there are four gigabytes or more of available data.
  831. // In this case, ReadClient or AsyncReadClient should be called until no more data is returned.
  832. // Equivalent to the CONTENT_LENGTH server variable or EXTENSION_CONTROL_BLOCK::cbTotalBytes.
  833. DWORD GetTotalBytes() throw()
  834. {
  835. ATLASSERT(m_pECB);
  836. return m_pECB->cbTotalBytes;
  837. }
  838. // Returns the number of bytes available in the request buffer accessible via GetAvailableData.
  839. // If GetAvailableBytes returns the same value as GetTotalBytes, the request buffer contains the whole request.
  840. // Otherwise, the remaining data should be read from the client using ReadClient or AsyncReadClient.
  841. // Equivalent to EXTENSION_CONTROL_BLOCK::cbAvailable.
  842. DWORD GetAvailableBytes() throw()
  843. {
  844. ATLASSERT(m_pECB);
  845. return m_pECB->cbAvailable;
  846. }
  847. // Returns a pointer to the request buffer containing the data sent by the client.
  848. // The size of the buffer can be determined by calling GetAvailableBytes.
  849. // Equivalent to EXTENSION_CONTROL_BLOCK::lpbData
  850. BYTE *GetAvailableData() throw()
  851. {
  852. ATLASSERT(m_pECB);
  853. return m_pECB->lpbData;
  854. }
  855. // Returns a nul-terminated string that contains the content type of the data sent by the client.
  856. // Equivalent to the CONTENT_TYPE server variable or EXTENSION_CONTROL_BLOCK::lpszContentType.
  857. LPCSTR GetContentType() throw()
  858. {
  859. ATLASSERT(m_pECB);
  860. return m_pECB->lpszContentType;
  861. }
  862. // Call this function to retrieve a nul-terminated string containing the value of the requested server variable.
  863. // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
  864. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  865. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  866. // Equivalent to EXTENSION_CONTROL_BLOCK::GetServerVariable.
  867. BOOL GetServerVariable(
  868. LPCSTR pszVariableName,
  869. LPSTR pvBuffer,
  870. DWORD *pdwSize) throw()
  871. {
  872. ATLASSERT(m_pECB);
  873. ATLASSERT(pszVariableName);
  874. ATLASSERT(pdwSize);
  875. if (pszVariableName && pdwSize)
  876. {
  877. return m_pECB->GetServerVariable(m_pECB->ConnID, (LPSTR) pszVariableName,
  878. pvBuffer, pdwSize);
  879. }
  880. return FALSE;
  881. }
  882. // Synchronously sends the data present in the given buffer to the client that made the request.
  883. // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
  884. // Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_SYNC).
  885. BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
  886. {
  887. ATLASSERT(m_pECB);
  888. ATLASSERT(pvBuffer);
  889. ATLASSERT(pdwBytes);
  890. if (pvBuffer && pdwBytes)
  891. {
  892. return m_pECB->WriteClient(m_pECB->ConnID, pvBuffer, pdwBytes, HSE_IO_SYNC | HSE_IO_NODELAY);
  893. }
  894. return FALSE;
  895. }
  896. // Asynchronously sends the data present in the given buffer to the client that made the request.
  897. // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
  898. // Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_ASYNC).
  899. BOOL AsyncWriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
  900. {
  901. ATLASSERT(m_pECB);
  902. ATLASSERT(pvBuffer);
  903. ATLASSERT(pdwBytes);
  904. if (pvBuffer && pdwBytes)
  905. {
  906. return m_pECB->WriteClient(m_pECB->ConnID, pvBuffer, pdwBytes, HSE_IO_ASYNC | HSE_IO_NODELAY);
  907. }
  908. return FALSE;
  909. }
  910. // Call this function to synchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
  911. // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
  912. // Equivalent to EXTENSION_CONTROL_BLOCK::ReadClient.
  913. BOOL ReadClient(void *pvBuffer, DWORD *pdwSize) throw()
  914. {
  915. ATLASSERT(m_pECB);
  916. ATLASSERT(pvBuffer);
  917. ATLASSERT(pdwSize);
  918. if (pvBuffer && pdwSize)
  919. {
  920. return m_pECB->ReadClient(m_pECB->ConnID, pvBuffer, pdwSize);
  921. }
  922. return FALSE;
  923. }
  924. // Call this function to asynchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
  925. // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
  926. // Equivalent to the HSE_REQ_ASYNC_READ_CLIENT server support function.
  927. BOOL AsyncReadClient(void *pvBuffer, DWORD *pdwSize) throw()
  928. {
  929. // To call this function successfully someone has to have already
  930. // called RequestIOCompletion specifying the callback function
  931. // to be used for IO completion.
  932. ATLASSERT(m_pECB);
  933. ATLASSERT(pvBuffer);
  934. ATLASSERT(pdwSize);
  935. if (pvBuffer && pdwSize)
  936. {
  937. DWORD dwFlag = HSE_IO_ASYNC;
  938. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  939. HSE_REQ_ASYNC_READ_CLIENT, pvBuffer, pdwSize,
  940. &dwFlag);
  941. }
  942. return FALSE;
  943. }
  944. // Call this function to redirect the client to the specified URL.
  945. // The client receives a 302 (Found) HTTP status code.
  946. // Returns TRUE on success, and FALSE on failure.
  947. // Equivalent to the HSE_REQ_SEND_URL_REDIRECT_RESP server support function.
  948. BOOL SendRedirectResponse(LPCSTR pszRedirectUrl) throw()
  949. {
  950. ATLASSERT(m_pECB);
  951. ATLASSERT(pszRedirectUrl);
  952. if (pszRedirectUrl)
  953. {
  954. DWORD dwSize = (DWORD) strlen(pszRedirectUrl);
  955. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  956. HSE_REQ_SEND_URL_REDIRECT_RESP,
  957. (void *) pszRedirectUrl, &dwSize, NULL);
  958. }
  959. return FALSE;
  960. }
  961. // Call this function to retrieve a handle to the impersonation token for this request.
  962. // An impersonation token represents a user context. You can use the handle in calls to ImpersonateLoggedOnUser or SetThreadToken.
  963. // Do not call CloseHandle on the handle.
  964. // Returns TRUE on success, and FALSE on failure.
  965. // Equivalent to the HSE_REQ_GET_IMPERSONATION_TOKEN server support function.
  966. BOOL GetImpersonationToken(HANDLE * pToken) throw()
  967. {
  968. ATLASSERT(m_pECB);
  969. if (pToken)
  970. {
  971. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  972. HSE_REQ_GET_IMPERSONATION_TOKEN, pToken,
  973. NULL, NULL);
  974. }
  975. return FALSE;
  976. }
  977. // Call this function to send an HTTP response header to the client including the HTTP status, server version, message time, and MIME version.
  978. // Returns TRUE on success, and FALSE on failure.
  979. // Equivalent to the HSE_REQ_SEND_RESPONSE_HEADER_EX server support function.
  980. BOOL SendResponseHeader(
  981. LPCSTR pszHeader = "Content-Type: text/html\r\n\r\n",
  982. LPCSTR pszStatusCode = "200 OK",
  983. BOOL fKeepConn=FALSE) throw()
  984. {
  985. ATLASSERT(m_pECB);
  986. if (m_bHeadersHaveBeenSent)
  987. return TRUE;
  988. HSE_SEND_HEADER_EX_INFO hex;
  989. hex.pszStatus = pszStatusCode;
  990. hex.pszHeader = pszHeader;
  991. hex.cchStatus = (DWORD)(pszStatusCode ? strlen(pszStatusCode) : 0);
  992. hex.cchHeader = (DWORD)(pszHeader ? strlen(pszHeader) : 0);
  993. hex.fKeepConn = fKeepConn;
  994. m_bHeadersHaveBeenSent = true;
  995. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  996. HSE_REQ_SEND_RESPONSE_HEADER_EX,
  997. &hex, NULL, NULL);
  998. }
  999. // Call this function to terminate the session for the current request.
  1000. // Returns TRUE on success, and FALSE on failure.
  1001. // Equivalent to the HSE_REQ_DONE_WITH_SESSION server support function.
  1002. BOOL DoneWithSession(DWORD dwHttpStatusCode) throw()
  1003. {
  1004. ATLASSERT(m_pECB);
  1005. m_pECB->dwHttpStatusCode = dwHttpStatusCode;
  1006. DWORD dwStatusCode = (dwHttpStatusCode >= 400) ? HSE_STATUS_ERROR : HSE_STATUS_SUCCESS;
  1007. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  1008. HSE_REQ_DONE_WITH_SESSION, &dwStatusCode, NULL, NULL);
  1009. }
  1010. // Call this function to set a special callback function that will be used for handling the completion of asynchronous I/O operations.
  1011. // Returns TRUE on success, and FALSE on failure.
  1012. // Equivalent to the HSE_REQ_IO_COMPLETION server support function.
  1013. BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION pfn, DWORD *pdwContext) throw()
  1014. {
  1015. ATLASSERT(m_pECB);
  1016. ATLASSERT(pfn);
  1017. if (pfn)
  1018. {
  1019. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  1020. HSE_REQ_IO_COMPLETION, pfn, NULL, pdwContext);
  1021. }
  1022. return FALSE;
  1023. }
  1024. // Call this function to transmit a file asynchronously to the client.
  1025. // Returns TRUE on success, and FALSE on failure.
  1026. // Equivalent to the HSE_REQ_TRANSMIT_FILE server support function.
  1027. BOOL TransmitFile(
  1028. HANDLE hFile,
  1029. PFN_HSE_IO_COMPLETION pfn,
  1030. void *pContext,
  1031. LPCSTR szStatusCode,
  1032. DWORD dwBytesToWrite,
  1033. DWORD dwOffset,
  1034. void *pvHead,
  1035. DWORD dwHeadLen,
  1036. void *pvTail,
  1037. DWORD dwTailLen,
  1038. DWORD dwFlags) throw()
  1039. {
  1040. ATLASSERT(m_pECB);
  1041. HSE_TF_INFO tf;
  1042. tf.hFile = hFile;
  1043. tf.BytesToWrite = dwBytesToWrite;
  1044. tf.Offset = dwOffset;
  1045. tf.pContext = pContext;
  1046. tf.pfnHseIO = pfn;
  1047. tf.pHead = pvHead;
  1048. tf.HeadLength = dwHeadLen;
  1049. tf.pTail = pvTail;
  1050. tf.TailLength = dwTailLen;
  1051. tf.pszStatusCode = szStatusCode;
  1052. tf.dwFlags = dwFlags;
  1053. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  1054. HSE_REQ_TRANSMIT_FILE, &tf, NULL, NULL);
  1055. }
  1056. // Appends the string szMessage to the web server log for the current
  1057. // request.
  1058. // Returns TRUE on success, FALSE on failure.
  1059. // Equivalent to the HSE_APPEND_LOG_PARAMETER server support function.
  1060. BOOL AppendToLog(LPCSTR szMessage, DWORD *pdwLen) throw()
  1061. {
  1062. DWORD dwLen = 0;
  1063. if (!pdwLen)
  1064. dwLen = (DWORD)strlen(szMessage);
  1065. else
  1066. dwLen = *pdwLen;
  1067. return m_pECB->ServerSupportFunction(m_pECB->ConnID,
  1068. HSE_APPEND_LOG_PARAMETER, (void *)szMessage,
  1069. &dwLen, NULL);
  1070. }
  1071. // Maps a logical Url Path to a physical path
  1072. // Returns TRUE on success, FALSE on failure.
  1073. // Equivalent to the HSE_REQ_MAP_URL_TO_PATH_EX server support function.
  1074. // you can pass 0 for dwLen if szLogicalPath is null terminated
  1075. BOOL MapUrlToPathEx(LPCSTR szLogicalPath, DWORD dwLen, HSE_URL_MAPEX_INFO *pumInfo)
  1076. {
  1077. if (dwLen == 0)
  1078. dwLen = (DWORD) strlen(szLogicalPath);
  1079. return m_pECB->ServerSupportFunction(m_pECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, (void *) szLogicalPath,
  1080. &dwLen, (DWORD *) pumInfo);
  1081. }
  1082. protected:
  1083. // The pointer to the extension control block provided by IIS.
  1084. EXTENSION_CONTROL_BLOCK *m_pECB;
  1085. bool m_bHeadersHaveBeenSent;
  1086. // The translated script path
  1087. // char m_szScriptPathTranslated[MAX_PATH];
  1088. CFixedStringT<CStringA, MAX_PATH> m_strScriptPathTranslated;
  1089. }; // class CServerContext
  1090. class CPageCachePeer
  1091. {
  1092. public:
  1093. struct PeerInfo
  1094. {
  1095. CStringA strHeader;
  1096. CStringA strStatus;
  1097. };
  1098. static BOOL Add(PeerInfo * pDest, void * pSrc) throw()
  1099. {
  1100. PeerInfo *pIn = (PeerInfo *)pSrc;
  1101. pDest->strHeader = pIn->strHeader;
  1102. pDest->strStatus = pIn->strStatus;
  1103. return TRUE;
  1104. }
  1105. static BOOL Remove(const PeerInfo * /*pDest*/) throw()
  1106. {
  1107. return TRUE;
  1108. }
  1109. };
  1110. class CCacheServerContext :
  1111. public CComObjectRootEx<CComMultiThreadModel>,
  1112. public CWrappedServerContext,
  1113. public IPageCacheControl
  1114. {
  1115. private:
  1116. CAtlTemporaryFile m_cacheFile;
  1117. CComPtr<IFileCache> m_spCache;
  1118. char m_szFullUrl[ATL_URL_MAX_URL_LENGTH + 1];
  1119. FILETIME m_ftExpiration;
  1120. BOOL m_bIsCached;
  1121. CPageCachePeer::PeerInfo m_Headers;
  1122. public:
  1123. BEGIN_COM_MAP(CCacheServerContext)
  1124. COM_INTERFACE_ENTRY(IHttpServerContext)
  1125. COM_INTERFACE_ENTRY(IPageCacheControl)
  1126. END_COM_MAP()
  1127. // The constructor.
  1128. CCacheServerContext() throw()
  1129. {
  1130. }
  1131. void Initialize(IHttpServerContext *pParent, IFileCache *pCache) throw()
  1132. {
  1133. ATLASSERT(pParent);
  1134. m_spParent = pParent;
  1135. m_spCache = pCache;
  1136. m_cacheFile.Create();
  1137. LPCSTR szPathInfo = pParent->GetPathInfo();
  1138. LPCSTR szQueryString = pParent->GetQueryString();
  1139. LPSTR szTo = m_szFullUrl;
  1140. while (*szPathInfo)
  1141. {
  1142. *szTo++ = *szPathInfo++;
  1143. }
  1144. *szTo++ = '?';
  1145. while (*szQueryString)
  1146. {
  1147. *szTo++ = *szQueryString++;
  1148. }
  1149. *szTo = '\0';
  1150. memset(&m_ftExpiration, 0x00, sizeof(FILETIME));
  1151. m_bIsCached = TRUE;
  1152. }
  1153. // IPageCacheControl methods
  1154. HRESULT GetExpiration(FILETIME *pftExpiration) throw()
  1155. {
  1156. if (!pftExpiration)
  1157. return E_INVALIDARG;
  1158. *pftExpiration = m_ftExpiration;
  1159. return S_OK;
  1160. }
  1161. HRESULT SetExpiration(FILETIME ftExpiration) throw()
  1162. {
  1163. m_ftExpiration = ftExpiration;
  1164. return S_OK;
  1165. }
  1166. BOOL IsCached() throw()
  1167. {
  1168. return m_bIsCached;
  1169. }
  1170. BOOL Cache(BOOL bCache) throw()
  1171. {
  1172. BOOL bRet = m_bIsCached;
  1173. m_bIsCached = bCache;
  1174. return bRet;
  1175. }
  1176. BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
  1177. {
  1178. ATLASSERT(m_spParent);
  1179. if (S_OK != m_cacheFile.Write(pvBuffer, *pdwBytes))
  1180. return FALSE;
  1181. return m_spParent->WriteClient(pvBuffer, pdwBytes);
  1182. }
  1183. BOOL DoneWithSession(DWORD dwHttpStatusCode) throw()
  1184. {
  1185. ATLASSERT(m_spParent);
  1186. _ATLTRY
  1187. {
  1188. if (m_bIsCached)
  1189. {
  1190. CT2CA strFileName(m_cacheFile.TempFileName());
  1191. m_cacheFile.HandsOff();
  1192. m_spCache->AddFile(m_szFullUrl, strFileName, &m_ftExpiration, &m_Headers, NULL);
  1193. }
  1194. else
  1195. m_cacheFile.Close();
  1196. }
  1197. _ATLCATCHALL()
  1198. {
  1199. m_cacheFile.Close();
  1200. }
  1201. return m_spParent->DoneWithSession(dwHttpStatusCode);
  1202. }
  1203. BOOL GetImpersonationToken(HANDLE * pToken) throw()
  1204. {
  1205. ATLTRACE(atlTraceISAPI, 0, _T("Getting impersonation token for cached page -- Possible security problem"));
  1206. ATLASSERT(m_spParent);
  1207. return m_spParent->GetImpersonationToken(pToken);
  1208. }
  1209. BOOL AppendToLog(LPCSTR szMessage, DWORD* pdwLen) throw()
  1210. {
  1211. ATLTRACE(atlTraceISAPI, 0, _T("Logging on cached page -- future hits will not log"));
  1212. ATLASSERT(m_spParent);
  1213. return m_spParent->AppendToLog(szMessage, pdwLen);
  1214. }
  1215. BOOL SendResponseHeader(
  1216. LPCSTR pszHeader = "Content-Type: text/html\r\n\r\n",
  1217. LPCSTR pszStatusCode = "200 OK",
  1218. BOOL fKeepConn=FALSE) throw()
  1219. {
  1220. ATLASSERT(m_spParent);
  1221. m_Headers.strHeader = pszHeader;
  1222. m_Headers.strStatus = pszStatusCode;
  1223. return m_spParent->SendResponseHeader(pszHeader, pszStatusCode, fKeepConn);
  1224. }
  1225. // The methods below this point are actions that should not be performed on cached
  1226. // pages, as they will not behave correctly.
  1227. BOOL AsyncWriteClient(void * /*pvBuffer*/, DWORD * /*pdwBytes*/) throw()
  1228. {
  1229. // Asynchronous calls will not work
  1230. ATLASSERT(FALSE);
  1231. return FALSE;
  1232. }
  1233. BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/) throw()
  1234. {
  1235. // Nobody should be reading from this client if the page is being cached
  1236. // Also, only GET's are cached anyway
  1237. ATLASSERT(FALSE);
  1238. return FALSE;
  1239. }
  1240. BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/) throw()
  1241. {
  1242. ATLASSERT(FALSE);
  1243. return FALSE;
  1244. }
  1245. BOOL SendRedirectResponse(LPCSTR /*pszRedirectUrl*/) throw()
  1246. {
  1247. ATLASSERT(FALSE);
  1248. return FALSE;
  1249. }
  1250. BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION /*pfn*/, DWORD * /*pdwContext*/) throw()
  1251. {
  1252. ATLASSERT(FALSE);
  1253. return FALSE;
  1254. }
  1255. BOOL TransmitFile(
  1256. HANDLE /*hFile*/,
  1257. PFN_HSE_IO_COMPLETION /*pfn*/,
  1258. void * /*pContext*/,
  1259. LPCSTR /*szStatusCode*/,
  1260. DWORD /*dwBytesToWrite*/,
  1261. DWORD /*dwOffset*/,
  1262. void * /*pvHead*/,
  1263. DWORD /*dwHeadLen*/,
  1264. void * /*pvTail*/,
  1265. DWORD /*dwTailLen*/,
  1266. DWORD /*dwFlags*/) throw()
  1267. {
  1268. ATLASSERT(FALSE);
  1269. return FALSE;
  1270. }
  1271. BOOL MapUrlToPathEx(LPCSTR szLogicalPath, DWORD dwLen, HSE_URL_MAPEX_INFO *pumInfo) throw()
  1272. {
  1273. return CWrappedServerContext::MapUrlToPathEx(szLogicalPath, dwLen, pumInfo);
  1274. }
  1275. };
  1276. // This class represents a collection of validation failures.
  1277. // Use this class in combination with CValidateObject to validate
  1278. // forms, cookies, or query strings and build up a collection of
  1279. // failures. If appropriate, use the information in the collection
  1280. // to return detailed responses to the client to help them correct the failures.
  1281. #define EMPTY_PARAMS_ARE_FAILURES 0x00000001
  1282. class CValidateContext :
  1283. public CSimpleMap<CStringA, DWORD>
  1284. {
  1285. public:
  1286. CValidateContext(DWORD dwFlags=0) throw()
  1287. {
  1288. m_bFailures = false;
  1289. m_dwFlags = dwFlags;
  1290. }
  1291. // Call this function to add a validation result to the collection managed by this object.
  1292. // Each result is identified by a name and the type of result that occurred.
  1293. // The result codes are the VALIDATION_ codes defined at the top of this file.
  1294. // The bOnlyFailure parameter below is used to only allow failure results to
  1295. // be added to the list of failures. The reason you'd want to do this is that
  1296. // success codes should be the common case in validation routines so you can
  1297. // use bOnlyFailures to limit the number of allocations by this class's base
  1298. // map for mapping success results if you don't care about iterating successes.
  1299. bool AddResult(LPCSTR szName, DWORD type, bool bOnlyFailures = true) throw()
  1300. {
  1301. _ATLTRY
  1302. {
  1303. if (!VALIDATION_SUCCEEDED(type) ||
  1304. (type == VALIDATION_S_EMPTY && (m_dwFlags & EMPTY_PARAMS_ARE_FAILURES)))
  1305. m_bFailures = true;
  1306. if (!bOnlyFailures)
  1307. return TRUE == Add(szName, type); // add everything
  1308. else if (bOnlyFailures &&
  1309. (!VALIDATION_SUCCEEDED(type) ||
  1310. (type == VALIDATION_S_EMPTY && (m_dwFlags & EMPTY_PARAMS_ARE_FAILURES))))
  1311. return TRUE == Add(szName, type); // only add failures
  1312. }
  1313. _ATLCATCHALL()
  1314. {
  1315. }
  1316. return false;
  1317. }
  1318. // Returns true if there are no validation failures in the collection,
  1319. // returns false otherwise.
  1320. bool ParamsOK() throw()
  1321. {
  1322. return !m_bFailures;
  1323. }
  1324. // Returns the number of validation results in the collection.
  1325. int GetResultCount() throw()
  1326. {
  1327. return GetSize();
  1328. }
  1329. // Call this function to retrieve the name and type of a
  1330. // validation result based on its index in the collection.
  1331. // Returns true on success, false on failure.
  1332. //
  1333. // i The index of a result managed by this collection.
  1334. //
  1335. // strName On success, the name of the result with index i.
  1336. //
  1337. // type On success, the type of the result with index i.
  1338. bool GetResultAt(int i, CStringA& strName, DWORD& type) throw()
  1339. {
  1340. if ( i >= 0 && i < GetSize())
  1341. {
  1342. _ATLTRY
  1343. {
  1344. strName = GetKeyAt(i);
  1345. type = GetValueAt(i);
  1346. }
  1347. _ATLCATCHALL()
  1348. {
  1349. return false;
  1350. }
  1351. return true;
  1352. }
  1353. return false;
  1354. }
  1355. DWORD m_dwFlags;
  1356. protected:
  1357. bool m_bFailures;
  1358. }; // CValidateContext
  1359. class CAtlValidator
  1360. {
  1361. public:
  1362. template <class T, class TCompType>
  1363. static DWORD Validate(
  1364. T value,
  1365. TCompType nMinValue,
  1366. TCompType nMaxValue) throw()
  1367. {
  1368. DWORD dwRet = VALIDATION_S_OK;
  1369. if (value < static_cast<T>(nMinValue))
  1370. dwRet = VALIDATION_E_LENGTHMIN;
  1371. else if (value > static_cast<T>(nMaxValue))
  1372. dwRet = VALIDATION_E_LENGTHMAX;
  1373. return dwRet;
  1374. }
  1375. static DWORD Validate( LPCSTR pszValue, int nMinChars, int nMaxChars) throw()
  1376. {
  1377. DWORD dwRet = VALIDATION_S_OK;
  1378. int nChars = (int) strlen(pszValue);
  1379. if (nChars < nMinChars)
  1380. dwRet = VALIDATION_E_LENGTHMIN;
  1381. else if (nChars > nMaxChars)
  1382. dwRet = VALIDATION_E_LENGTHMAX;
  1383. return dwRet;
  1384. }
  1385. static DWORD Validate( double dblValue, double dblMinValue, double dblMaxValue) throw()
  1386. {
  1387. DWORD dwRet = VALIDATION_S_OK;
  1388. if ( dblValue < (dblMinValue - ATL_EPSILON) )
  1389. dwRet = VALIDATION_E_LENGTHMIN;
  1390. else if ( dblValue > (dblMaxValue + ATL_EPSILON) )
  1391. dwRet = VALIDATION_E_LENGTHMAX;
  1392. return dwRet;
  1393. }
  1394. };
  1395. // This class provides functions for retrieving and validating named values.
  1396. //
  1397. // The named values are expected to be provided in string form by the class used as
  1398. // the template parameter. CValidateObject provides the means of
  1399. // retrieving these values converted to data types chosen by you. You can validate the values
  1400. // by specifying a range for numeric values or by specifying a minimum and maximum length
  1401. // for string values.
  1402. //
  1403. // Call one of the Exchange overloads to retrieve a named value converted to your chosen data type.
  1404. // Call one of the Validate overloads to retrieve a named value converted to your chosen data type
  1405. // and validated against a minimum and maximum value or length supplied by you.
  1406. //
  1407. // To add validation functionality to the class TLookupClass, derive that class from CValidateObject<TLookupClass>
  1408. // and provide a Lookup function that takes a name as a string and returns the corresponding value
  1409. // also as a string:
  1410. // LPCSTR Lookup(LPCSTR szName);
  1411. template <class TLookupClass, class TValidator = CAtlValidator>
  1412. class CValidateObject
  1413. {
  1414. public:
  1415. // Exchange Routines
  1416. // Call this function to retrieve a named value converted to your chosen data type.
  1417. // Returns one of the following validation status codes:
  1418. // VALIDATION_S_OK The named value was found and could be converted successfully
  1419. // VALIDATION_S_EMPTY The name was present, but the value was empty
  1420. // VALIDATION_E_PARAMNOTFOUND The named value was not found
  1421. // VALIDATION_E_INVALIDPARAM The name was present, but the value could not be converted to the requested data type
  1422. // VALIDATION_E_FAIL An unspecified error occurred
  1423. // Pass a pointer to a validation context object if you want to add
  1424. // failures to the collection managed by that object.
  1425. template <class T>
  1426. ATL_NOINLINE DWORD Exchange(
  1427. LPCSTR szParam,
  1428. T* pValue,
  1429. CValidateContext *pContext = NULL) const throw()
  1430. {
  1431. DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
  1432. if (pValue)
  1433. {
  1434. const TLookupClass *pT = static_cast<const TLookupClass*>(this);
  1435. LPCSTR szValue = pT->Lookup(szParam);
  1436. if (szValue)
  1437. {
  1438. if (*szValue=='\0')
  1439. dwRet = VALIDATION_S_EMPTY;
  1440. else
  1441. {
  1442. dwRet = ConvertNumber(szValue, pValue);
  1443. }
  1444. }
  1445. }
  1446. else
  1447. dwRet = VALIDATION_E_FAIL; // invalid input
  1448. if (pContext)
  1449. pContext->AddResult(szParam, dwRet);
  1450. return dwRet;
  1451. }
  1452. template<>
  1453. ATL_NOINLINE DWORD Exchange(
  1454. LPCSTR szParam,
  1455. CString* pstrValue,
  1456. CValidateContext *pContext) const throw()
  1457. {
  1458. _ATLTRY
  1459. {
  1460. LPCSTR pszValue = NULL;
  1461. DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
  1462. if (pstrValue)
  1463. {
  1464. dwRet = Exchange(szParam, &pszValue, pContext);
  1465. if (VALIDATION_SUCCEEDED(dwRet) && pstrValue != NULL)
  1466. *pstrValue = CA2T(pszValue);
  1467. }
  1468. else
  1469. {
  1470. dwRet = VALIDATION_E_FAIL; // invalid input
  1471. if (pContext)
  1472. pContext->AddResult(szParam, dwRet);
  1473. }
  1474. return dwRet;
  1475. }
  1476. _ATLCATCHALL()
  1477. {
  1478. return VALIDATION_E_FAIL;
  1479. }
  1480. }
  1481. template<>
  1482. ATL_NOINLINE DWORD Exchange(
  1483. LPCSTR szParam,
  1484. LPCSTR* ppszValue,
  1485. CValidateContext *pContext) const throw()
  1486. {
  1487. DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
  1488. if (ppszValue)
  1489. {
  1490. *ppszValue = NULL;
  1491. const TLookupClass *pT = static_cast<const TLookupClass*>(this);
  1492. LPCSTR szValue = pT->Lookup(szParam);
  1493. if (szValue)
  1494. {
  1495. if (*szValue=='\0')
  1496. dwRet = VALIDATION_S_EMPTY;
  1497. else
  1498. {
  1499. *ppszValue = szValue;
  1500. dwRet = VALIDATION_S_OK;
  1501. }
  1502. }
  1503. }
  1504. else
  1505. dwRet = VALIDATION_E_FAIL; // invalid input
  1506. if (pContext)
  1507. pContext->AddResult(szParam, dwRet);
  1508. return dwRet;
  1509. }
  1510. template<>
  1511. ATL_NOINLINE DWORD Exchange(
  1512. LPCSTR szParam,
  1513. GUID* pValue,
  1514. CValidateContext *pContext) const throw()
  1515. {
  1516. DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
  1517. if (pValue)
  1518. {
  1519. const TLookupClass *pT = static_cast<const TLookupClass*>(this);
  1520. LPCSTR szValue = pT->Lookup(szParam);
  1521. if (szValue)
  1522. {
  1523. if (*szValue=='\0')
  1524. dwRet = VALIDATION_S_EMPTY;
  1525. else
  1526. {
  1527. USES_CONVERSION;
  1528. if (S_OK != CLSIDFromString(A2OLE(szValue), pValue))
  1529. {
  1530. dwRet = VALIDATION_E_INVALIDPARAM;
  1531. }
  1532. else
  1533. dwRet = VALIDATION_S_OK;
  1534. }
  1535. }
  1536. }
  1537. else
  1538. dwRet = VALIDATION_E_FAIL; // invalid input
  1539. if (pContext)
  1540. pContext->AddResult(szParam, dwRet);
  1541. return dwRet;
  1542. }
  1543. template<>
  1544. ATL_NOINLINE DWORD Exchange(
  1545. LPCSTR szParam,
  1546. bool* pbValue,
  1547. CValidateContext *pContext) const throw()
  1548. {
  1549. DWORD dwRet = VALIDATION_S_OK;
  1550. if (pbValue)
  1551. {
  1552. const TLookupClass *pT = static_cast<const TLookupClass*>(this);
  1553. LPCSTR szValue = pT->Lookup(szParam);
  1554. *pbValue = false;
  1555. if (szValue)
  1556. {
  1557. if (*szValue != '\0')
  1558. *pbValue = true;
  1559. }
  1560. }
  1561. else
  1562. dwRet = VALIDATION_E_FAIL; // invalid input
  1563. if (pContext)
  1564. pContext->AddResult(szParam, dwRet);
  1565. return dwRet;
  1566. }
  1567. DWORD ConvertNumber(LPCSTR szVal, ULONGLONG *pnVal) const throw()
  1568. {
  1569. if (!pnVal)
  1570. return VALIDATION_E_FAIL;
  1571. char *pEnd = NULL;
  1572. ULONGLONG n = _strtoui64(szVal, &pEnd, 10);
  1573. if (pEnd == szVal || errno == ERANGE)
  1574. {
  1575. errno = 0;
  1576. return VALIDATION_E_INVALIDPARAM;
  1577. }
  1578. *pnVal = n;
  1579. return VALIDATION_S_OK;
  1580. }
  1581. DWORD ConvertNumber(LPCSTR szVal, LONGLONG *pnVal) const throw()
  1582. {
  1583. if (!pnVal)
  1584. return VALIDATION_E_FAIL;
  1585. char *pEnd = NULL;
  1586. LONGLONG n = _strtoi64(szVal, &pEnd, 10);
  1587. if (pEnd == szVal || errno == ERANGE)
  1588. {
  1589. errno = 0;
  1590. return VALIDATION_E_INVALIDPARAM;
  1591. }
  1592. *pnVal = n;
  1593. return VALIDATION_S_OK;
  1594. }
  1595. DWORD ConvertNumber(LPCSTR szVal, double *pdblVal) const throw()
  1596. {
  1597. if (!pdblVal)
  1598. return VALIDATION_E_FAIL;
  1599. char *pEnd = NULL;
  1600. double d = strtod(szVal, &pEnd);
  1601. if (pEnd == szVal || errno == ERANGE)
  1602. {
  1603. errno = 0;
  1604. return VALIDATION_E_INVALIDPARAM;
  1605. }
  1606. *pdblVal = d;
  1607. return VALIDATION_S_OK;
  1608. }
  1609. DWORD ConvertNumber(LPCSTR szVal, int *pnVal) const throw()
  1610. {
  1611. return ConvertNumber(szVal, (long*)pnVal);
  1612. }
  1613. DWORD ConvertNumber(LPCSTR szVal, unsigned int *pnVal) const throw()
  1614. {
  1615. return ConvertNumber(szVal, (unsigned long*)pnVal);
  1616. }
  1617. DWORD ConvertNumber(LPCSTR szVal, long *pnVal) const throw()
  1618. {
  1619. if (!pnVal)
  1620. return VALIDATION_E_FAIL;
  1621. char *pEnd = NULL;
  1622. long n = strtol(szVal, &pEnd, 10);
  1623. if (pEnd == szVal || errno == ERANGE)
  1624. {
  1625. errno = 0;
  1626. return VALIDATION_E_INVALIDPARAM;
  1627. }
  1628. *pnVal = n;
  1629. return VALIDATION_S_OK;
  1630. }
  1631. DWORD ConvertNumber(LPCSTR szVal, unsigned long *pnVal) const throw()
  1632. {
  1633. if (!pnVal)
  1634. return VALIDATION_E_FAIL;
  1635. char *pEnd = NULL;
  1636. long n = strtoul(szVal, &pEnd, 10);
  1637. if (pEnd == szVal || errno == ERANGE)
  1638. {
  1639. errno = 0;
  1640. return VALIDATION_E_INVALIDPARAM;
  1641. }
  1642. *pnVal = n;
  1643. return VALIDATION_S_OK;
  1644. }
  1645. DWORD ConvertNumber(LPCSTR szVal, short *pnVal) const throw()
  1646. {
  1647. long nVal = 0;
  1648. DWORD dwRet = ConvertNumber(szVal, &nVal);
  1649. if (dwRet == VALIDATION_S_OK)
  1650. {
  1651. // clamp to the size of a short
  1652. if(nVal <= SHRT_MAX &&
  1653. nVal >= SHRT_MIN)
  1654. {
  1655. *pnVal = (short)nVal;
  1656. }
  1657. else
  1658. {
  1659. dwRet = VALIDATION_E_INVALIDPARAM;
  1660. }
  1661. }
  1662. return dwRet;
  1663. };
  1664. DWORD ConvertNumber(LPCSTR szVal, unsigned short *pnVal) const throw()
  1665. {
  1666. unsigned long nVal = 0;
  1667. DWORD dwRet = ConvertNumber(szVal, &nVal);
  1668. if (dwRet == VALIDATION_S_OK)
  1669. {
  1670. // clamp to the size of a short
  1671. if(nVal <= USHRT_MAX &&
  1672. nVal >= 0)
  1673. {
  1674. *pnVal = (unsigned short)nVal;
  1675. }
  1676. else
  1677. {
  1678. dwRet = VALIDATION_E_INVALIDPARAM;
  1679. }
  1680. }
  1681. return dwRet;
  1682. };
  1683. // Call this function to retrieve a named value converted to your chosen data type
  1684. // and validated against a minimum and maximum value or length supplied by you.
  1685. //
  1686. // Returns one of the following validation status codes:
  1687. // VALIDATION_S_OK The named value was found and could be converted successfully
  1688. // VALIDATION_S_EMPTY The name was present, but the value was empty
  1689. // VALIDATION_E_PARAMNOTFOUND The named value was not found
  1690. // VALIDATION_E_INVALIDPARAM The name was present, but the value could not be converted to the requested data type
  1691. // VALIDATION_E_LENGTHMIN The name was present and could be converted to the requested data type, but the value was too small
  1692. // VALIDATION_E_LENGTHMAX The name was present and could be converted to the requested data type, but the value was too large
  1693. // VALIDATION_E_FAIL An unspecified error occurred
  1694. //
  1695. // Validate can be used to convert and validate name-value pairs
  1696. // such as those associated with HTTP requests (query string, form fields, or cookie values).
  1697. // The numeric specializations validate the minimum and maximum value.
  1698. // The string specializations validate the minimum and maximum length.
  1699. //
  1700. // Pass a pointer to a validation context object if you want to add
  1701. // failures to the collection managed by that object.
  1702. //
  1703. // Note that you can validate the value of a parameter without
  1704. // storing its value by passing NULL for the second parameter. However
  1705. // if you pass NULL for the second parameter, make sure you cast the NULL to a
  1706. // type so that the compiler will call the correct specialization of Validate.
  1707. template <class T, class TCompType>
  1708. ATL_NOINLINE DWORD Validate(
  1709. LPCSTR Param,
  1710. T *pValue,
  1711. TCompType nMinValue,
  1712. TCompType nMaxValue,
  1713. CValidateContext *pContext = NULL) const throw()
  1714. {
  1715. T value;
  1716. DWORD dwRet = Exchange(Param, &value, pContext);
  1717. if ( dwRet == VALIDATION_S_OK )
  1718. {
  1719. if (pValue)
  1720. *pValue = value;
  1721. dwRet = TValidator::Validate(value, nMinValue, nMaxValue);
  1722. if (pContext && dwRet != VALIDATION_S_OK)
  1723. pContext->AddResult(Param, dwRet);
  1724. }
  1725. else if (dwRet == VALIDATION_S_EMPTY &&
  1726. !IsNullByType(nMinValue))
  1727. {
  1728. dwRet = VALIDATION_E_LENGTHMIN;
  1729. if (pContext)
  1730. {
  1731. pContext->SetAt(Param, VALIDATION_E_LENGTHMIN);
  1732. }
  1733. }
  1734. return dwRet;
  1735. }
  1736. // Specialization for strings. Comparison is for number of characters.
  1737. template<>
  1738. ATL_NOINLINE DWORD Validate(
  1739. LPCSTR Param,
  1740. LPCSTR* ppszValue,
  1741. int nMinChars,
  1742. int nMaxChars,
  1743. CValidateContext *pContext) const throw()
  1744. {
  1745. LPCSTR pszValue = NULL;
  1746. DWORD dwRet = Exchange(Param, &pszValue, pContext);
  1747. if (dwRet == VALIDATION_S_OK )
  1748. {
  1749. if (ppszValue)
  1750. *ppszValue = pszValue;
  1751. dwRet = TValidator::Validate(pszValue, nMinChars, nMaxChars);
  1752. if (pContext && dwRet != VALIDATION_S_OK)
  1753. pContext->AddResult(Param, dwRet);
  1754. }
  1755. else if (dwRet == VALIDATION_S_EMPTY &&
  1756. nMinChars > 0)
  1757. {
  1758. dwRet = VALIDATION_E_LENGTHMIN;
  1759. if (pContext)
  1760. {
  1761. pContext->SetAt(Param, VALIDATION_E_LENGTHMIN);
  1762. }
  1763. }
  1764. return dwRet;
  1765. }
  1766. // Specialization for CString so caller doesn't have to cast CString
  1767. template<>
  1768. ATL_NOINLINE DWORD Validate(
  1769. LPCSTR Param,
  1770. CString* pstrValue,
  1771. int nMinChars,
  1772. int nMaxChars,
  1773. CValidateContext *pContext) const throw()
  1774. {
  1775. _ATLTRY
  1776. {
  1777. LPCSTR szValue;
  1778. DWORD dwRet = Validate(Param, &szValue, nMinChars, nMaxChars, pContext);
  1779. if (pstrValue && dwRet == VALIDATION_S_OK )
  1780. *pstrValue = szValue;
  1781. return dwRet;
  1782. }
  1783. _ATLCATCHALL()
  1784. {
  1785. return VALIDATION_E_FAIL;
  1786. }
  1787. }
  1788. // Specialization for doubles, uses a different comparison.
  1789. template<>
  1790. ATL_NOINLINE DWORD Validate(
  1791. LPCSTR Param,
  1792. double* pdblValue,
  1793. double dblMinValue,
  1794. double dblMaxValue,
  1795. CValidateContext *pContext) const throw()
  1796. {
  1797. double dblValue;
  1798. DWORD dwRet = Exchange(Param, &dblValue, pContext);
  1799. if (dwRet == VALIDATION_S_OK)
  1800. {
  1801. if (pdblValue)
  1802. *pdblValue = dblValue;
  1803. dwRet = TValidator::Validate(dblValue, dblMinValue, dblMaxValue);
  1804. if (pContext && dwRet != VALIDATION_S_OK)
  1805. pContext->AddResult(Param, dwRet);
  1806. }
  1807. else if (dwRet == VALIDATION_S_EMPTY &&
  1808. (dblMinValue < -ATL_EPSILON ||
  1809. dblMinValue > ATL_EPSILON))
  1810. {
  1811. dwRet = VALIDATION_E_LENGTHMIN;
  1812. if (pContext)
  1813. {
  1814. pContext->SetAt(Param, VALIDATION_E_LENGTHMIN);
  1815. }
  1816. }
  1817. return dwRet;
  1818. }
  1819. };
  1820. // Cookies provide a way for a server to store a small amount of data on a client
  1821. // and have that data returned to it on each request the client makes.
  1822. // Use this class to represent a cookie to be sent from the server to a client
  1823. // or to represent a cookie that has been returned by a client to the originating server.
  1824. //
  1825. // At the HTTP level, a cookie is an application-defined name-value pair
  1826. // plus some standard attribute-value pairs that describe the way in which the user agent (web browser)
  1827. // should interact with the cookie. The HTTP format of a cookie is described in RFC 2109.
  1828. //
  1829. // The CCookie class provides methods to set and get the application-defined name and value
  1830. // as well as methods for the standard attributes. In addition, CCookie provides an abstraction
  1831. // on top of the application-defined value that allows it to be treated as a collection of name-value
  1832. // pairs if that model makes sense to you. Cookies with a single value are known as single-valued cookies.
  1833. // Cookies whose value consists of name-value pairs are known as multi-valued cookies or dictionary cookies.
  1834. //
  1835. // You can set the name of a cookie by calling SetName or using the appropriate constructor.
  1836. // The name of a cookie can be 0 or more characters.
  1837. //
  1838. // You can set the value of a cookie by calling SetValue or using the appropriate constructor.
  1839. // If the cookie has a value set, it is a single-valued cookie and attempts to add a name-value pair will fail.
  1840. // You can remove the value of a cookie by calling SetValue(NULL).
  1841. //
  1842. // You can add a name-value pair to a cookie by calling AddValue.
  1843. // If the cookie has any name-value pairs, it is a multi-valued cookie and attempts to set the primary value will fail.
  1844. // You can remove all the name-value pairs of a cookie by calling RemoveAllValues.
  1845. //
  1846. // Class CCookie follows the same rules for creating cookies as ASP does.
  1847. class CCookie :
  1848. public CValidateObject<CCookie>
  1849. {
  1850. typedef CStringA elemType;
  1851. typedef CAtlMap<elemType, elemType, CStringElementTraits<elemType>,
  1852. CStringElementTraits<elemType> > mapType;
  1853. public:
  1854. // Constructs a named cookie.
  1855. CCookie(LPCSTR szName) throw(...)
  1856. {
  1857. SetName(szName);
  1858. }
  1859. // Constructs a single-valued cookie.
  1860. CCookie(LPCSTR szName, LPCSTR szValue) throw(...)
  1861. {
  1862. SetName(szName);
  1863. SetValue(szValue);
  1864. }
  1865. CCookie(const CCookie& thatCookie) throw(...)
  1866. {
  1867. Copy(thatCookie);
  1868. }
  1869. CCookie& operator=(const CCookie& thatCookie) throw(...)
  1870. {
  1871. return Copy(thatCookie);
  1872. }
  1873. CCookie() throw()
  1874. {
  1875. }
  1876. BOOL IsEmpty() const throw()
  1877. {
  1878. return m_strName.IsEmpty();
  1879. }
  1880. // Call this function to set the name of this cookie.
  1881. // Returns TRUE on success, FALSE on failure.
  1882. // The name of a cookie cannot contain whitespace, semicolons or commas.
  1883. // The name should not begin with a dollar sign ($) since such names are reserved for future use.
  1884. BOOL SetName(LPCSTR szName) throw()
  1885. {
  1886. _ATLTRY
  1887. {
  1888. if (szName && *szName)
  1889. {
  1890. m_strName = szName;
  1891. return TRUE;
  1892. }
  1893. }
  1894. _ATLCATCHALL()
  1895. {
  1896. }
  1897. return FALSE;
  1898. }
  1899. // Call this function to retrieve the name of this cookie.
  1900. // Returns TRUE on success, FALSE on failure.
  1901. BOOL GetName(LPSTR szBuff, DWORD *pdwSize) const throw()
  1902. {
  1903. return CopyCString(m_strName, szBuff, pdwSize);
  1904. }
  1905. // Call this function to retrieve the name of this cookie.
  1906. // Returns TRUE on success, FALSE on failure.
  1907. BOOL GetName(CStringA &szName) const throw(...)
  1908. {
  1909. szName = m_strName;
  1910. return TRUE;
  1911. }
  1912. // Call this function to set the value of this cookie.
  1913. // Returns TRUE on success, FALSE on failure.
  1914. // Will fail if the cookie is multi-valued.
  1915. // Pass NULL to remove the cookie's value.
  1916. BOOL SetValue(LPCSTR szValue) throw(...)
  1917. {
  1918. if (m_Values.GetCount())
  1919. return FALSE; //already dictionary values in the cookie
  1920. if (!szValue)
  1921. m_strValue.Empty();
  1922. else
  1923. m_strValue = szValue;
  1924. return TRUE;
  1925. }
  1926. // Call this function to retrieve the value of this cookie.
  1927. // Returns TRUE on success, FALSE on failure.
  1928. // Returns TRUE if there is no value or the value is of zero length.
  1929. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  1930. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  1931. BOOL GetValue(LPSTR szBuff, DWORD *pdwSize) const throw()
  1932. {
  1933. return CopyCString(m_strValue, szBuff, pdwSize);
  1934. }
  1935. // Call this function to retrieve the value of this cookie.
  1936. // Returns TRUE on success, FALSE on failure.
  1937. BOOL GetValue(CStringA &strValue) const throw()
  1938. {
  1939. _ATLTRY
  1940. {
  1941. strValue = m_strValue;
  1942. return TRUE;
  1943. }
  1944. _ATLCATCHALL()
  1945. {
  1946. }
  1947. return FALSE;
  1948. }
  1949. // Call this function to add a name-value pair to the cookie.
  1950. // Returns TRUE on success, FALSE on failure.
  1951. // Will fail if the cookie is single-valued.
  1952. // If the named value is already present in the cookie, calling this function
  1953. // will modify the current value, otherwise a new name-value pair is added to the cookie.
  1954. // Call RemoveValue or RemoveAllValues to remove the name-value pairs
  1955. // added by this function.
  1956. BOOL AddValue(LPCSTR szName, LPCSTR szValue) throw()
  1957. {
  1958. if (m_strValue.GetLength())
  1959. return FALSE;
  1960. _ATLTRY
  1961. {
  1962. return m_Values.SetAt(szName, szValue) != NULL;
  1963. }
  1964. _ATLCATCHALL()
  1965. {
  1966. }
  1967. return FALSE;
  1968. }
  1969. // Call this function to modify a name-value pair associated with the cookie.
  1970. // Returns TRUE on success, FALSE on failure.
  1971. // Will fail if the cookie is single-valued.
  1972. // This function just calls AddValue so the name-value pair will be added if not already present.
  1973. // Use this function instead of AddValue to document the intentions of your call.
  1974. BOOL ModifyValue(LPCSTR szName, LPCSTR szValue) throw()
  1975. {
  1976. return AddValue(szName, szValue);
  1977. }
  1978. // Call this function to remove a name-value pair from the collection managed by this cookie.
  1979. // Returns TRUE on success, FALSE on failure.
  1980. BOOL RemoveValue(LPCSTR szName) throw()
  1981. {
  1982. return m_Values.RemoveKey(szName);
  1983. }
  1984. // Call this function to remove all the name-value pairs from the collection managed by this cookie.
  1985. void RemoveAllValues() throw()
  1986. {
  1987. m_Values.RemoveAll();
  1988. }
  1989. // Call this function to add an attribute-value pair to the collection of attributes for this cookie.
  1990. // Returns TRUE on success, FALSE on failure.
  1991. // This function is equivalent to calling ModifyAttribute.
  1992. // Both functions will add the attribute if not already present or
  1993. // change its value if it has already been applied to the cookie.
  1994. BOOL AddAttribute(LPCSTR szName, LPCSTR szValue) throw()
  1995. {
  1996. if (!szName || !*szName || !szValue)
  1997. return FALSE;
  1998. _ATLTRY
  1999. {
  2000. return (m_Attributes.SetAt(szName, szValue) != NULL);
  2001. }
  2002. _ATLCATCHALL()
  2003. {
  2004. }
  2005. return FALSE;
  2006. }
  2007. // Call this function to modify an attribute-value pair associated with the cookie.
  2008. // Returns TRUE on success, FALSE on failure.
  2009. // This function is equivalent to calling AddAttribute.
  2010. // Both functions will add the attribute if not already present or
  2011. // change its value if it has already been applied to the cookie.
  2012. BOOL ModifyAttribute(LPCSTR szName, LPCSTR szValue) throw()
  2013. {
  2014. return AddAttribute(szName, szValue);
  2015. }
  2016. // Call this function to remove an attribute-value pair from the collection of attributes managed by this cookie.
  2017. // Returns TRUE on success, FALSE on failure.
  2018. BOOL RemoveAttribute(LPCSTR szName) throw()
  2019. {
  2020. return m_Attributes.RemoveKey(szName);
  2021. }
  2022. // Call this function to remove all the attribute-value pairs from the collection of attributes managed by this cookie.
  2023. void RemoveAllAttributes() throw()
  2024. {
  2025. m_Attributes.RemoveAll();
  2026. }
  2027. // Call this function to set the Comment attribute of the cookie.
  2028. // Returns TRUE on success, FALSE on failure.
  2029. // The Comment attribute allows a web server to document its
  2030. // intended use of a cookie. This information may be displayed
  2031. // by supporting browsers so that the user of the web site can
  2032. // decide whether to initiate or continue a session with this cookie.
  2033. // This attribute is optional.
  2034. // Version 1 attribute.
  2035. BOOL SetComment(LPCSTR szComment) throw()
  2036. {
  2037. BOOL bRet = SetVersion(1);
  2038. if (bRet)
  2039. bRet = AddAttribute("comment", szComment);
  2040. return bRet;
  2041. }
  2042. // Call this function to set the CommentUrl attribute of the cookie.
  2043. // Returns TRUE on success, FALSE on failure.
  2044. // The CommentUrl attribute allows a web server to document its intended
  2045. // use of a cookie via a URL that the user of the web site can navigate to.
  2046. // The URL specified here should not send further cookies to the client to
  2047. // avoid frustrating the user.
  2048. // This attribute is optional.
  2049. // Version 1 attribute.
  2050. BOOL SetCommentUrl(LPCSTR szUrl) throw()
  2051. {
  2052. BOOL bRet = SetVersion(1);
  2053. if (bRet)
  2054. bRet = AddAttribute("commenturl", szUrl);
  2055. return bRet;
  2056. }
  2057. // Call this function to add or remove the Discard attribute of the cookie.
  2058. // Returns TRUE on success, FALSE on failure.
  2059. // The Discard attribute does not have a value.
  2060. // Call SetDiscard(TRUE) to add the Discard attribute
  2061. // or SetDiscard(FALSE) to remove the Discard attribute.
  2062. // Setting the Discard attribute tells a web browser that it should
  2063. // discard this cookie when the browser exits regardless of the
  2064. // value of the Max-Age attribute.
  2065. // This attribute is optional.
  2066. // When omitted, the default behavior is that the Max-Age attribute
  2067. // controls the lifetime of the cookie.
  2068. // Version 1 attribute.
  2069. BOOL SetDiscard(BOOL bDiscard) throw()
  2070. {
  2071. BOOL bRet = FALSE;
  2072. LPCSTR szKey = "Discard";
  2073. bRet = SetVersion(1);
  2074. if (bRet)
  2075. {
  2076. if (bDiscard == 0)
  2077. {
  2078. bRet = m_Attributes.RemoveKey(szKey);
  2079. }
  2080. else
  2081. {
  2082. _ATLTRY
  2083. {
  2084. bRet = m_Attributes.SetAt(szKey, " ") != 0;
  2085. }
  2086. _ATLCATCHALL()
  2087. {
  2088. bRet = FALSE;
  2089. }
  2090. }
  2091. }
  2092. return bRet;
  2093. }
  2094. // Call this function to set the Domain attribute of the cookie.
  2095. // Returns TRUE on success, FALSE on failure.
  2096. // The Domain attribute is used to indicate the domain to which the current
  2097. // cookie applies. Browsers should only send cookies back to the relevant domains.
  2098. // This attribute is optional.
  2099. // When omitted, the default behavior is for
  2100. // browsers to use the full domain of the server originating the cookie. You can
  2101. // set this attribute value explicitly if you want to share cookies among several servers.
  2102. // Version 0 & Version 1 attribute.
  2103. BOOL SetDomain(LPCSTR szDomain) throw()
  2104. {
  2105. BOOL bRet = SetVersion(1);
  2106. if (bRet)
  2107. bRet = AddAttribute("domain", szDomain);
  2108. return bRet;
  2109. }
  2110. // Call this function to set the Max-Age attribute of the cookie.
  2111. // Returns TRUE on success, FALSE on failure.
  2112. // The value of the Max-Age attribute is a lifetime in seconds for the cookie.
  2113. // When the time has expired, compliant browsers will discard this cookie
  2114. // (if they haven't already done so as a result of the Discard attribute).
  2115. // If Max-Age is set to zero, the browser discards the cookie immediately.
  2116. // This attribute is the Version 1 replacement for the Expires attribute.
  2117. // This attribute is optional.
  2118. // When omitted, the default behavior is for browsers to discard cookies
  2119. // when the user closes the browser.
  2120. // Version 1 attribute.
  2121. BOOL SetMaxAge(UINT nMaxAge) throw()
  2122. {
  2123. BOOL bRet = FALSE;
  2124. bRet = SetVersion(1);
  2125. if (bRet)
  2126. {
  2127. CHAR buff[20];
  2128. if (_itoa(nMaxAge, buff, 10))
  2129. {
  2130. bRet = AddAttribute("max-age", buff);
  2131. }
  2132. }
  2133. return bRet;
  2134. }
  2135. // Call this function to set the Path attribute of the cookie.
  2136. // Returns TRUE on success, FALSE on failure.
  2137. // The Path attribute specifies the subset of URLs to which this cookie applies.
  2138. // Only URLs that contain that path are allowed to read or modify the cookie.
  2139. // This attribute is optional.
  2140. // When omitted the default behavior is for browsers to treat the path of a cookie
  2141. // as the path of the request URL that generated the Set-Cookie response, up to,
  2142. // but not including, the right-most /.
  2143. // Version 0 & Version 1 attribute.
  2144. BOOL SetPath(LPCSTR szPath) throw()
  2145. {
  2146. BOOL bRet = SetVersion(1);
  2147. if (bRet)
  2148. bRet = AddAttribute("path", szPath);
  2149. return bRet;
  2150. }
  2151. // Call this function to set the Port attribute of the cookie.
  2152. // Returns TRUE on success, FALSE on failure.
  2153. // The Port attribute specifies the port to which this cookie applies.
  2154. // Only URLs accessed via that port are allowed to read or modify the cookie.
  2155. // This attribute is optional.
  2156. // When omitted the default behavior is for browsers to return the cookie via any port.
  2157. // Version 1 attribute.
  2158. BOOL SetPort(LPCSTR szPort) throw()
  2159. {
  2160. BOOL bRet = SetVersion(1);
  2161. if (bRet)
  2162. bRet = AddAttribute("port", szPort);
  2163. return bRet;
  2164. }
  2165. // Call this function to add or remove the Secure attribute of the cookie.
  2166. // Returns TRUE on success, FALSE on failure.
  2167. // The Secure attribute does not have a value.
  2168. // Call SetSecure(TRUE) to add the Secure attribute
  2169. // or SetSecure(FALSE) to remove the Secure attribute.
  2170. // Setting the Secure attribute tells a browser that it should
  2171. // transmit the cookie to the web server only via secure means such as HTTPS.
  2172. // This attribute is optional.
  2173. // When omitted, the default behavior is that the cookie
  2174. // will be sent via unsecured protocols.
  2175. // Version 0 & Version 1 attribute.
  2176. BOOL SetSecure(BOOL bSecure) throw()
  2177. {
  2178. BOOL bRet = FALSE;
  2179. LPCSTR szKey = "secure";
  2180. bRet = SetVersion(1);
  2181. if (bRet)
  2182. {
  2183. if (bSecure == 0)
  2184. {
  2185. bRet = m_Attributes.RemoveKey(szKey);
  2186. }
  2187. else
  2188. {
  2189. _ATLTRY
  2190. {
  2191. bRet = m_Attributes.SetAt(szKey, " ") != 0;
  2192. }
  2193. _ATLCATCHALL()
  2194. {
  2195. bRet = FALSE;
  2196. }
  2197. }
  2198. }
  2199. return bRet;
  2200. }
  2201. // Call this function to set the Version attribute of the cookie.
  2202. // Returns TRUE on success, FALSE on failure.
  2203. // This attribute is required for Version 1 cookies by RFC 2109 and must have a value of 1.
  2204. // However, you do not need to call SetVersion explicitly from your own code unless you need to
  2205. // force RFC 2109 compliance. CCookie will automatically set this attribute whenever
  2206. // you use a Version 1 attribute in your cookie.
  2207. // Version 1 attribute.
  2208. BOOL SetVersion(UINT nVersion) throw()
  2209. {
  2210. BOOL bRet = FALSE;
  2211. CHAR buff[20];
  2212. if (_itoa(nVersion, buff, 10))
  2213. {
  2214. bRet = AddAttribute("version", buff);
  2215. }
  2216. return bRet;
  2217. }
  2218. // Call this function to set the Expires attribute of the cookie.
  2219. // Returns TRUE on success, FALSE on failure.
  2220. // The Expires attribute specifies an absolute date and time at which this cookie
  2221. // should be discarded by web browsers. Pass a SYSTEMTIME holding a Greenwich Mean Time (GMT)
  2222. // value or a string in the following format:
  2223. // Wdy, DD-Mon-YY HH:MM:SS GMT
  2224. // This attribute is optional.
  2225. // When omitted, the default behavior is for browsers to discard cookies
  2226. // when the user closes the browser.
  2227. // This attribute has been superceded in Version 1 by the Max-Age attribute,
  2228. // but you should continue to use this attribute for Version 0 clients.
  2229. // Version 0 attribute.
  2230. BOOL SetExpires(LPCSTR szExpires) throw()
  2231. {
  2232. return AddAttribute("expires", szExpires);
  2233. }
  2234. BOOL SetExpires(const SYSTEMTIME &st) throw()
  2235. {
  2236. _ATLTRY
  2237. {
  2238. CFixedStringT<CStringA, ATLS_MAX_HTTP_DATE> strTime;
  2239. SystemTimeToHttpDate(st, strTime);
  2240. return SetExpires(strTime);
  2241. }
  2242. _ATLCATCHALL()
  2243. {
  2244. }
  2245. return FALSE;
  2246. }
  2247. // Call this function to look up the value of a name-value pair applied to this cookie.
  2248. // Returns the requested value if present or NULL if the name was not found.
  2249. LPCSTR Lookup(LPCSTR szName=NULL) const throw()
  2250. {
  2251. if (IsEmpty())
  2252. return NULL;
  2253. if (!szName && m_strValue.GetLength())
  2254. return m_strValue;
  2255. if (m_Values.GetCount())
  2256. {
  2257. const mapType::CPair *pPair = m_Values.Lookup(szName);
  2258. if (pPair)
  2259. return (LPCSTR)pPair->m_value;
  2260. }
  2261. return NULL;
  2262. }
  2263. // Call this function to clear the cookie of all content
  2264. // including name, value, name-value pairs, and attributes.
  2265. void Empty() throw()
  2266. {
  2267. m_strName = "";
  2268. m_strValue = "";
  2269. m_Attributes.RemoveAll();
  2270. m_Values.RemoveAll();
  2271. }
  2272. // Call this function to create a CCookie from a buffer.
  2273. // The passed in buffer contains a cookie header retrieved
  2274. // from the HTTP_COOKIE request variable
  2275. //
  2276. // review: support MBCS cookie values?
  2277. ATL_NOINLINE bool Parse(LPSTR pstart) throw()
  2278. {
  2279. //pStart points to the beginning of the cookie
  2280. //pEnd points to the character just past the end of the cookie.
  2281. //cookie is in the form name=value
  2282. LPSTR pEnd = pstart;
  2283. LPSTR pStart = pstart;
  2284. int index = 0;
  2285. LPSTR pTokens[16];
  2286. CStringA strName, name, value;
  2287. while (*pEnd != '&' && *pEnd != ';' && *pEnd != '\0')
  2288. pEnd++;
  2289. int nCount = CountOf('=', pStart, pEnd);
  2290. if (nCount == 2)
  2291. {
  2292. //first token is name, next tokens are first name/value in
  2293. //the Values collection
  2294. pEnd = pStart;
  2295. index = 1;
  2296. pTokens[0]=pStart;
  2297. while (*pEnd != '&' && *pEnd != ';' && *pEnd != '\0')
  2298. {
  2299. if (*pEnd == '=')
  2300. {
  2301. pTokens[index++] = pEnd;
  2302. }
  2303. pEnd++;
  2304. }
  2305. CopyToCString(strName, pTokens[0], pTokens[1]-1);
  2306. CopyToCString(name, pTokens[1]+1, pTokens[2]-1);
  2307. CopyToCString(value, pTokens[2]+1, pEnd-1);
  2308. _ATLTRY
  2309. {
  2310. m_strName = strName;
  2311. AddValue(name, value);
  2312. }
  2313. _ATLCATCHALL()
  2314. {
  2315. return false;
  2316. }
  2317. }
  2318. else if (nCount == 1)
  2319. {
  2320. LPSTR pCurr = pStart;
  2321. index = 1;
  2322. pTokens[0] = pStart;
  2323. while (pCurr != pEnd)
  2324. {
  2325. if (*pCurr == '=')
  2326. {
  2327. pTokens[index] = pCurr;
  2328. }
  2329. pCurr++;
  2330. }
  2331. CopyToCString(name, pTokens[0], pTokens[1]-1);
  2332. CopyToCString(value, pTokens[1]+1, pEnd-1);
  2333. _ATLTRY
  2334. {
  2335. m_strName = name;
  2336. m_strValue = value;
  2337. }
  2338. _ATLCATCHALL()
  2339. {
  2340. return false;
  2341. }
  2342. }
  2343. else if (nCount == 0)
  2344. {
  2345. // no value
  2346. if (pEnd > pStart)
  2347. {
  2348. CopyToCString(name, pStart, pEnd-1);
  2349. _ATLTRY
  2350. {
  2351. m_strName = name;
  2352. m_strValue = "";
  2353. }
  2354. _ATLCATCHALL()
  2355. {
  2356. return false;
  2357. }
  2358. }
  2359. }
  2360. else
  2361. return false; //error in cookie
  2362. if (*pEnd == '&')
  2363. {
  2364. //still have name/values to parse
  2365. pStart = pEnd+1;
  2366. pEnd = pStart;
  2367. while(1)
  2368. {
  2369. if (*pEnd == '=') //separates the name from the value
  2370. pTokens[0] = pEnd;
  2371. //Marks either the end of the name/values or
  2372. //the end of the name/value statement
  2373. if (*pEnd == '&' || *pEnd == ';' || *pEnd == '\0')
  2374. {
  2375. CopyToCString(name, pStart, pTokens[0]-1);
  2376. CopyToCString(value, pTokens[0]+1, pEnd-1);
  2377. AddValue(name, value);
  2378. pStart = pEnd+1;
  2379. }
  2380. if (*pEnd == ';' || *pEnd =='\0')
  2381. break;
  2382. pEnd++;
  2383. }
  2384. }
  2385. m_strName.TrimRight();
  2386. m_strName.TrimLeft();
  2387. m_strValue.TrimRight();
  2388. m_strValue.TrimLeft();
  2389. return true;
  2390. }
  2391. // Call this function to render this cookie
  2392. // into a buffer. Returns TRUE on success, FALSE on failure.
  2393. // On entry, pdwLen should point to a DWORD that indicates
  2394. // the size of the buffer in bytes. On exit, the DWORD contains
  2395. // the number of bytes transferred or available to be transferred
  2396. // into the buffer (including the nul-terminating byte). On
  2397. // success, the buffer will contain the correct HTTP
  2398. // representation of the current cookie suitable for sending to
  2399. // a client as the body of a Set-Cookie header.
  2400. ATL_NOINLINE BOOL Render(LPSTR szCookieBuffer, DWORD *pdwLen) const throw()
  2401. {
  2402. CStringA strCookie;
  2403. CStringA name, value;
  2404. DWORD nLenBuff = *pdwLen;
  2405. *pdwLen = 0;
  2406. // A cookie must have a name!
  2407. if (!m_strName.GetLength())
  2408. {
  2409. *pdwLen = 0;
  2410. return FALSE;
  2411. }
  2412. _ATLTRY
  2413. {
  2414. strCookie = m_strName;
  2415. int nValSize = (int) m_Values.GetCount();
  2416. //add value or name/value pairs.
  2417. if (m_strValue.GetLength())
  2418. {
  2419. strCookie += '=';
  2420. strCookie += m_strValue;
  2421. }
  2422. else if (nValSize)
  2423. {
  2424. strCookie += '=';
  2425. POSITION pos = m_Values.GetStartPosition();
  2426. for (int i=0; pos; i++)
  2427. {
  2428. m_Values.GetNextAssoc(pos, name, value);
  2429. strCookie += name;
  2430. if (value.GetLength())
  2431. {
  2432. strCookie += '=';
  2433. strCookie += value;
  2434. }
  2435. if (i <= nValSize-2)
  2436. strCookie += '&';
  2437. }
  2438. }
  2439. CStringA strAttributes;
  2440. if (!RenderAttributes(strAttributes))
  2441. return FALSE;
  2442. if (strAttributes.GetLength() > 0)
  2443. {
  2444. strCookie += "; ";
  2445. strCookie += strAttributes;
  2446. }
  2447. DWORD dwLenCookie = strCookie.GetLength() + 1;
  2448. if (dwLenCookie > nLenBuff)
  2449. {
  2450. *pdwLen = dwLenCookie;
  2451. return FALSE; //buffer wasn't big enough
  2452. }
  2453. *pdwLen = dwLenCookie - 1;
  2454. strcpy(szCookieBuffer, strCookie);
  2455. }
  2456. _ATLCATCHALL()
  2457. {
  2458. return FALSE;
  2459. }
  2460. return TRUE;
  2461. }
  2462. POSITION GetFirstAttributePos() const throw()
  2463. {
  2464. return m_Attributes.GetStartPosition();
  2465. }
  2466. const elemType& GetNextAttributeName(POSITION& pos) const throw()
  2467. {
  2468. return m_Attributes.GetNextKey(pos);
  2469. }
  2470. const elemType& GetAttributeValueAt(POSITION pos) const throw()
  2471. {
  2472. return m_Attributes.GetValueAt(pos);
  2473. }
  2474. BOOL GetNextAttrAssoc(POSITION& pos, elemType& key,
  2475. elemType& val) const throw()
  2476. {
  2477. _ATLTRY
  2478. {
  2479. m_Attributes.GetNextAssoc(pos, key, val);
  2480. }
  2481. _ATLCATCHALL()
  2482. {
  2483. return FALSE;
  2484. }
  2485. return TRUE;
  2486. }
  2487. POSITION GetFirstValuePos() const throw()
  2488. {
  2489. return m_Values.GetStartPosition();
  2490. }
  2491. const elemType& GetNextValueName(POSITION& pos) const throw()
  2492. {
  2493. return m_Values.GetNextKey(pos);
  2494. }
  2495. const elemType& GetValueAt(POSITION pos) const throw()
  2496. {
  2497. return m_Values.GetValueAt(pos);
  2498. }
  2499. BOOL GetNextValueAssoc(POSITION& pos, elemType& key,
  2500. elemType& val) const throw()
  2501. {
  2502. _ATLTRY
  2503. {
  2504. m_Values.GetNextAssoc(pos, key, val);
  2505. }
  2506. _ATLCATCHALL()
  2507. {
  2508. return FALSE;
  2509. }
  2510. return TRUE;
  2511. }
  2512. protected:
  2513. // Implementation
  2514. BOOL RenderAttributes(CStringA& strAttributes) const throw()
  2515. {
  2516. _ATLTRY
  2517. {
  2518. strAttributes = "";
  2519. POSITION pos = m_Attributes.GetStartPosition();
  2520. CStringA key, val;
  2521. for (int i=0; pos; i++)
  2522. {
  2523. if (i)
  2524. strAttributes += ";";
  2525. m_Attributes.GetNextAssoc(pos, key, val);
  2526. strAttributes += key;
  2527. strAttributes += '=';
  2528. strAttributes += val;
  2529. }
  2530. }
  2531. _ATLCATCHALL()
  2532. {
  2533. return FALSE;
  2534. }
  2535. return TRUE;
  2536. }
  2537. private:
  2538. CCookie& Copy(const CCookie& thatCookie) throw(...)
  2539. {
  2540. m_strName = thatCookie.m_strName;
  2541. m_strValue = thatCookie.m_strValue;
  2542. POSITION pos = NULL;
  2543. CStringA strName, strValue;
  2544. if (!thatCookie.m_Attributes.IsEmpty())
  2545. {
  2546. pos = thatCookie.m_Attributes.GetStartPosition();
  2547. while (pos)
  2548. {
  2549. thatCookie.m_Attributes.GetNextAssoc(pos, strName, strValue);
  2550. m_Attributes.SetAt(strName, strValue);
  2551. }
  2552. }
  2553. if (!thatCookie.m_Values.IsEmpty())
  2554. {
  2555. strName.Empty();
  2556. strValue.Empty();
  2557. pos = thatCookie.m_Values.GetStartPosition();
  2558. while (pos)
  2559. {
  2560. thatCookie.m_Values.GetNextAssoc(pos, strName, strValue);
  2561. m_Values.SetAt(strName, strValue);
  2562. }
  2563. }
  2564. return *this;
  2565. }
  2566. public:
  2567. // These are implementation only, use at your own risk!
  2568. // Map of attribute-value pairs applied to this cookie.
  2569. mapType m_Attributes;
  2570. // Map of name-value pairs applied to this cookie.
  2571. mapType m_Values;
  2572. // The name of this cookie.
  2573. CStringA m_strName;
  2574. // The value of this cookie.
  2575. CStringA m_strValue;
  2576. }; // class CCookie
  2577. class CSessionCookie : public CCookie
  2578. {
  2579. public:
  2580. CSessionCookie() throw(...)
  2581. {
  2582. if (!SetName(SESSION_COOKIE_NAME) &&
  2583. !SetPath("/"))
  2584. AtlThrow(E_OUTOFMEMORY);
  2585. }
  2586. CSessionCookie(LPCSTR szSessionID) throw(...)
  2587. {
  2588. if (!SetName(SESSION_COOKIE_NAME) &&
  2589. !SetPath("/") &&
  2590. !SetSessionID(szSessionID) )
  2591. AtlThrow(E_OUTOFMEMORY);
  2592. }
  2593. BOOL SetSessionID(LPCSTR szSessionID) throw()
  2594. {
  2595. ATLASSERT(szSessionID && szSessionID[0]);
  2596. return SetValue(szSessionID);
  2597. }
  2598. }; // class CSessionCookie
  2599. template<>
  2600. class CElementTraits< CCookie > :
  2601. public CElementTraitsBase< CCookie >
  2602. {
  2603. public:
  2604. typedef const CCookie& INARGTYPE;
  2605. typedef CCookie& OUTARGTYPE;
  2606. static ULONG Hash( INARGTYPE cookie )
  2607. {
  2608. return CStringElementTraits<CStringA>::Hash( cookie.m_strName );
  2609. }
  2610. static bool CompareElements( INARGTYPE cookie1, INARGTYPE cookie2 )
  2611. {
  2612. return( cookie1.m_strName == cookie2.m_strName );
  2613. }
  2614. static int CompareElementsOrdered( INARGTYPE cookie1, INARGTYPE cookie2 )
  2615. {
  2616. return( cookie1.m_strName.Compare( cookie2.m_strName ) );
  2617. }
  2618. };
  2619. ///////////////////////////////////////////////////////////////////////////////
  2620. // Request and response classes and support functions
  2621. // This class is a wrapper for CAtlMap that allows maps to be chained.
  2622. // It simply adds a bool that tells whether or not a map shares values
  2623. template <typename K, typename V, typename KTraits=CElementTraits<K>, typename VTraits=CElementTraits<V> >
  2624. class CHttpMap
  2625. {
  2626. private:
  2627. #ifdef ATL_HTTP_PARAM_MULTIMAP
  2628. typedef CRBMultiMap<K, V, KTraits, VTraits> MAPTYPE;
  2629. #else
  2630. typedef CAtlMap<K, V, KTraits, VTraits> MAPTYPE;
  2631. #endif // ATL_HTTP_PARAM_MULTIMAP
  2632. public:
  2633. typedef KTraits::INARGTYPE KINARGTYPE;
  2634. typedef KTraits::OUTARGTYPE KOUTARGTYPE;
  2635. typedef VTraits::INARGTYPE VINARGTYPE;
  2636. typedef VTraits::OUTARGTYPE VOUTARGTYPE;
  2637. typedef MAPTYPE::CPair CPair;
  2638. private:
  2639. bool m_bShared;
  2640. MAPTYPE m_map;
  2641. public:
  2642. CHttpMap() throw()
  2643. : m_bShared(false)
  2644. {
  2645. }
  2646. virtual ~CHttpMap()
  2647. {
  2648. }
  2649. inline bool IsShared() const throw()
  2650. {
  2651. return m_bShared;
  2652. }
  2653. inline void SetShared(bool bShared) throw()
  2654. {
  2655. m_bShared = bShared;
  2656. }
  2657. //
  2658. // exposed lookup and iteration functionality
  2659. //
  2660. inline size_t GetCount() const throw()
  2661. {
  2662. return m_map.GetCount();
  2663. }
  2664. inline bool IsEmpty() const throw()
  2665. {
  2666. return m_map.IsEmpty();
  2667. }
  2668. inline POSITION GetStartPosition() const throw()
  2669. {
  2670. #ifdef ATL_HTTP_PARAM_MULTIMAP
  2671. return m_map.GetHeadPosition();
  2672. #else
  2673. return m_map.GetStartPosition();
  2674. #endif // ATL_HTTP_PARAM_MULTIMAP
  2675. }
  2676. // Lookup wrappers
  2677. bool Lookup( KINARGTYPE key, VOUTARGTYPE value ) const throw()
  2678. {
  2679. _ATLTRY
  2680. {
  2681. #ifdef ATL_HTTP_PARAM_MULTIMAP
  2682. CPair *p = Lookup(key);
  2683. if (p != NULL)
  2684. {
  2685. value = p->m_value;
  2686. return true;
  2687. }
  2688. return false;
  2689. #else
  2690. return m_map.Lookup(key, value);
  2691. #endif // ATL_HTTP_PARAM_MULTIMAP
  2692. }
  2693. _ATLCATCHALL()
  2694. {
  2695. return false;
  2696. }
  2697. }
  2698. const CPair* Lookup( KINARGTYPE key ) const throw()
  2699. {
  2700. #ifdef ATL_HTTP_PARAM_MULTIMAP
  2701. POSITION pos = m_map.FindFirstWithKey(key);
  2702. if (pos != NULL)
  2703. {
  2704. return m_map.GetAt(pos);
  2705. }
  2706. return NULL;
  2707. #else
  2708. return m_map.Lookup(key);
  2709. #endif // ATL_HTTP_PARAM_MULTIMAP
  2710. }
  2711. CPair* Lookup( KINARGTYPE key ) throw()
  2712. {
  2713. #ifdef ATL_HTTP_PARAM_MULTIMAP
  2714. POSITION pos = m_map.FindFirstWithKey(key);
  2715. if (pos != NULL)
  2716. {
  2717. return m_map.GetAt(pos);
  2718. }
  2719. return NULL;
  2720. #else
  2721. return m_map.Lookup(key);
  2722. #endif // ATL_HTTP_PARAM_MULTIMAP
  2723. }
  2724. // iteration wrappers
  2725. void GetNextAssoc( POSITION& pos, KOUTARGTYPE key, VOUTARGTYPE value ) const throw(...)
  2726. {
  2727. m_map.GetNextAssoc(pos, key, value);
  2728. }
  2729. const CPair* GetNext( POSITION& pos ) const throw()
  2730. {
  2731. return m_map.GetNext(pos);
  2732. }
  2733. CPair* GetNext( POSITION& pos ) throw()
  2734. {
  2735. return m_map.GetNext(pos);
  2736. }
  2737. const K& GetNextKey( POSITION& pos ) const throw()
  2738. {
  2739. return m_map.GetNextKey(pos);
  2740. }
  2741. const V& GetNextValue( POSITION& pos ) const throw()
  2742. {
  2743. return m_map.GetNextValue(pos);
  2744. }
  2745. V& GetNextValue( POSITION& pos ) throw()
  2746. {
  2747. return m_map.GetNextValue(pos);
  2748. }
  2749. void GetAt( POSITION pos, KOUTARGTYPE key, VOUTARGTYPE value ) const throw(...)
  2750. {
  2751. return m_map.GetAt(pos, key, value);
  2752. }
  2753. CPair* GetAt( POSITION pos ) throw()
  2754. {
  2755. return m_map.GetAt(pos);
  2756. }
  2757. const CPair* GetAt( POSITION pos ) const throw()
  2758. {
  2759. return m_map.GetAt(pos);
  2760. }
  2761. const K& GetKeyAt( POSITION pos ) const throw()
  2762. {
  2763. return m_map.GetKeyAt(pos);
  2764. }
  2765. const V& GetValueAt( POSITION pos ) const throw()
  2766. {
  2767. return m_map.GetValueAt(pos);
  2768. }
  2769. V& GetValueAt( POSITION pos ) throw()
  2770. {
  2771. return m_map.GetValueAt(pos);
  2772. }
  2773. // modification wrappers
  2774. POSITION SetAt( KINARGTYPE key, VINARGTYPE value ) throw(...)
  2775. {
  2776. #ifdef ATL_HTTP_PARAM_MULTIMAP
  2777. return m_map.Insert(key, value);
  2778. #else
  2779. return m_map.SetAt(key, value);
  2780. #endif // ATL_HTTP_PARAM_MULTIMAP
  2781. }
  2782. virtual void RemoveAll() throw()
  2783. {
  2784. m_map.RemoveAll();
  2785. }
  2786. };
  2787. // This class is a wrapper for CHttpMap that assumes it's values are pointers that
  2788. // should be deleted on RemoveAll
  2789. template <typename K, typename V, typename KTraits=CElementTraits<K>, typename VTraits=CElementTraits<V> >
  2790. class CHttpPtrMap : public CHttpMap<K, V, KTraits, VTraits>
  2791. {
  2792. public:
  2793. typedef CHttpMap<K, V, KTraits, VTraits> Base;
  2794. void RemoveAll() throw()
  2795. {
  2796. if (!IsShared())
  2797. {
  2798. POSITION pos = GetStartPosition();
  2799. while (pos)
  2800. {
  2801. GetNextValue(pos)->Release();
  2802. }
  2803. }
  2804. Base::RemoveAll();
  2805. }
  2806. ~CHttpPtrMap() throw()
  2807. {
  2808. RemoveAll();
  2809. }
  2810. };
  2811. // This class represents a collection of request parameters - the name-value pairs
  2812. // found, for example, in a query string or in the data provided when a form is submitted to the server.
  2813. // Call Parse to build the collection from a string of URL-encoded data.
  2814. // Use the standard collection methods of the CSimpleMap base class to retrieve the
  2815. // decoded names and values.
  2816. // Use the methods of the CValidateObject base class to validate the parameters.
  2817. class CHttpRequestParams :
  2818. #if (defined(ATL_HTTP_PARAM_MAP_CASEINSENSITIVE))
  2819. public CHttpMap<CStringA, CStringA, CStringElementTraitsI<CStringA>, CStringElementTraitsI<CStringA> >,
  2820. #else
  2821. public CHttpMap<CStringA, CStringA, CStringElementTraits<CStringA>, CStringElementTraits<CStringA> >,
  2822. #endif
  2823. public CValidateObject<CHttpRequestParams>
  2824. {
  2825. public:
  2826. #if (defined(ATL_HTTP_PARAM_MAP_CASEINSENSITIVE))
  2827. typedef CHttpMap<CStringA, CStringA, CStringElementTraitsI<CStringA>, CStringElementTraitsI<CStringA> > BaseMap;
  2828. #else
  2829. typedef CHttpMap<CStringA, CStringA, CStringElementTraits<CStringA>, CStringElementTraits<CStringA> > BaseMap;
  2830. #endif
  2831. LPCSTR Lookup(LPCSTR szName) const throw()
  2832. {
  2833. if (!szName)
  2834. return NULL;
  2835. const CPair *p = BaseMap::Lookup(szName);
  2836. if (p)
  2837. {
  2838. return p->m_value;
  2839. }
  2840. return NULL;
  2841. }
  2842. // Call this function to build a collection of name-value pairs from a string of URL-encoded data.
  2843. // Returns TRUE on success, FALSE on failure.
  2844. // URL-encoded data:
  2845. // Each name-value pair is separated from the next by an ampersand (&)
  2846. // Each name is separated from its corresponding value by an equals signs (=)
  2847. // The end of the data to be parsed is indicated by a nul character (\0) or a pound symbol (#)
  2848. // A plus sign (+) in the input will be decoded as a space
  2849. // A percent sign (%) in the input signifies the start of an escaped octet.
  2850. // The next two digits represent the hexadecimal code of the character.
  2851. // For example, %21 is the escaped encoding for the US-ASCII exclamation mark and will be decoded as !.
  2852. // Common sources of URL-encoded data are query strings and the bodies of POST requests with content type of application/x-www-form-urlencoded.
  2853. //
  2854. // Parse and Render are complementary operations.
  2855. // Parse creates a collection from a string.
  2856. // Render creates a string from a collection.
  2857. ATL_NOINLINE BOOL Parse(LPSTR szQueryString) throw()
  2858. {
  2859. while (szQueryString && *szQueryString)
  2860. {
  2861. LPSTR szUrlCurrent = szQueryString;
  2862. LPSTR szName = szUrlCurrent;
  2863. LPSTR szPropValue;
  2864. while (*szQueryString)
  2865. {
  2866. if (*szQueryString == '=')
  2867. {
  2868. szQueryString++;
  2869. break;
  2870. }
  2871. if (*szQueryString == '&')
  2872. {
  2873. break;
  2874. }
  2875. if (*szQueryString == '+')
  2876. *szUrlCurrent = ' ';
  2877. else if (*szQueryString == '%')
  2878. {
  2879. // if there is a % without two characters
  2880. // at the end of the url we skip it
  2881. if (*(szQueryString+1) && *(szQueryString+2))
  2882. {
  2883. CHAR szCharCode[3];
  2884. szCharCode[0] = *(szQueryString+1);
  2885. szCharCode[1] = *(szQueryString+2);
  2886. szCharCode[2] = '\0';
  2887. LPSTR szEnd;
  2888. *szUrlCurrent = (CHAR) strtoul(szCharCode, &szEnd, 16);
  2889. szQueryString += 2;
  2890. }
  2891. else
  2892. *szUrlCurrent = '\0';
  2893. }
  2894. else
  2895. *szUrlCurrent = *szQueryString;
  2896. szQueryString++;
  2897. szUrlCurrent++;
  2898. }
  2899. if (*szUrlCurrent)
  2900. *szUrlCurrent++ = '\0';
  2901. // we have the property name
  2902. szPropValue = szUrlCurrent;
  2903. while (*szQueryString && *szQueryString != '#')
  2904. {
  2905. if (*szQueryString == '&')
  2906. {
  2907. szQueryString++;
  2908. break;
  2909. }
  2910. if (*szQueryString == '+')
  2911. *szUrlCurrent = ' ';
  2912. else if (*szQueryString == '%')
  2913. {
  2914. // if there is a % without two characters
  2915. // at the end of the url we skip it
  2916. if (*(szQueryString+1) && *(szQueryString+2))
  2917. {
  2918. CHAR szCharCode[3];
  2919. szCharCode[0] = *(szQueryString+1);
  2920. szCharCode[1] = *(szQueryString+2);
  2921. szCharCode[2] = '\0';
  2922. LPSTR szEnd;
  2923. *szUrlCurrent = (CHAR) strtoul(szCharCode, &szEnd, 16);
  2924. szQueryString += 2;
  2925. }
  2926. else
  2927. *szUrlCurrent = '\0';
  2928. }
  2929. else
  2930. *szUrlCurrent = *szQueryString;
  2931. szQueryString++;
  2932. szUrlCurrent++;
  2933. }
  2934. // we have the value
  2935. *szUrlCurrent = '\0';
  2936. szUrlCurrent++;
  2937. _ATLTRY
  2938. {
  2939. SetAt(szName, szPropValue);
  2940. }
  2941. _ATLCATCHALL()
  2942. {
  2943. return FALSE;
  2944. }
  2945. }
  2946. return TRUE;
  2947. }
  2948. // Call this function to render the map of names and values into a buffer as a URL-encoded string.
  2949. // Returns TRUE on success, FALSE on failure.
  2950. // On entry, pdwLen should point to a DWORD that indicates the size of the buffer in bytes.
  2951. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  2952. // On success, the buffer will contain the correct URL-encoded representation of the current object
  2953. // suitable for sending to a server as a query string or in the body of a form.
  2954. // URL-encoding:
  2955. // Each name-value pair is separated from the next by an ampersand (&)
  2956. // Each name is separated from its corresponding value by an equals signs (=)
  2957. // A space is encoded as a plus sign (+).
  2958. // Other unsafe characters (as determined by AtlIsUnsafeUrlChar) are encoded as escaped octets.
  2959. // An escaped octet is a percent sign (%) followed by two digits representing the hexadecimal code of the character.
  2960. //
  2961. // Parse and Render are complementary operations.
  2962. // Parse creates a collection from a string.
  2963. // Render creates a string from a collection.
  2964. ATL_NOINLINE BOOL Render(LPSTR szParameters, LPDWORD pdwLen) throw()
  2965. {
  2966. ATLASSERT(szParameters);
  2967. ATLASSERT(pdwLen);
  2968. _ATLTRY
  2969. {
  2970. if (GetCount() == 0)
  2971. {
  2972. *szParameters = '\0';
  2973. *pdwLen = 0;
  2974. return TRUE;
  2975. }
  2976. CStringA strParams;
  2977. POSITION pos = GetStartPosition();
  2978. while (pos != NULL)
  2979. {
  2980. LPCSTR szBuf = GetKeyAt(pos);
  2981. EscapeToCString(strParams, szBuf);
  2982. szBuf = GetValueAt(pos);
  2983. if (*szBuf)
  2984. {
  2985. strParams+= '=';
  2986. EscapeToCString(strParams, szBuf);
  2987. }
  2988. strParams+= '&';
  2989. GetNext(pos);
  2990. }
  2991. DWORD dwLen = strParams.GetLength();
  2992. strParams.Delete(dwLen-1);
  2993. BOOL bRet = TRUE;
  2994. if (dwLen >= *pdwLen)
  2995. {
  2996. bRet = FALSE;
  2997. }
  2998. else
  2999. {
  3000. dwLen--;
  3001. memcpy(szParameters, static_cast<LPCSTR>(strParams), dwLen);
  3002. szParameters[dwLen] = '\0';
  3003. }
  3004. *pdwLen = dwLen;
  3005. return bRet;
  3006. }
  3007. _ATLCATCHALL()
  3008. {
  3009. return FALSE;
  3010. }
  3011. }
  3012. }; // class CHttpRequestParams
  3013. #define MAX_TOKEN_LENGTH (MAX_PATH)
  3014. // This class represents the information about a file that has been uploaded to the web server.
  3015. class CHttpRequestFile : public IHttpFile
  3016. {
  3017. protected:
  3018. // The name of the form field used to upload the file.
  3019. CHAR m_szParamName[MAX_TOKEN_LENGTH];
  3020. // The original file name of the uploaded file as set by the client.
  3021. CHAR m_szFileName[MAX_PATH];
  3022. // The original path and file name of the uploaded file as set by the client.
  3023. CHAR m_szFullFileName[MAX_PATH];
  3024. // The MIME type of the uploaded file.
  3025. CHAR m_szContentType[MAX_TOKEN_LENGTH];
  3026. // The name of the uploaded file on the server.
  3027. CHAR m_szTempFileName[MAX_PATH];
  3028. // The size of the file in bytes.
  3029. ULONGLONG m_nFileSize;
  3030. public:
  3031. // The constructor.
  3032. CHttpRequestFile(
  3033. LPCSTR pParamName,
  3034. LPCSTR pFileName,
  3035. LPCSTR pTempFileName,
  3036. LPCSTR pContentType,
  3037. const ULONGLONG& nFileSize) throw()
  3038. {
  3039. ATLASSERT(pFileName);
  3040. m_szParamName[0] = 0;
  3041. m_szFileName[0] = 0;
  3042. m_szTempFileName[0] = 0;
  3043. m_szFullFileName[0] = 0;
  3044. m_szContentType[0] = 0;
  3045. m_nFileSize = nFileSize;
  3046. strcpy(m_szParamName, pParamName);
  3047. strcpy(m_szFullFileName, pFileName);
  3048. strcpy(m_szTempFileName, pTempFileName);
  3049. if (pContentType && *pContentType)
  3050. {
  3051. strcpy(m_szContentType, pContentType);
  3052. }
  3053. // Set m_szFileName to be the file name without the path.
  3054. // This is likely to be the most meaningful part of the
  3055. // original file name once the file reaches the server.
  3056. LPSTR szTmp = m_szFullFileName;
  3057. LPSTR szFile = m_szFileName;
  3058. while (*szTmp)
  3059. {
  3060. if (*szTmp == '\\')
  3061. {
  3062. szFile = m_szFileName;
  3063. }
  3064. else
  3065. {
  3066. *szFile++ = *szTmp;
  3067. }
  3068. szTmp++;
  3069. }
  3070. *szFile = 0;
  3071. }
  3072. //=======================================
  3073. // IHttpFile interface
  3074. //=======================================
  3075. LPCSTR GetParamName() throw()
  3076. {
  3077. return m_szParamName;
  3078. }
  3079. LPCSTR GetFileName() throw()
  3080. {
  3081. return m_szFileName;
  3082. }
  3083. LPCSTR GetFullFileName() throw()
  3084. {
  3085. return m_szFullFileName;
  3086. }
  3087. LPCSTR GetContentType() throw()
  3088. {
  3089. return m_szContentType;
  3090. }
  3091. LPCSTR GetTempFileName() throw()
  3092. {
  3093. return m_szTempFileName;
  3094. }
  3095. ULONGLONG GetFileSize() throw()
  3096. {
  3097. return m_nFileSize;
  3098. }
  3099. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
  3100. {
  3101. if (!ppv)
  3102. {
  3103. return E_POINTER;
  3104. }
  3105. if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
  3106. InlineIsEqualGUID(riid, __uuidof(IHttpFile)))
  3107. {
  3108. *ppv = static_cast<IUnknown*>(static_cast<IHttpFile*>(this));
  3109. AddRef();
  3110. return S_OK;
  3111. }
  3112. return E_NOINTERFACE;
  3113. }
  3114. ULONG STDMETHODCALLTYPE AddRef() throw()
  3115. {
  3116. ATLASSERT( FALSE );
  3117. return 1;
  3118. }
  3119. ULONG STDMETHODCALLTYPE Release() throw()
  3120. {
  3121. delete this;
  3122. return 1;
  3123. }
  3124. }; // class CHttpRequestFile
  3125. // utility function to ReadData from a ServerContext
  3126. ATL_NOINLINE inline
  3127. BOOL ReadClientData(IHttpServerContext *pServerContext, LPSTR pbDest, LPDWORD pdwLen, DWORD dwBytesRead) throw()
  3128. {
  3129. ATLASSERT(pServerContext != NULL);
  3130. ATLASSERT(pbDest != NULL);
  3131. ATLASSERT(pdwLen != NULL);
  3132. DWORD dwToRead = *pdwLen;
  3133. DWORD dwAvailableBytes = pServerContext->GetAvailableBytes();
  3134. DWORD dwRead(0);
  3135. // Read from available data first
  3136. if (dwBytesRead < dwAvailableBytes)
  3137. {
  3138. LPBYTE pbAvailableData = pServerContext->GetAvailableData();
  3139. pbAvailableData+= dwBytesRead;
  3140. DWORD dwAvailableToRead = min(dwToRead, dwAvailableBytes-dwBytesRead);
  3141. memcpy(pbDest, pbAvailableData, dwAvailableToRead);
  3142. dwBytesRead+= dwAvailableToRead;
  3143. dwToRead-= dwAvailableToRead;
  3144. pbDest+= dwAvailableToRead;
  3145. dwRead+= dwAvailableToRead;
  3146. }
  3147. DWORD dwTotalBytes = pServerContext->GetTotalBytes();
  3148. // If there is still more to read after the available data is exhausted
  3149. if (dwToRead && dwBytesRead < dwTotalBytes)
  3150. {
  3151. DWORD dwClientBytesToRead = min(pServerContext->GetTotalBytes()-dwBytesRead, dwToRead);
  3152. DWORD dwClientBytesRead = 0;
  3153. // keep on reading until we've read the amount requested
  3154. do
  3155. {
  3156. dwClientBytesRead = dwClientBytesToRead;
  3157. if (!pServerContext->ReadClient(pbDest, &dwClientBytesRead))
  3158. {
  3159. return FALSE;
  3160. }
  3161. dwClientBytesToRead-= dwClientBytesRead;
  3162. pbDest+= dwClientBytesRead;
  3163. } while (dwClientBytesToRead != 0 && dwClientBytesRead != 0);
  3164. dwRead+= dwToRead-dwClientBytesToRead;
  3165. }
  3166. *pdwLen = dwRead;
  3167. return TRUE;
  3168. }
  3169. #define FORM_BUFFER_SIZE 2048
  3170. #define MAX_MIME_LINE_LEN 1024
  3171. #define MAX_MIME_BOUNDARY_LEN 128
  3172. #define MAX_PARAM_LEN _MAX_PATH
  3173. #define MAX_CONTENT_TYPE_LEN 512
  3174. enum ATL_FORM_FLAGS
  3175. {
  3176. ATL_FORM_FLAG_NONE = 0,
  3177. ATL_FORM_FLAG_IGNORE_FILES = 1,
  3178. ATL_FORM_FLAG_REFUSE_FILES = 2,
  3179. ATL_FORM_FLAG_IGNORE_EMPTY_FILES = 4,
  3180. ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS = 8,
  3181. };
  3182. // Use this class to read multipart/form-data from the associated server context
  3183. // and generate files as necessary from the data in the body of the request.
  3184. class CMultiPartFormParser
  3185. {
  3186. protected:
  3187. LPSTR m_pCurrent;
  3188. LPSTR m_pEnd;
  3189. LPSTR m_pStart;
  3190. CHAR m_szBoundary[MAX_MIME_BOUNDARY_LEN+2];
  3191. CHAR m_szSearchBoundary[MAX_MIME_BOUNDARY_LEN+4];
  3192. DWORD m_dwBoundaryLen;
  3193. BOOL m_bFinished;
  3194. CComPtr<IHttpServerContext> m_spServerContext;
  3195. public:
  3196. // The constructor.
  3197. CMultiPartFormParser(IHttpServerContext* pServerContext) throw() :
  3198. m_pCurrent(NULL),
  3199. m_pEnd(NULL),
  3200. m_pStart(NULL),
  3201. m_dwBoundaryLen(0),
  3202. m_bFinished(FALSE),
  3203. m_spServerContext(pServerContext)
  3204. {
  3205. *m_szBoundary = '\0';
  3206. }
  3207. ~CMultiPartFormParser() throw()
  3208. {
  3209. // free memory if necessary
  3210. if (m_spServerContext->GetTotalBytes() > m_spServerContext->GetAvailableBytes())
  3211. {
  3212. free(m_pStart);
  3213. }
  3214. }
  3215. // Call this function to read multipart/form-data from the current HTTP request,
  3216. // allowing files to be uploaded to the web server.
  3217. //
  3218. // Returns TRUE on success, FALSE on failure.
  3219. //
  3220. // Forms can be sent to a web server using one of two encodings: application/x-www-form-urlencoded or multipart/form-data.
  3221. // In addition to the simple name-value pairs typically associated with
  3222. // application/x-www-form-urlencoded form data, multipart/form-data (as
  3223. // described in RFC 2388) can also contain files to be uploaded
  3224. // to the web server.
  3225. //
  3226. // This function will generate a physical file for each file contained in the multipart/form-data request body.
  3227. // The generated files are stored in the server's temporary directory as returned by the
  3228. // GetTempPath API and are named using the GetTempFileName API.
  3229. // The information about each file can be obtained from the elements of the Files array.
  3230. // You can retrieve the original name of the file on the client, the name of the generated file on the server,
  3231. // the MIME content type of the uploaded file, the name of the form field associated with that file, and the size in
  3232. // bytes of the file. All this information is exposed by the CHttpRequestFile objects in the array.
  3233. //
  3234. // In addition to generating files and populating the Files array with information about them,
  3235. // this function also populates the pQueryParams array with the names and values of the other form fields
  3236. // contained in the current request. The file fields are also added to this array. The value of these fields
  3237. // is the full name of the generated file on the server.
  3238. //
  3239. // Note that files can be generated even if this function returns FALSE unless you specify either the
  3240. // ATL_FORM_FLAG_IGNORE_FILES or the ATL_FORM_FLAG_REFUSE_FILES flag. If you don't specify one of these
  3241. // flags, you should always check the Files array for generated files and delete any that are no longer
  3242. // needed to prevent your web server from running out of disk space.
  3243. //
  3244. // dwFlags can be a combination of one or more of the following values:
  3245. // ATL_FORM_FLAG_NONE Default behavior.
  3246. // ATL_FORM_FLAG_IGNORE_FILES Any attempt to upload files is ignored.
  3247. // ATL_FORM_FLAG_REFUSE_FILES Any attempt to upload files is treated as a failure. The function will return FALSE.
  3248. // ATL_FORM_FLAG_IGNORE_EMPTY_FILES Files with a size of zero bytes are ignored.
  3249. // ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS Fields with no content are ignored.
  3250. ATL_NOINLINE BOOL GetMultiPartData(CHttpMap<CStringA, IHttpFile*, CStringElementTraits<CStringA> >& Files,
  3251. CHttpRequestParams* pQueryParams,
  3252. DWORD dwFlags=ATL_FORM_FLAG_NONE) throw()
  3253. {
  3254. _ATLTRY
  3255. {
  3256. if (!InitializeParser())
  3257. {
  3258. return FALSE;
  3259. }
  3260. //Get to the first boundary
  3261. if (!ReadUntilBoundary())
  3262. {
  3263. return FALSE;
  3264. }
  3265. CStringA strParamName;
  3266. CStringA strFileName;
  3267. CStringA strContentType;
  3268. CStringA strData;
  3269. BOOL bFound;
  3270. while (!m_bFinished)
  3271. {
  3272. // look for "name" field
  3273. if (!GetMimeData(strParamName, "name=", sizeof("name=")-1, &bFound) || !bFound)
  3274. {
  3275. ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
  3276. return FALSE;
  3277. }
  3278. // see if it's a file
  3279. if (!GetMimeData(strFileName, "filename=", sizeof("filename=")-1, &bFound))
  3280. {
  3281. ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
  3282. return FALSE;
  3283. }
  3284. if (bFound)
  3285. {
  3286. if (dwFlags & ATL_FORM_FLAG_REFUSE_FILES)
  3287. {
  3288. return FALSE;
  3289. }
  3290. if (!strFileName.GetLength())
  3291. {
  3292. ReadUntilBoundary();
  3293. continue;
  3294. }
  3295. if (!GetMimeData(strContentType, "Content-Type:", sizeof("Content-Type:")-1, &bFound, TRUE))
  3296. {
  3297. ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
  3298. return FALSE;
  3299. }
  3300. // move to the actual uploaded data
  3301. if (!MoveToData())
  3302. {
  3303. ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
  3304. return FALSE;
  3305. }
  3306. // if the user doesn't want files, don't save the file
  3307. if (dwFlags & ATL_FORM_FLAG_IGNORE_FILES)
  3308. {
  3309. if (!ReadUntilBoundary(NULL, NULL))
  3310. {
  3311. return FALSE;
  3312. }
  3313. continue;
  3314. }
  3315. CAtlTemporaryFile ctf;
  3316. HRESULT hr = ctf.Create();
  3317. if (hr != S_OK)
  3318. return FALSE;
  3319. if (!ReadUntilBoundary(NULL, &ctf))
  3320. {
  3321. ctf.Close();
  3322. return FALSE;
  3323. }
  3324. ULONGLONG nFileSize = 0;
  3325. if (ctf.GetSize(nFileSize) != S_OK)
  3326. return FALSE;
  3327. if ((dwFlags & ATL_FORM_FLAG_IGNORE_EMPTY_FILES) && nFileSize == 0)
  3328. {
  3329. ctf.Close();
  3330. continue;
  3331. }
  3332. //REVIEW: if exceptions are thrown, the temp file created by ctf is never removed
  3333. ctf.HandsOff();
  3334. //REVIEW: pFile always leaks
  3335. CHttpRequestFile* pFile = NULL;
  3336. CT2AEX<MAX_PATH+1> szTempFileNameA(ctf.TempFileName());
  3337. ATLTRY(pFile = new CHttpRequestFile(strParamName, strFileName, szTempFileNameA, strContentType, nFileSize));
  3338. if (!pFile)
  3339. return FALSE;
  3340. Files.SetAt(szTempFileNameA, pFile);
  3341. pQueryParams->SetAt(strParamName, szTempFileNameA);
  3342. continue;
  3343. }
  3344. // move to the actual uploaded data
  3345. if (!MoveToData())
  3346. {
  3347. ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
  3348. return FALSE;
  3349. }
  3350. if (!ReadUntilBoundary(&strData))
  3351. {
  3352. return FALSE;
  3353. }
  3354. if ((dwFlags & ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS) && strData.GetLength() == 0)
  3355. continue;
  3356. pQueryParams->SetAt(strParamName, strData);
  3357. }
  3358. return TRUE;
  3359. }
  3360. _ATLCATCHALL()
  3361. {
  3362. return FALSE;
  3363. }
  3364. }
  3365. protected:
  3366. // case insensitive substring search -- does not handle multibyte characters
  3367. // allows searching up to a maximum point in a string
  3368. inline char AtlCharLower(char ch) throw()
  3369. {
  3370. if (ch > 64 && ch < 91)
  3371. {
  3372. return ch+32;
  3373. }
  3374. return ch;
  3375. }
  3376. inline char * _stristrex (const char * str1, const char * str2, const char * str1End) throw()
  3377. {
  3378. char *cp = (char *) str1;
  3379. char *s1, *s2;
  3380. if ( !*str2 )
  3381. return((char *)str1);
  3382. while (cp != str1End)
  3383. {
  3384. s1 = cp;
  3385. s2 = (char *) str2;
  3386. while ( s1 != str1End && *s2 && !(AtlCharLower(*s1)-AtlCharLower(*s2)) )
  3387. {
  3388. s1++, s2++;
  3389. }
  3390. if (s1 == str1End)
  3391. {
  3392. return (NULL);
  3393. }
  3394. if (!*s2)
  3395. {
  3396. return (cp);
  3397. }
  3398. cp++;
  3399. }
  3400. return(NULL);
  3401. }
  3402. inline char * _strstrex (const char * str1, const char * str2, const char * str1End) throw()
  3403. {
  3404. char *cp = (char *) str1;
  3405. char *s1, *s2;
  3406. if ( !*str2 )
  3407. return((char *)str1);
  3408. while (cp != str1End)
  3409. {
  3410. s1 = cp;
  3411. s2 = (char *) str2;
  3412. while ( s1 != str1End && *s2 && !((*s1)-(*s2)) )
  3413. {
  3414. s1++, s2++;
  3415. }
  3416. if (s1 == str1End)
  3417. {
  3418. return (NULL);
  3419. }
  3420. if (!*s2)
  3421. {
  3422. return (cp);
  3423. }
  3424. cp++;
  3425. }
  3426. return(NULL);
  3427. }
  3428. ATL_NOINLINE BOOL InitializeParser() throw()
  3429. {
  3430. DWORD dwBytesTotal = m_spServerContext->GetTotalBytes();
  3431. // if greater than bytes available, allocate necessary space
  3432. if (dwBytesTotal > m_spServerContext->GetAvailableBytes())
  3433. {
  3434. ATLTRYALLOC(m_pStart = (LPSTR) malloc(dwBytesTotal));
  3435. if (!m_pStart)
  3436. {
  3437. return FALSE;
  3438. }
  3439. m_pCurrent = m_pStart;
  3440. DWORD dwLen = dwBytesTotal;
  3441. if (!ReadClientData(m_spServerContext, m_pStart, &dwLen, 0) || dwLen != dwBytesTotal)
  3442. {
  3443. return FALSE;
  3444. }
  3445. }
  3446. else
  3447. {
  3448. m_pStart = (LPSTR) m_spServerContext->GetAvailableData();
  3449. }
  3450. m_pCurrent = m_pStart;
  3451. m_pEnd = m_pCurrent + dwBytesTotal;
  3452. //get the boundary
  3453. LPCSTR pszContentType = m_spServerContext ? m_spServerContext->GetContentType() : NULL;
  3454. ATLASSERT(pszContentType != NULL);
  3455. LPCSTR pszTmp = strstr(pszContentType, "boundary=");
  3456. if (!pszTmp)
  3457. {
  3458. ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
  3459. return FALSE;
  3460. }
  3461. pszTmp += sizeof("boundary=")-1;
  3462. BOOL bInQuote = FALSE;
  3463. if (*pszTmp == '\"')
  3464. {
  3465. bInQuote = TRUE;
  3466. pszTmp++;
  3467. }
  3468. LPSTR pszMimeBoundary = m_szBoundary;
  3469. *pszMimeBoundary++ = '-';
  3470. *pszMimeBoundary++ = '-';
  3471. m_dwBoundaryLen = 2;
  3472. while (*pszTmp && (bInQuote || IsStandardBoundaryChar(*pszTmp)))
  3473. {
  3474. if (m_dwBoundaryLen >= MAX_MIME_BOUNDARY_LEN)
  3475. {
  3476. ATLTRACE(atlTraceISAPI, 0, _T("Malformed MIME boundary"));
  3477. return FALSE;
  3478. }
  3479. if (*pszTmp == '\r' || *pszTmp == '\n')
  3480. {
  3481. if (bInQuote)
  3482. {
  3483. pszTmp++;
  3484. continue;
  3485. }
  3486. break;
  3487. }
  3488. if (bInQuote && *pszTmp == '"')
  3489. {
  3490. break;
  3491. }
  3492. *pszMimeBoundary++ = *pszTmp++;
  3493. m_dwBoundaryLen++;
  3494. }
  3495. *pszMimeBoundary = '\0';
  3496. m_szSearchBoundary[0] = '\r';
  3497. m_szSearchBoundary[1] = '\n';
  3498. strcpy(m_szSearchBoundary+2, m_szBoundary);
  3499. return TRUE;
  3500. }
  3501. inline BOOL MoveToData() throw()
  3502. {
  3503. LPSTR szEnd = _strstrex(m_pCurrent, "\r\n\r\n", m_pEnd);
  3504. if (!szEnd)
  3505. {
  3506. return FALSE;
  3507. }
  3508. m_pCurrent = szEnd+4;
  3509. if (m_pCurrent >= m_pEnd)
  3510. {
  3511. return FALSE;
  3512. }
  3513. return TRUE;
  3514. }
  3515. inline BOOL GetMimeData(CStringA &str, LPCSTR szField, DWORD dwFieldLen, LPBOOL pbFound, BOOL bIgnoreCase = FALSE) throw()
  3516. {
  3517. _ATLTRY
  3518. {
  3519. ATLASSERT( szField != NULL );
  3520. ATLASSERT( pbFound != NULL );
  3521. *pbFound = FALSE;
  3522. LPSTR szEnd = _strstrex(m_pCurrent, "\r\n\r\n", m_pEnd);
  3523. if (!szEnd)
  3524. {
  3525. return FALSE;
  3526. }
  3527. LPSTR szDataStart = NULL;
  3528. if (!bIgnoreCase)
  3529. {
  3530. szDataStart = _strstrex(m_pCurrent, szField, szEnd);
  3531. }
  3532. else
  3533. {
  3534. szDataStart = _stristrex(m_pCurrent, szField, szEnd);
  3535. }
  3536. if (szDataStart)
  3537. {
  3538. szDataStart+= dwFieldLen;
  3539. if (szDataStart >= m_pEnd)
  3540. {
  3541. return FALSE;
  3542. }
  3543. BOOL bInQuote = FALSE;
  3544. if (*szDataStart == '\"')
  3545. {
  3546. bInQuote = TRUE;
  3547. szDataStart++;
  3548. }
  3549. LPSTR szDataEnd = szDataStart;
  3550. while (!bInQuote && (szDataEnd < m_pEnd) && (*szDataEnd == ' ' || *szDataEnd == '\t'))
  3551. {
  3552. szDataEnd++;
  3553. }
  3554. if (szDataEnd >= m_pEnd)
  3555. {
  3556. return FALSE;
  3557. }
  3558. while (szDataEnd < m_pEnd)
  3559. {
  3560. if (!IsValidTokenChar(*szDataEnd))
  3561. {
  3562. if (*szDataEnd == '\"' || !bInQuote)
  3563. {
  3564. break;
  3565. }
  3566. }
  3567. szDataEnd++;
  3568. }
  3569. if (szDataEnd >= m_pEnd)
  3570. {
  3571. return FALSE;
  3572. }
  3573. LPSTR szOut = str.GetBuffer((int)(szDataEnd-szDataStart)+1);
  3574. if (!szOut)
  3575. {
  3576. str.ReleaseBuffer();
  3577. return FALSE;
  3578. }
  3579. memcpy(szOut, szDataStart, szDataEnd-szDataStart);
  3580. szOut[szDataEnd-szDataStart] = '\0';
  3581. str.ReleaseBuffer((int)(szDataEnd-szDataStart));
  3582. *pbFound = TRUE;
  3583. }
  3584. return TRUE;
  3585. }
  3586. _ATLCATCHALL()
  3587. {
  3588. return FALSE;
  3589. }
  3590. }
  3591. ATL_NOINLINE BOOL ReadUntilBoundary(CStringA* pStrData=NULL, CAtlTemporaryFile* pCtf=NULL) throw()
  3592. {
  3593. _ATLTRY
  3594. {
  3595. LPSTR szBoundaryStart = m_pCurrent;
  3596. LPSTR szBoundaryEnd = NULL;
  3597. do
  3598. {
  3599. szBoundaryStart = _strstrex(szBoundaryStart, m_szBoundary, m_pEnd);
  3600. if (szBoundaryStart != m_pStart)
  3601. {
  3602. if ((szBoundaryStart-m_pStart) >= 2)
  3603. {
  3604. if (*(szBoundaryStart-1) != 0x0a && *(szBoundaryStart-2) != 0x0d)
  3605. continue;
  3606. }
  3607. else
  3608. {
  3609. return FALSE;
  3610. }
  3611. }
  3612. szBoundaryEnd = szBoundaryStart+m_dwBoundaryLen;
  3613. if (szBoundaryEnd+2 >= m_pEnd)
  3614. {
  3615. return FALSE;
  3616. }
  3617. if (szBoundaryEnd[0] == '\r' && szBoundaryEnd[1] == '\n')
  3618. {
  3619. break;
  3620. }
  3621. if (szBoundaryEnd[0] == '-' && szBoundaryEnd[1] == '-')
  3622. {
  3623. m_bFinished = TRUE;
  3624. break;
  3625. }
  3626. } while (szBoundaryStart);
  3627. if (!szBoundaryStart)
  3628. {
  3629. return FALSE;
  3630. }
  3631. szBoundaryStart-= 2;
  3632. if (pStrData)
  3633. {
  3634. LPSTR szData = pStrData->GetBuffer((int)(szBoundaryStart-m_pCurrent)+1);
  3635. if (!szData)
  3636. {
  3637. pStrData->ReleaseBuffer();
  3638. return FALSE;
  3639. }
  3640. memcpy(szData, m_pCurrent, (int)(szBoundaryStart-m_pCurrent));
  3641. szData[szBoundaryStart-m_pCurrent] = '\0';
  3642. pStrData->ReleaseBuffer((int)(szBoundaryStart-m_pCurrent));
  3643. }
  3644. if (pCtf)
  3645. {
  3646. if (FAILED(pCtf->Write(m_pCurrent, (DWORD)(szBoundaryStart-m_pCurrent))))
  3647. {
  3648. return FALSE;
  3649. }
  3650. }
  3651. if (!m_bFinished)
  3652. {
  3653. m_pCurrent = szBoundaryEnd+2;
  3654. if (m_pCurrent >= m_pEnd)
  3655. {
  3656. return FALSE;
  3657. }
  3658. }
  3659. return TRUE;
  3660. }
  3661. _ATLCATCHALL()
  3662. {
  3663. return FALSE;
  3664. }
  3665. }
  3666. static inline BOOL IsStandardBoundaryChar(CHAR ch) throw()
  3667. {
  3668. if ( (ch >= 'A' && ch <= 'Z') ||
  3669. (ch >= 'a' && ch <= 'z') ||
  3670. (ch >= '0' && ch <= '9') ||
  3671. (ch == '\'') ||
  3672. (ch == '+') ||
  3673. (ch == '_') ||
  3674. (ch == '-') ||
  3675. (ch == '=') ||
  3676. (ch == '?') )
  3677. {
  3678. return TRUE;
  3679. }
  3680. return FALSE;
  3681. }
  3682. inline IsValidTokenChar(CHAR ch) throw()
  3683. {
  3684. return ( (ch != 0) && (ch != 0xd) && (ch != 0xa) && (ch != ' ') && (ch != '\"') );
  3685. }
  3686. private:
  3687. // Prevents copying.
  3688. CMultiPartFormParser(const CMultiPartFormParser& /*that*/) throw()
  3689. {
  3690. ATLASSERT(FALSE);
  3691. }
  3692. const CMultiPartFormParser& operator=(const CMultiPartFormParser& /*that*/) throw()
  3693. {
  3694. ATLASSERT(FALSE);
  3695. return (*this);
  3696. }
  3697. }; // class CMultiPartFormParser
  3698. // 48K max form size
  3699. #ifndef DEFAULT_MAX_FORM_SIZE
  3700. #define DEFAULT_MAX_FORM_SIZE 49152
  3701. #endif
  3702. // This class provides access to the information contained in an HTTP request submitted to a web server.
  3703. //
  3704. // CHttpRequest provides access to the query string parameters, form fields, cookies, and files
  3705. // that make up an HTTP request, as well as many other important properties of the request.
  3706. class CHttpRequest : public IHttpRequestLookup
  3707. {
  3708. protected:
  3709. // Implementation: Array used to map an HTTP request method (for example, "GET" or "POST")
  3710. // from a string to a numeric constant from the HTTP_METHOD enum (HTTP_METHOD_GET or HTTP_METHOD_HEAD).
  3711. static LPCSTR m_szMethodStrings[];
  3712. // Implementation: The server context.
  3713. CComPtr<IHttpServerContext> m_spServerContext;
  3714. // Implementation: The number of bytes read from the body of the request.
  3715. DWORD m_dwBytesRead;
  3716. // Implementation: TRUE if the request method was POST and the encoding was
  3717. // multipart/form-data, FALSE otherwise.
  3718. BOOL m_bMultiPart;
  3719. // Implementation: Constructor function used to reinitialize all data members.
  3720. void Construct() throw()
  3721. {
  3722. m_spServerContext.Release();
  3723. m_bMultiPart = FALSE;
  3724. m_dwBytesRead = 0;
  3725. if (m_pFormVars != &m_QueryParams)
  3726. delete m_pFormVars;
  3727. m_pFormVars = NULL;
  3728. m_pFormVars = &m_QueryParams;
  3729. m_QueryParams.RemoveAll();
  3730. m_QueryParams.SetShared(false);
  3731. ClearFilesAndCookies();
  3732. }
  3733. void ClearFilesAndCookies() throw()
  3734. {
  3735. m_Files.RemoveAll();
  3736. m_Files.SetShared(false);
  3737. m_requestCookies.RemoveAll();
  3738. m_requestCookies.SetShared(false);
  3739. }
  3740. public:
  3741. // Implementation: The collection of query parameters (name-value pairs) obtained from the query string.
  3742. CHttpRequestParams m_QueryParams;
  3743. // Implementation: The collection of form fields (name-value pairs).
  3744. // The elements of this collection are obtained from the query string for a GET request,
  3745. // or from the body of the request for a POST request.
  3746. CHttpRequestParams *m_pFormVars;
  3747. // The array of CHttpRequestFiles obtained from the current request.
  3748. // See CHttpRequest::Initialize and CMultiPartFormParser::GetMultiPartData for more information.
  3749. typedef CHttpPtrMap<CStringA, IHttpFile*, CStringElementTraits<CStringA> > FileMap;
  3750. FileMap m_Files;
  3751. // Implementation: The array of cookies obtained from the current request.
  3752. typedef CHttpMap<CStringA, CCookie, CStringElementTraits<CStringA> > CookieMap;
  3753. CookieMap m_requestCookies;
  3754. // Numeric constants for the HTTP request methods (such as GET and POST).
  3755. enum HTTP_METHOD
  3756. {
  3757. HTTP_METHOD_UNKNOWN=-1,
  3758. HTTP_METHOD_GET,
  3759. HTTP_METHOD_POST,
  3760. HTTP_METHOD_HEAD,
  3761. HTTP_METHOD_DELETE,
  3762. HTTP_METHOD_LINK,
  3763. HTTP_METHOD_UNLINK,
  3764. HTTP_METHOD_DEBUG // Debugging support for VS7
  3765. };
  3766. // The collection of query parameters (name-value pairs) obtained from the query string.
  3767. // A read-only property.
  3768. __declspec(property(get=GetQueryParams)) const CHttpRequestParams& QueryParams;
  3769. // Returns a reference to the collection of query parameters(name-value pairs)
  3770. // obtained from the query string.
  3771. const CHttpRequestParams& GetQueryParams() const throw()
  3772. {
  3773. return m_QueryParams;
  3774. }
  3775. // The collection of form fields (name-value pairs).
  3776. // The elements of this collection are obtained from the query string for a GET request,
  3777. // or from the body of the request for a POST request.
  3778. // A read-only property.
  3779. __declspec(property(get=GetFormVars)) const CHttpRequestParams& FormVars;
  3780. // Returns a reference to the collection of form fields (name-value pairs)
  3781. // obtained from the query string for a GET request,
  3782. // or from the body of the request for a POST request.
  3783. const CHttpRequestParams& GetFormVars() const throw()
  3784. {
  3785. return *m_pFormVars;
  3786. }
  3787. // The default constructor.
  3788. CHttpRequest() throw()
  3789. :m_pFormVars(NULL)
  3790. {
  3791. Construct();
  3792. }
  3793. // Implementation: The destructor.
  3794. ~CHttpRequest() throw()
  3795. {
  3796. DeleteFiles();
  3797. ClearFilesAndCookies();
  3798. if (m_pFormVars != &m_QueryParams)
  3799. {
  3800. delete m_pFormVars;
  3801. m_pFormVars = NULL;
  3802. }
  3803. }
  3804. // Constructs and initializes the object.
  3805. CHttpRequest(
  3806. IHttpServerContext *pServerContext,
  3807. DWORD dwMaxFormSize=DEFAULT_MAX_FORM_SIZE,
  3808. DWORD dwFlags=ATL_FORM_FLAG_NONE) throw(...)
  3809. :m_pFormVars(NULL)
  3810. {
  3811. Construct();
  3812. if (!Initialize(pServerContext, dwMaxFormSize, dwFlags))
  3813. AtlThrow(E_FAIL);
  3814. }
  3815. CHttpRequest(IHttpRequestLookup *pRequestLookup) throw(...)
  3816. :m_pFormVars(NULL)
  3817. {
  3818. if (!Initialize(pRequestLookup)) // Calls Construct for you
  3819. AtlThrow(E_FAIL);
  3820. }
  3821. //=========================================================================================
  3822. // BEGIN IHttpRequestLoookup interface
  3823. //=========================================================================================
  3824. POSITION GetFirstQueryParam(LPCSTR *ppszName, LPCSTR *ppszValue) const throw()
  3825. {
  3826. ATLASSERT(ppszName != NULL);
  3827. ATLASSERT(ppszValue != NULL);
  3828. POSITION pos = m_QueryParams.GetStartPosition();
  3829. if (pos != NULL)
  3830. {
  3831. *ppszName = m_QueryParams.GetKeyAt(pos);
  3832. *ppszValue = m_QueryParams.GetValueAt(pos);
  3833. }
  3834. return pos;
  3835. }
  3836. POSITION GetNextQueryParam(POSITION pos, LPCSTR *ppszName, LPCSTR *ppszValue) const throw()
  3837. {
  3838. ATLASSERT(pos != NULL);
  3839. ATLASSERT(ppszName != NULL);
  3840. ATLASSERT(ppszValue != NULL);
  3841. POSITION posNext(pos);
  3842. m_QueryParams.GetNext(posNext);
  3843. if (posNext != NULL)
  3844. {
  3845. *ppszName = m_QueryParams.GetKeyAt(posNext);
  3846. *ppszValue = m_QueryParams.GetValueAt(posNext);
  3847. }
  3848. return posNext;
  3849. }
  3850. POSITION GetFirstFormVar(LPCSTR *ppszName, LPCSTR *ppszValue) const throw()
  3851. {
  3852. ATLASSERT(ppszName != NULL);
  3853. ATLASSERT(ppszValue != NULL);
  3854. // if no form vars and just pointing to the query params,
  3855. // then return NULL
  3856. if (m_pFormVars == &m_QueryParams)
  3857. return NULL;
  3858. POSITION pos = m_pFormVars->GetStartPosition();
  3859. if (pos != NULL)
  3860. {
  3861. *ppszName = m_pFormVars->GetKeyAt(pos);
  3862. *ppszValue = m_pFormVars->GetValueAt(pos);
  3863. }
  3864. return pos;
  3865. }
  3866. POSITION GetNextFormVar(POSITION pos, LPCSTR *ppszName, LPCSTR *ppszValue) const throw()
  3867. {
  3868. ATLASSERT(pos != NULL);
  3869. ATLASSERT(ppszName != NULL);
  3870. ATLASSERT(ppszValue != NULL);
  3871. POSITION posNext(pos);
  3872. m_pFormVars->GetNext(posNext);
  3873. if (posNext != NULL)
  3874. {
  3875. *ppszName = m_pFormVars->GetKeyAt(posNext);
  3876. *ppszValue = m_pFormVars->GetValueAt(posNext);
  3877. }
  3878. return posNext;
  3879. }
  3880. POSITION GetFirstFile(LPCSTR *ppszName, IHttpFile **ppFile) const throw()
  3881. {
  3882. ATLASSERT(ppszName != NULL);
  3883. ATLASSERT(ppFile != NULL);
  3884. POSITION pos = m_Files.GetStartPosition();
  3885. if (pos != NULL)
  3886. {
  3887. *ppszName = m_Files.GetKeyAt(pos);
  3888. *ppFile = m_Files.GetValueAt(pos);
  3889. }
  3890. return pos;
  3891. }
  3892. POSITION GetNextFile(POSITION pos, LPCSTR *ppszName, IHttpFile **ppFile) const throw()
  3893. {
  3894. ATLASSERT(pos != NULL);
  3895. ATLASSERT(ppszName != NULL);
  3896. ATLASSERT(ppFile != NULL);
  3897. POSITION posNext(pos);
  3898. m_Files.GetNext(posNext);
  3899. if (posNext != NULL)
  3900. {
  3901. *ppszName = m_Files.GetKeyAt(posNext);
  3902. *ppFile = m_Files.GetValueAt(posNext);
  3903. }
  3904. return posNext;
  3905. }
  3906. POSITION GetFirstCookie(LPCSTR *ppszName, const CCookie **ppCookie) throw()
  3907. {
  3908. ATLASSERT(ppszName != NULL);
  3909. ATLASSERT(ppCookie != NULL);
  3910. POSITION pos = NULL;
  3911. if (GetRequestCookies())
  3912. {
  3913. pos = m_requestCookies.GetStartPosition();
  3914. if (pos != NULL)
  3915. {
  3916. *ppszName = m_requestCookies.GetKeyAt(pos);
  3917. *ppCookie = &(m_requestCookies.GetValueAt(pos));
  3918. }
  3919. }
  3920. return pos;
  3921. }
  3922. POSITION GetNextCookie(POSITION pos, LPCSTR *ppszName, const CCookie **ppCookie) throw()
  3923. {
  3924. ATLASSERT(pos != NULL);
  3925. ATLASSERT(ppszName != NULL);
  3926. ATLASSERT(ppCookie != NULL);
  3927. POSITION posNext(pos);
  3928. m_requestCookies.GetNext(posNext);
  3929. if (posNext != NULL)
  3930. {
  3931. *ppszName = m_requestCookies.GetKeyAt(posNext);
  3932. *ppCookie = &(m_requestCookies.GetValueAt(posNext));
  3933. }
  3934. return posNext;
  3935. }
  3936. // Returns a pointer to the IHttpServerContext interface for the current request.
  3937. HRESULT GetServerContext(IHttpServerContext ** ppOut) throw()
  3938. {
  3939. return m_spServerContext.CopyTo(ppOut);
  3940. }
  3941. //=========================================================================================
  3942. // END IHttpRequestLookup interface
  3943. //=========================================================================================
  3944. void SetServerContext(IHttpServerContext *pServerContext) throw()
  3945. {
  3946. m_spServerContext = pServerContext;
  3947. }
  3948. BOOL Initialize(IHttpRequestLookup *pRequestLookup) throw()
  3949. {
  3950. _ATLTRY
  3951. {
  3952. ATLASSERT(pRequestLookup != NULL);
  3953. // if there's no pRequestLookup, just return
  3954. if (!pRequestLookup)
  3955. return TRUE;
  3956. Construct();
  3957. HRESULT hr = pRequestLookup->GetServerContext(&m_spServerContext);
  3958. if (FAILED(hr))
  3959. return FALSE;
  3960. LPCSTR szName(NULL);
  3961. LPCSTR szValue(NULL);
  3962. // Initialize query params from the IHttpRequestLookup*
  3963. POSITION pos(pRequestLookup->GetFirstQueryParam(&szName, &szValue));
  3964. while (pos != NULL)
  3965. {
  3966. m_QueryParams.SetAt(szName, szValue);
  3967. pos = pRequestLookup->GetNextQueryParam(pos, &szName, &szValue);
  3968. }
  3969. m_QueryParams.SetShared(true);
  3970. // Initialize the form vars from the IHttpRequestLookup*
  3971. pos = pRequestLookup->GetFirstFormVar(&szName, &szValue);
  3972. if (pos)
  3973. {
  3974. m_pFormVars = NULL;
  3975. ATLTRY(m_pFormVars = new CHttpRequestParams);
  3976. if (!m_pFormVars)
  3977. return FALSE;
  3978. while (pos != NULL)
  3979. {
  3980. m_pFormVars->SetAt(szName, szValue);
  3981. pos = pRequestLookup->GetNextFormVar(pos, &szName, &szValue);
  3982. }
  3983. m_pFormVars->SetShared(true);
  3984. }
  3985. else
  3986. {
  3987. m_pFormVars = &m_QueryParams;
  3988. }
  3989. // Initialize the files from the IHttpRequestLookup*
  3990. IHttpFile *pFile(NULL);
  3991. pos = pRequestLookup->GetFirstFile(&szName, &pFile);
  3992. while (pos != NULL)
  3993. {
  3994. m_Files.SetAt(szName, pFile);
  3995. pos = pRequestLookup->GetNextFile(pos, &szName, &pFile);
  3996. }
  3997. m_Files.SetShared(true);
  3998. // Initialzie the cookies form the IHttpRequestLookup*
  3999. BOOL bRet = FALSE;
  4000. CStringA strCookies;
  4001. bRet = GetCookies(strCookies);
  4002. if (bRet)
  4003. {
  4004. bRet = Parse((LPSTR)(LPCSTR)strCookies);
  4005. }
  4006. m_requestCookies.SetShared(false);
  4007. return bRet;
  4008. } // _ATLTRY
  4009. _ATLCATCHALL()
  4010. {
  4011. }
  4012. return FALSE;
  4013. }
  4014. // Call this function to initialize the object with information about the current request.
  4015. //
  4016. // Returns TRUE on success, FALSE on failure.
  4017. //
  4018. // Call Initialize directly or via the appropriate constructor before using the methods and
  4019. // properties of the request object.
  4020. //
  4021. // Initialize does the following:
  4022. //
  4023. // Parses and decodes the query string into a collection of name-value pairs.
  4024. // This collection is accessible via the GetQueryParams method or the QueryParams property.
  4025. //
  4026. // Sets m_bMultiPart to TRUE if the request is a POST request with multipart/form-data encoding.
  4027. //
  4028. // Parses the body of a POST request if the size of the request data is less than or equal to dwMaxFormSize.
  4029. // The body of the request will consist of simple form fields and may also contain files if the request is encoded as multipart/form-data.
  4030. // In that case, the dwFlags parameter is passed to CMultiPartFormParser::GetMultiPartData to control the creation of the files.
  4031. // The collection of form fields is accessible via the GetFormVars method or the FormVars property.
  4032. // The collection of files is accessible via the m_Files member.
  4033. //
  4034. // Note that Initialize does not parse the cookies associated with a request.
  4035. // Cookies are not processed until an attempt is made to access a cookie in the collection.
  4036. BOOL Initialize(
  4037. IHttpServerContext *pServerContext,
  4038. DWORD dwMaxFormSize=DEFAULT_MAX_FORM_SIZE,
  4039. DWORD dwFlags=ATL_FORM_FLAG_NONE) throw()
  4040. {
  4041. _ATLTRY
  4042. {
  4043. ATLASSERT(pServerContext != NULL);
  4044. if (!pServerContext)
  4045. return FALSE;
  4046. m_spServerContext = pServerContext;
  4047. HTTP_METHOD httpMethod = GetMethod();
  4048. // Parse the query string.
  4049. CHAR szQueryString[ATL_URL_MAX_URL_LENGTH];
  4050. strcpy(szQueryString, GetQueryString());
  4051. if (!m_QueryParams.Parse(szQueryString))
  4052. return FALSE;
  4053. if (m_QueryParams.IsShared())
  4054. return TRUE;
  4055. // If this is a GET request, the collection of form fields
  4056. // is the same as the collection of query parameters.
  4057. if (httpMethod == HTTP_METHOD_GET)
  4058. m_pFormVars = &m_QueryParams;
  4059. else if (httpMethod == HTTP_METHOD_POST)
  4060. {
  4061. LPCSTR szContentType = GetContentType();
  4062. if (!szContentType)
  4063. return FALSE;
  4064. // Don't parse the form data if the size is bigger than the maximum specified.
  4065. if (m_spServerContext->GetTotalBytes() > dwMaxFormSize)
  4066. {
  4067. if (memcmp(szContentType, "multipart/form-data", 19) == 0)
  4068. m_bMultiPart = TRUE;
  4069. m_dwBytesRead = 0;
  4070. // REVIEW : We have to assume the developer knows what they're doing to
  4071. // some extent here.
  4072. return TRUE;
  4073. }
  4074. // If POSTed data is urlencoded, call InitFromPost.
  4075. if (memcmp(szContentType, "application/x-www-form-urlencoded", 33) == 0 && !m_pFormVars->IsShared())
  4076. return InitFromPost();
  4077. // If POSTed data is encoded as multipart/form-data, use CMultiPartFormParser.
  4078. if (memcmp(szContentType, "multipart/form-data", 19) == 0 && !m_pFormVars->IsShared())
  4079. {
  4080. if (m_pFormVars != &m_QueryParams)
  4081. delete m_pFormVars;
  4082. m_pFormVars = NULL;
  4083. CMultiPartFormParser FormParser(m_spServerContext);
  4084. ATLTRY(m_pFormVars = new CHttpRequestParams);
  4085. if (!m_pFormVars)
  4086. return FALSE;
  4087. BOOL bRet = FormParser.GetMultiPartData(m_Files, m_pFormVars, dwFlags);
  4088. return bRet;
  4089. }
  4090. // else initialize m_dwBytesRead for ReadData
  4091. m_dwBytesRead = 0;
  4092. }
  4093. return TRUE;
  4094. }
  4095. _ATLCATCHALL()
  4096. {
  4097. }
  4098. return FALSE;
  4099. }
  4100. // Implementation: Call this function to initialize the collection of form fields
  4101. // from the body of an application/x-www-form-urlencoded POST request.
  4102. ATL_NOINLINE BOOL InitFromPost() throw()
  4103. {
  4104. _ATLTRY
  4105. {
  4106. ATLASSERT(m_spServerContext != NULL);
  4107. // create our m_pFormVars
  4108. if (m_pFormVars == NULL || m_pFormVars == &m_QueryParams)
  4109. {
  4110. ATLTRY(m_pFormVars = new CHttpRequestParams);
  4111. if (m_pFormVars == NULL)
  4112. {
  4113. return FALSE;
  4114. }
  4115. }
  4116. // read the form data into a buffer
  4117. DWORD dwBytesTotal = m_spServerContext->GetTotalBytes();
  4118. CAutoVectorPtr<CHAR> szBuff;
  4119. if (!szBuff.Allocate(dwBytesTotal+1))
  4120. {
  4121. return FALSE;
  4122. }
  4123. // first copy the available
  4124. BOOL bRet = ReadClientData(m_spServerContext, szBuff, &dwBytesTotal, 0);
  4125. if (bRet)
  4126. {
  4127. szBuff[dwBytesTotal] = '\0';
  4128. bRet = m_pFormVars->Parse(szBuff);
  4129. }
  4130. return bRet;
  4131. }
  4132. _ATLCATCHALL()
  4133. {
  4134. }
  4135. return FALSE;
  4136. }
  4137. // Call this function to remove the files listed in m_Files from the web server's hard disk.
  4138. // Returns the number of files deleted.
  4139. int DeleteFiles() throw()
  4140. {
  4141. int nDeleted = 0;
  4142. POSITION pos = m_Files.GetStartPosition();
  4143. while (pos != NULL)
  4144. {
  4145. LPCSTR szTempFile = m_Files.GetKeyAt(pos);
  4146. if (szTempFile && DeleteFileA(szTempFile))
  4147. {
  4148. nDeleted++;
  4149. }
  4150. m_Files.GetNext(pos);
  4151. }
  4152. return nDeleted;
  4153. }
  4154. // Read a specified amount of data into pbDest and return the bytes read in pdwLen.
  4155. // Returns TRUE on success, FALSE on failure.
  4156. BOOL ReadData(LPSTR pDest, LPDWORD pdwLen) throw()
  4157. {
  4158. ATLASSERT(pDest);
  4159. ATLASSERT(pdwLen);
  4160. BOOL bRet = ReadClientData(m_spServerContext, pDest, pdwLen, m_dwBytesRead);
  4161. if (bRet)
  4162. m_dwBytesRead+= *pdwLen;
  4163. return bRet;
  4164. }
  4165. // Returns the number of bytes available in the request buffer accessible via GetAvailableData.
  4166. // If GetAvailableBytes returns the same value as GetTotalBytes, the request buffer contains the whole request.
  4167. // Otherwise, the remaining data should be read from the client using ReadData.
  4168. // Equivalent to EXTENSION_CONTROL_BLOCK::cbAvailable.
  4169. DWORD GetAvailableBytes() throw()
  4170. {
  4171. return m_spServerContext ? m_spServerContext->GetAvailableBytes() : 0;
  4172. }
  4173. // Returns the total number of bytes to be received from the client.
  4174. // If this value is 0xffffffff, then there are four gigabytes or more of available data.
  4175. // In this case, ReadData should be called until no more data is returned.
  4176. // Equivalent to the CONTENT_LENGTH server variable or EXTENSION_CONTROL_BLOCK::cbTotalBytes.
  4177. DWORD GetTotalBytes() throw()
  4178. {
  4179. return m_spServerContext ? m_spServerContext->GetTotalBytes() : 0;
  4180. }
  4181. // Returns a pointer to the request buffer containing the data sent by the client.
  4182. // The size of the buffer can be determined by calling GetAvailableBytes.
  4183. // Equivalent to EXTENSION_CONTROL_BLOCK::lpbData
  4184. LPBYTE GetAvailableData() throw()
  4185. {
  4186. return m_spServerContext ? m_spServerContext->GetAvailableData() : NULL;
  4187. }
  4188. // Returns a nul-terminated string that contains the query information.
  4189. // This is the part of the URL that appears after the question mark (?).
  4190. // Equivalent to the QUERY_STRING server variable or EXTENSION_CONTROL_BLOCK::lpszQueryString.
  4191. LPCSTR GetQueryString() throw()
  4192. {
  4193. return m_spServerContext ? m_spServerContext->GetQueryString() : NULL;
  4194. }
  4195. // Returns a nul-terminated string that contains the HTTP method of the current request.
  4196. // Examples of common HTTP methods include "GET" and "POST".
  4197. // Equivalent to the REQUEST_METHOD server variable or EXTENSION_CONTROL_BLOCK::lpszMethod.
  4198. LPCSTR GetMethodString() throw()
  4199. {
  4200. return m_spServerContext ? m_spServerContext->GetRequestMethod() : NULL;
  4201. }
  4202. // Returns an HTTP_METHOD enum value corresponding to the HTTP method of the current request.
  4203. // Returns HTTP_METHOD_UNKNOWN if the request method is not one of the following methods:
  4204. // GET
  4205. // POST
  4206. // HEAD
  4207. // DELETE
  4208. // LINK
  4209. // UNLINK
  4210. HTTP_METHOD GetMethod() throw()
  4211. {
  4212. LPCSTR szMethod = GetMethodString();
  4213. if (!szMethod)
  4214. return HTTP_METHOD_UNKNOWN;
  4215. for (int i=0; m_szMethodStrings[i]; i++)
  4216. {
  4217. if (strcmp(szMethod, m_szMethodStrings[i]) == 0)
  4218. return (HTTP_METHOD) i;
  4219. }
  4220. return HTTP_METHOD_UNKNOWN;
  4221. }
  4222. // Returns a nul-terminated string that contains the content type of the data sent by the client.
  4223. // Equivalent to the CONTENT_TYPE server variable or EXTENSION_CONTROL_BLOCK::lpszContentType.
  4224. LPCSTR GetContentType() throw()
  4225. {
  4226. return m_spServerContext ? m_spServerContext->GetContentType() : NULL;
  4227. }
  4228. // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_USER" server variable.
  4229. //
  4230. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4231. //
  4232. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4233. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4234. BOOL GetAuthUserName(LPSTR szBuff, DWORD *pdwSize) throw()
  4235. {
  4236. return m_spServerContext ? m_spServerContext->GetServerVariable("AUTH_USER", szBuff, pdwSize) :
  4237. FALSE;
  4238. }
  4239. // Call this function to retrieve a nul-terminated string containing the value of the "APPL_PHYSICAL_PATH" server variable.
  4240. //
  4241. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4242. //
  4243. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4244. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4245. BOOL GetPhysicalPath(LPSTR szBuff, DWORD *pdwSize) throw()
  4246. {
  4247. return m_spServerContext ? m_spServerContext->GetServerVariable("APPL_PHYSICAL_PATH", szBuff, pdwSize) :
  4248. FALSE;
  4249. }
  4250. // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_PASSWORD" server variable.
  4251. //
  4252. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4253. //
  4254. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4255. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4256. BOOL GetAuthUserPassword(LPSTR szBuff, DWORD *pdwSize) throw()
  4257. {
  4258. return m_spServerContext ? m_spServerContext->GetServerVariable("AUTH_PASSWORD", szBuff, pdwSize) :
  4259. FALSE;
  4260. }
  4261. // Call this function to retrieve a nul-terminated string containing the value of the "URL" server variable.
  4262. //
  4263. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4264. //
  4265. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4266. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4267. BOOL GetUrl(LPSTR szBuff, DWORD *pdwSize) throw()
  4268. {
  4269. return m_spServerContext ? m_spServerContext->GetServerVariable("URL", szBuff, pdwSize) :
  4270. FALSE;
  4271. }
  4272. // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_HOST" server variable.
  4273. //
  4274. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4275. //
  4276. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4277. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4278. BOOL GetUserHostName(LPSTR szBuff, DWORD *pdwSize) throw()
  4279. {
  4280. return m_spServerContext ? m_spServerContext->GetServerVariable("REMOTE_HOST", szBuff, pdwSize) :
  4281. FALSE;
  4282. }
  4283. // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_ADDR" server variable.
  4284. //
  4285. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4286. //
  4287. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4288. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4289. BOOL GetUserHostAddress(LPSTR szBuff, DWORD *pdwSize) throw()
  4290. {
  4291. return m_spServerContext ? m_spServerContext->GetServerVariable("REMOTE_ADDR", szBuff, pdwSize) :
  4292. FALSE;
  4293. }
  4294. // Call this function to retrieve a nul-terminated string containing the physical path of the script.
  4295. //
  4296. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4297. //
  4298. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4299. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4300. // The script path is the same as GetPathTranslated up to the first .srf or .dll.
  4301. // For example, if GetPathTranslated returns "c:\inetpub\vcisapi\hello.srf\goodmorning",
  4302. // then this function returns "c:\inetpub\vcisapi\hello.srf".
  4303. LPCSTR GetScriptPathTranslated() throw()
  4304. {
  4305. return m_spServerContext ? m_spServerContext->GetScriptPathTranslated() : NULL;
  4306. }
  4307. // Returns a nul-terminated string that contains the physical path of the requested resource on the local server.
  4308. // Equivalent to the PATH_TRANSLATED server variable or EXTENSION_CONTROL_BLOCK::lpszPathTranslated.
  4309. LPCSTR GetPathTranslated() throw()
  4310. {
  4311. return m_spServerContext ? m_spServerContext->GetPathTranslated() : NULL;
  4312. }
  4313. // Returns a nul-terminated string that contains the path of the current request.
  4314. // This is the part of the URL that appears after the server name, but before the query string.
  4315. // Equivalent to the PATH_INFO server variable or EXTENSION_CONTROL_BLOCK::lpszPathInfo.
  4316. LPCSTR GetPathInfo() throw()
  4317. {
  4318. return m_spServerContext ? m_spServerContext->GetPathInfo() : NULL;
  4319. }
  4320. // Call this function to determine whether the current request was authenticated.
  4321. // Returns TRUE if the authentication type is one of the following:
  4322. // BASIC
  4323. // NTLM
  4324. // Negotiate
  4325. // Returns FALSE otherwise.
  4326. BOOL GetAuthenticated() throw(...)
  4327. {
  4328. // check for basic or NTLM authentication
  4329. CStringA strAuthType;
  4330. if (GetAuthenticationType(strAuthType) &&
  4331. (strAuthType == "BASIC" ||
  4332. strAuthType == "NTLM" ||
  4333. strAuthType == "Negotiate"))
  4334. return TRUE;
  4335. return FALSE;
  4336. }
  4337. // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_TYPE" server variable.
  4338. //
  4339. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4340. //
  4341. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4342. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4343. BOOL GetAuthenticationType(LPSTR szBuff, DWORD *pdwSize) throw()
  4344. {
  4345. return m_spServerContext ? m_spServerContext->GetServerVariable("AUTH_TYPE", szBuff, pdwSize) :
  4346. FALSE;
  4347. }
  4348. // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_USER" server variable.
  4349. //
  4350. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4351. //
  4352. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4353. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4354. BOOL GetUserName(LPSTR szBuff, DWORD *pdwSize) throw()
  4355. {
  4356. return m_spServerContext ? m_spServerContext->GetServerVariable("REMOTE_USER", szBuff, pdwSize) :
  4357. FALSE;
  4358. }
  4359. // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_USER_AGENT" server variable.
  4360. //
  4361. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4362. //
  4363. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4364. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4365. BOOL GetUserAgent(LPSTR szBuff, DWORD *pdwSize) throw()
  4366. {
  4367. return m_spServerContext ? m_spServerContext->GetServerVariable("HTTP_USER_AGENT", szBuff, pdwSize) :
  4368. FALSE;
  4369. }
  4370. // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT_LANGUAGE" server variable.
  4371. //
  4372. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4373. //
  4374. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4375. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4376. BOOL GetUserLanguages(LPSTR szBuff, DWORD *pdwSize) throw()
  4377. {
  4378. return m_spServerContext ? m_spServerContext->GetServerVariable("HTTP_ACCEPT_LANGUAGE", szBuff, pdwSize) :
  4379. FALSE;
  4380. }
  4381. // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT" server variable.
  4382. //
  4383. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4384. //
  4385. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4386. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4387. BOOL GetAcceptTypes(LPSTR szBuff, DWORD *pdwSize) throw()
  4388. {
  4389. return m_spServerContext ? m_spServerContext->GetServerVariable("HTTP_ACCEPT", szBuff, pdwSize) :
  4390. FALSE;
  4391. }
  4392. // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT_ENCODING" server variable.
  4393. //
  4394. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4395. //
  4396. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4397. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4398. BOOL GetAcceptEncodings(LPSTR szBuff, DWORD *pdwSize) throw()
  4399. {
  4400. return m_spServerContext ? m_spServerContext->GetServerVariable("HTTP_ACCEPT_ENCODING", szBuff, pdwSize) :
  4401. FALSE;
  4402. }
  4403. // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_REFERER" server variable.
  4404. //
  4405. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4406. //
  4407. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4408. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4409. BOOL GetUrlReferer(LPSTR szBuff, DWORD *pdwSize) throw()
  4410. {
  4411. return m_spServerContext ? m_spServerContext->GetServerVariable("HTTP_REFERER", szBuff, pdwSize) :
  4412. FALSE;
  4413. }
  4414. // Call this function to retrieve a nul-terminated string containing the value of the "SCRIPT_NAME" server variable.
  4415. //
  4416. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4417. //
  4418. // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
  4419. // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
  4420. BOOL GetScriptName(LPSTR szBuff, DWORD *pdwSize) throw()
  4421. {
  4422. return m_spServerContext ? m_spServerContext->GetServerVariable("SCRIPT_NAME", szBuff, pdwSize) :
  4423. FALSE;
  4424. }
  4425. // Fills a buffer with the contents of the HTTP_COOKIE headers sent
  4426. // from the browser.
  4427. BOOL GetCookies(LPSTR szBuf, LPDWORD pdwSize) const throw()
  4428. {
  4429. ATLASSERT(pdwSize != NULL);
  4430. ATLASSERT(szBuf != NULL);
  4431. CStringA strCookie;
  4432. if (GetCookies(strCookie))
  4433. {
  4434. if (pdwSize && *pdwSize > (DWORD)strCookie.GetLength())
  4435. {
  4436. strcpy(szBuf, strCookie);
  4437. *pdwSize = strCookie.GetLength();
  4438. return true;
  4439. }
  4440. }
  4441. return false;
  4442. }
  4443. // Fills a CStringA with the contents of the HTTP_COOKIE headers sent
  4444. // from the browser.
  4445. BOOL GetCookies(CStringA& strBuff) const throw()
  4446. {
  4447. return GetServerVariable("HTTP_COOKIE", strBuff);
  4448. }
  4449. // Call this function to retrieve a reference to the specified cookie.
  4450. // Returns a CCookie reference to the specified cookie or a
  4451. // reference to an empty cookie if the name can not be found.
  4452. ATL_NOINLINE const CCookie& Cookies(LPCSTR szName) throw()
  4453. {
  4454. static CCookie m_EmptyCookie;
  4455. if (GetRequestCookies())
  4456. {
  4457. // p->m_value is a const CCookie&
  4458. CookieMap::CPair *p = m_requestCookies.Lookup(szName);
  4459. if (p)
  4460. {
  4461. return p->m_value;
  4462. }
  4463. }
  4464. return m_EmptyCookie;
  4465. }
  4466. // Call this function to retrieve the session cookie.
  4467. const CCookie& GetSessionCookie() throw()
  4468. {
  4469. return Cookies(SESSION_COOKIE_NAME);
  4470. }
  4471. // Call this function to retrieve the value of the requested server variable in a CStringA object.
  4472. // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
  4473. // Equivalent to EXTENSION_CONTROL_BLOCK::GetServerVariable.
  4474. BOOL GetServerVariable(LPCSTR szVariable, CStringA &str) const throw()
  4475. {
  4476. if (!m_spServerContext)
  4477. return FALSE;
  4478. DWORD dwSize = 0;
  4479. BOOL bRet = FALSE;
  4480. _ATLTRY
  4481. {
  4482. m_spServerContext->GetServerVariable(szVariable, NULL, &dwSize);
  4483. bRet = m_spServerContext->GetServerVariable(szVariable, str.GetBuffer(dwSize), &dwSize);
  4484. if (dwSize > 0)
  4485. dwSize--;
  4486. str.ReleaseBuffer(dwSize);
  4487. }
  4488. _ATLCATCHALL()
  4489. {
  4490. bRet = FALSE;
  4491. }
  4492. return bRet;
  4493. }
  4494. // Call this function to retrieve the value of the "APPL_PHYSICAL_PATH" server variable.
  4495. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4496. BOOL GetPhysicalPath(CStringA &str) throw()
  4497. {
  4498. return GetServerVariable("APPL_PHYSICAL_PATH", str);
  4499. }
  4500. // Call this function to retrieve the value of the "REMOTE_HOST" server variable.
  4501. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4502. BOOL GetUserHostName(CStringA &str) throw()
  4503. {
  4504. return GetServerVariable("REMOTE_HOST", str);
  4505. }
  4506. // Call this function to retrieve the value of the "REMOTE_ADDR" server variable.
  4507. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4508. BOOL GetUserHostAddress(CStringA &str) throw()
  4509. {
  4510. return GetServerVariable("REMOTE_ADDR", str);
  4511. }
  4512. // Call this function to retrieve the value of the "AUTH_TYPE" server variable.
  4513. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4514. BOOL GetAuthenticationType(CStringA &str) throw()
  4515. {
  4516. return GetServerVariable("AUTH_TYPE", str);
  4517. }
  4518. // Call this function to retrieve the value of the "REMOTE_USER" server variable.
  4519. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4520. BOOL GetUserName(CStringA &str) throw()
  4521. {
  4522. return GetServerVariable("REMOTE_USER", str);
  4523. }
  4524. // Call this function to retrieve the value of the "HTTP_USER_AGENT" server variable.
  4525. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4526. BOOL GetUserAgent(CStringA &str) throw()
  4527. {
  4528. return GetServerVariable("HTTP_USER_AGENT", str);
  4529. }
  4530. // Call this function to retrieve the value of the "HTTP_ACCEPT_LANGUAGE" server variable.
  4531. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4532. BOOL GetUserLanguages(CStringA &str) throw()
  4533. {
  4534. return GetServerVariable("HTTP_ACCEPT_LANGUAGE", str);
  4535. }
  4536. // Call this function to retrieve the value of the "AUTH_USER" server variable.
  4537. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4538. BOOL GetAuthUserName(CStringA &str) throw()
  4539. {
  4540. return GetServerVariable("AUTH_USER", str);
  4541. }
  4542. // Call this function to retrieve the value of the "AUTH_PASSWORD" server variable.
  4543. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4544. BOOL GetAuthUserPassword(CStringA &str) throw()
  4545. {
  4546. return GetServerVariable("AUTH_PASSWORD", str);
  4547. }
  4548. // Call this function to retrieve the value of the "URL" server variable.
  4549. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4550. BOOL GetUrl(CStringA &str) throw()
  4551. {
  4552. return GetServerVariable("URL", str);
  4553. }
  4554. // Call this function to retrieve the value of the "HTTP_ACCEPT" server variable.
  4555. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4556. BOOL GetAcceptTypes(CStringA &str) throw()
  4557. {
  4558. return GetServerVariable("HTTP_ACCEPT", str);
  4559. }
  4560. // Call this function to retrieve the value of the "HTTP_ACCEPT_ENCODING" server variable.
  4561. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4562. BOOL GetAcceptEncodings(CStringA& str) throw()
  4563. {
  4564. return GetServerVariable("HTTP_ACCEPT_ENCODING", str);
  4565. }
  4566. // Call this function to retrieve the value of the "HTTP_REFERER" server variable.
  4567. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4568. BOOL GetUrlReferer(CStringA &str) throw()
  4569. {
  4570. return GetServerVariable("HTTP_REFERER", str);
  4571. }
  4572. // Call this function to retrieve the value of the "SCRIPT_NAME" server variable.
  4573. // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
  4574. BOOL GetScriptName(CStringA &str) throw()
  4575. {
  4576. return GetServerVariable("SCRIPT_NAME", str);
  4577. }
  4578. // Implementation: Call this function to populate the collection
  4579. // of CCookie objects with the cookies in the current request.
  4580. // Returns TRUE on success, FALSE on failure.
  4581. BOOL GetRequestCookies() throw()
  4582. {
  4583. BOOL bRet = FALSE;
  4584. if (m_requestCookies.GetCount())
  4585. return TRUE; // we already got the cookies!
  4586. CStringA strCookies;
  4587. if (GetCookies(strCookies))
  4588. {
  4589. bRet = Parse((LPSTR)(LPCSTR)strCookies);
  4590. }
  4591. return bRet;
  4592. }
  4593. // Implementation: Call this function to populate m_requestCookies
  4594. // with a collection of CCookie objects which represents the
  4595. // cookies contained in szCookie header sent from the browser.
  4596. BOOL Parse(LPSTR szCookieIn) throw()
  4597. {
  4598. // The browser only sends back the data for the
  4599. // cookie, separated by ';'. Parse out all of the cookies
  4600. // in the cookie string and create CCookie's out of them which
  4601. // we add to our array of CCookies
  4602. // example 1: Param1=Value1; hello=world&right=wrong&the+direction=west;
  4603. // example 2: hello=world
  4604. if (!szCookieIn)
  4605. return FALSE;
  4606. LPSTR pEnd = szCookieIn;
  4607. LPSTR pStart = szCookieIn;
  4608. CStringA strCookieName;
  4609. _ATLTRY
  4610. {
  4611. while (1)
  4612. {
  4613. if (*pEnd == '\0' || *pEnd == ';')
  4614. {
  4615. CCookie c;
  4616. if (c.Parse(pStart))
  4617. {
  4618. if (c.GetName(strCookieName))
  4619. m_requestCookies.SetAt(strCookieName, c);
  4620. }
  4621. if (*pEnd)
  4622. pStart = pEnd+1;
  4623. }
  4624. if (*pEnd == '\0')
  4625. break;
  4626. pEnd++;
  4627. }
  4628. }
  4629. _ATLCATCHALL()
  4630. {
  4631. return FALSE;
  4632. }
  4633. return TRUE;
  4634. }
  4635. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
  4636. {
  4637. if (!ppv)
  4638. return E_POINTER;
  4639. if (InlineIsEqualGUID(riid, __uuidof(IHttpRequestLookup)))
  4640. {
  4641. *ppv = static_cast<IUnknown*>(static_cast<IHttpRequestLookup*>(this));
  4642. AddRef();
  4643. return S_OK;
  4644. }
  4645. if (InlineIsEqualGUID(riid, __uuidof(IUnknown)))
  4646. {
  4647. *ppv = static_cast<IUnknown*>(this);
  4648. AddRef();
  4649. return S_OK;
  4650. }
  4651. return E_NOINTERFACE;
  4652. }
  4653. ULONG STDMETHODCALLTYPE AddRef() throw()
  4654. {
  4655. return 1;
  4656. }
  4657. ULONG STDMETHODCALLTYPE Release() throw()
  4658. {
  4659. return 1;
  4660. }
  4661. }; // class CHttpRequest
  4662. LPCSTR __declspec(selectany) CHttpRequest::m_szMethodStrings[] = {
  4663. "GET",
  4664. "POST",
  4665. "HEAD",
  4666. "DELETE",
  4667. "LINK",
  4668. "UNLINK",
  4669. "DEBUG", // Debugging support for VS7
  4670. NULL
  4671. };
  4672. // This class provides type conversions via the Write method
  4673. // and overloaded left shift << operator for writing
  4674. // data to the IWriteStream interface. The IWriteStream interface
  4675. // only accepts strings, but this helper class allows you to transparently
  4676. // pass many different types by providing automatic type conversions.
  4677. //
  4678. // Notes on Type Conversions:
  4679. // Numeric types are converted to their decimal representations.
  4680. // Floating point values are output with a precision of 6 decimal places.
  4681. // Currency values are converted according to the locale settings of the current thread.
  4682. class CWriteStreamHelper
  4683. {
  4684. protected:
  4685. // Implementation: The IWriteStream interface.
  4686. IWriteStream *m_pStream;
  4687. public:
  4688. // The default constructor.
  4689. CWriteStreamHelper() throw()
  4690. :m_pStream(NULL)
  4691. {
  4692. }
  4693. // The constructor.
  4694. CWriteStreamHelper(IWriteStream *pStream) throw()
  4695. {
  4696. m_pStream = pStream;
  4697. }
  4698. // Attach a IWriteStream
  4699. IWriteStream *Attach(IWriteStream *pStream) throw()
  4700. {
  4701. IWriteStream *pRetStream = m_pStream;
  4702. m_pStream = pStream;
  4703. return pRetStream;
  4704. }
  4705. // Call this function to write data to the IWriteStream interface managed by this object.
  4706. // Returns TRUE on success, FALSE on failure.
  4707. BOOL Write(LPCSTR szOut) throw()
  4708. {
  4709. if (!szOut)
  4710. return FALSE;
  4711. DWORD dwWritten;
  4712. return SUCCEEDED(m_pStream->WriteStream(szOut, (int) strlen(szOut), &dwWritten));
  4713. }
  4714. // Call this function to write data to the IWriteStream interface managed by this object.
  4715. // Returns TRUE on success, FALSE on failure.
  4716. BOOL Write(int n) throw()
  4717. {
  4718. CHAR szTmp[21];
  4719. _itoa(n, szTmp, 10);
  4720. return Write(szTmp);
  4721. }
  4722. // Call this function to write data to the IWriteStream interface managed by this object.
  4723. // Returns TRUE on success, FALSE on failure.
  4724. BOOL Write(unsigned int u) throw()
  4725. {
  4726. CHAR szTmp[21];
  4727. _itoa((int)u, szTmp, 10);
  4728. return Write(szTmp);
  4729. }
  4730. // Call this function to write data to the IWriteStream interface managed by this object.
  4731. // Returns TRUE on success, FALSE on failure.
  4732. BOOL Write(short int w) throw()
  4733. {
  4734. return Write((int) w);
  4735. }
  4736. // Call this function to write data to the IWriteStream interface managed by this object.
  4737. // Returns TRUE on success, FALSE on failure.
  4738. BOOL Write(long int dw) throw()
  4739. {
  4740. CHAR szTmp[21];
  4741. _ltoa(dw, szTmp, 10);
  4742. return Write(szTmp);
  4743. }
  4744. // Call this function to write data to the IWriteStream interface managed by this object.
  4745. // Returns TRUE on success, FALSE on failure.
  4746. BOOL Write(unsigned long int dw) throw()
  4747. {
  4748. CHAR szTmp[21];
  4749. _ultoa(dw, szTmp, 10);
  4750. return Write(szTmp);
  4751. }
  4752. // Call this function to write data to the IWriteStream interface managed by this object.
  4753. // Returns TRUE on success, FALSE on failure.
  4754. BOOL Write(double d, int nDigitCount=ATL_DEFAULT_PRECISION) throw()
  4755. {
  4756. CHAR szTmp[512];
  4757. int nDec = 0;
  4758. int nSign = 0;
  4759. bool fWriteDec=true;
  4760. strcpy(szTmp, _fcvt(d, nDigitCount, &nDec, &nSign));
  4761. if (nSign != 0)
  4762. m_pStream->WriteStream("-", 1, NULL);
  4763. if (nDec < 0)
  4764. {
  4765. nDec *= -1;
  4766. m_pStream->WriteStream("0.", 2, NULL);
  4767. for (int i=0;i<nDec;i++)
  4768. {
  4769. m_pStream->WriteStream("0", 1, NULL);
  4770. }
  4771. nDec = 0;
  4772. fWriteDec=false;
  4773. }
  4774. char *p = szTmp;
  4775. while (*p)
  4776. {
  4777. // if the decimal lies at the end of the number
  4778. // (no digits to the right of the decimal, we don't
  4779. // print it.
  4780. if (nDec == 0 && fWriteDec)
  4781. m_pStream->WriteStream(".", 1, NULL);
  4782. m_pStream->WriteStream(p, 1, NULL);
  4783. nDec--;
  4784. p++;
  4785. }
  4786. return TRUE;
  4787. }
  4788. // Call this function to write data to the IWriteStream interface managed by this object.
  4789. // Returns TRUE on success, FALSE on failure.
  4790. BOOL Write(__int64 i) throw()
  4791. {
  4792. CHAR szTmp[21];
  4793. _i64toa(i, szTmp, 10);
  4794. return Write(szTmp);
  4795. }
  4796. // Call this function to write data to the IWriteStream interface managed by this object.
  4797. // Returns TRUE on success, FALSE on failure.
  4798. BOOL Write(unsigned __int64 i) throw()
  4799. {
  4800. CHAR szTmp[21];
  4801. _ui64toa(i, szTmp, 10);
  4802. return Write(szTmp);
  4803. }
  4804. // Call this function to write data to the IWriteStream interface managed by this object.
  4805. // Returns TRUE on success, FALSE on failure.
  4806. BOOL Write(CURRENCY c) throw()
  4807. {
  4808. CHAR szDest[256];
  4809. CHAR szNumber[32];
  4810. #if 0
  4811. sprintf(szNumber, "%05I64d", c.int64);
  4812. int nLen = (int) strlen(szNumber);
  4813. #else
  4814. _i64toa(c.int64, szNumber, 10);
  4815. int nLen = (int) strlen(szNumber);
  4816. if (nLen < 5)
  4817. {
  4818. // prepend ascii zeros
  4819. memmove(szNumber+5-nLen, szNumber, nLen+1);
  4820. memset(szNumber, '0', 5-nLen);
  4821. nLen = 5;
  4822. }
  4823. #endif
  4824. memmove(szNumber+nLen-3, szNumber+nLen-4, 5);
  4825. szNumber[nLen-4] = '.';
  4826. int nRet = GetCurrencyFormatA(GetThreadLocale(), 0, szNumber, NULL, szDest, sizeof(szDest));
  4827. if (nRet > 0)
  4828. return Write(szDest);
  4829. ATLASSERT(GetLastError()==ERROR_INSUFFICIENT_BUFFER);
  4830. nRet = GetCurrencyFormatA(GetThreadLocale(), 0, szNumber, NULL, NULL, 0);
  4831. ATLASSERT(nRet > 0);
  4832. if (nRet <= 0)
  4833. return FALSE;
  4834. CAutoVectorPtr<CHAR> szBuffer;
  4835. if (!szBuffer.Allocate(nRet))
  4836. {
  4837. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  4838. return FALSE;
  4839. }
  4840. nRet = GetCurrencyFormatA(GetThreadLocale(), 0, szNumber, NULL, szBuffer, nRet);
  4841. ATLASSERT(nRet > 0);
  4842. BOOL bRet = FALSE;
  4843. if (nRet > 0)
  4844. bRet = Write(szBuffer);
  4845. return bRet;
  4846. }
  4847. // Call this function to write data to the IWriteStream interface managed by this object.
  4848. // Returns TRUE on success, FALSE on failure.
  4849. BOOL Write(LPCWSTR wsz) throw()
  4850. {
  4851. BOOL bRet;
  4852. _ATLTRY
  4853. {
  4854. CW2A sz(wsz);
  4855. if (!sz)
  4856. {
  4857. bRet = FALSE;
  4858. }
  4859. DWORD dwWritten;
  4860. bRet = SUCCEEDED(m_pStream->WriteStream(sz, (int) strlen(sz), &dwWritten));
  4861. }
  4862. _ATLCATCHALL()
  4863. {
  4864. bRet = FALSE;
  4865. }
  4866. return bRet;
  4867. }
  4868. // Use this operator to write data to the IWriteStream interface managed by this object.
  4869. CWriteStreamHelper& operator<<(LPCSTR szStr) throw(...)
  4870. {
  4871. if (!Write(szStr))
  4872. AtlThrow(E_FAIL);
  4873. return *this;
  4874. }
  4875. // Use this operator to write data to the IWriteStream interface managed by this object.
  4876. CWriteStreamHelper& operator<<(LPCWSTR wszStr) throw(...)
  4877. {
  4878. if (!Write(wszStr))
  4879. AtlThrow(E_FAIL);
  4880. return *this;
  4881. }
  4882. // Use this operator to write data to the IWriteStream interface managed by this object.
  4883. CWriteStreamHelper& operator<<(int n) throw(...)
  4884. {
  4885. if (!Write(n))
  4886. AtlThrow(E_FAIL);
  4887. return *this;
  4888. }
  4889. // Use this operator to write data to the IWriteStream interface managed by this object.
  4890. CWriteStreamHelper& operator<<(short int w) throw(...)
  4891. {
  4892. if (!Write(w))
  4893. AtlThrow(E_FAIL);
  4894. return *this;
  4895. }
  4896. // Use this operator to write data to the IWriteStream interface managed by this object.
  4897. CWriteStreamHelper& operator<<(unsigned int u) throw(...)
  4898. {
  4899. if (!Write(u))
  4900. AtlThrow(E_FAIL);
  4901. return *this;
  4902. }
  4903. // Use this operator to write data to the IWriteStream interface managed by this object.
  4904. CWriteStreamHelper& operator<<(long int dw) throw(...)
  4905. {
  4906. if (!Write(dw))
  4907. AtlThrow(E_FAIL);
  4908. return *this;
  4909. }
  4910. // Use this operator to write data to the IWriteStream interface managed by this object.
  4911. CWriteStreamHelper& operator<<(unsigned long int dw) throw(...)
  4912. {
  4913. if (!Write(dw))
  4914. AtlThrow(E_FAIL);
  4915. return *this;
  4916. }
  4917. // Use this operator to write data to the IWriteStream interface managed by this object.
  4918. CWriteStreamHelper& operator<<(double d) throw(...)
  4919. {
  4920. if (!Write(d))
  4921. AtlThrow(E_FAIL);
  4922. return *this;
  4923. }
  4924. // Use this operator to write data to the IWriteStream interface managed by this object.
  4925. CWriteStreamHelper& operator<<(__int64 i) throw(...)
  4926. {
  4927. if (!Write(i))
  4928. AtlThrow(E_FAIL);
  4929. return *this;
  4930. }
  4931. // Use this operator to write data to the IWriteStream interface managed by this object.
  4932. CWriteStreamHelper& operator<<(unsigned __int64 i) throw(...)
  4933. {
  4934. if (!Write(i))
  4935. AtlThrow(E_FAIL);
  4936. return *this;
  4937. }
  4938. // Use this operator to write data to the IWriteStream interface managed by this object.
  4939. CWriteStreamHelper& operator<<(CURRENCY c) throw(...)
  4940. {
  4941. if (!Write(c))
  4942. AtlThrow(E_FAIL);
  4943. return *this;
  4944. }
  4945. };
  4946. // This class represents the response that the web server will send back to the client.
  4947. //
  4948. // CHttpResponse provides friendly functions for building up the headers, cookies, and body of an HTTP response.
  4949. // The class derives from IWriteStream and CWriteStreamHelper, allowing you to call those classes' methods
  4950. // to build up the body of the response. By default, the class improves performance by buffering the response until it is complete before sending it back to the client.
  4951. class CHttpResponse : public IWriteStream, public CWriteStreamHelper
  4952. {
  4953. protected:
  4954. // Implementation: A map of HTTP response headers.
  4955. // The key is the name of the response header.
  4956. // The value is the data for the response header.
  4957. CSimpleMap<CStringA, CStringA> m_headers;
  4958. // Implementation: Determines whether the response is currently being buffered.
  4959. BOOL m_bBufferOutput;
  4960. // Implementation: Determines whether any output should be sent to the client.
  4961. // Intended mainly for HEAD requests, where the client should get the same headers
  4962. // (i.e. Content-Length) as for a GET request
  4963. BOOL m_bSendOutput;
  4964. // Implementation: The limit in bytes of the response buffer.
  4965. // When the limit is reached, the buffer is automatically flushed
  4966. // and data is sent to the client. You can set this to ULONG_MAX
  4967. // to enable full buffering (this is the default, and is required
  4968. // for enabling keep alive connections).
  4969. DWORD m_dwBufferLimit;
  4970. // Implementation: The server context.
  4971. CComPtr<IHttpServerContext> m_spServerContext;
  4972. // Implementation: The HTTP status code for the response.
  4973. int m_nStatusCode;
  4974. // Implementation: Determines whether the response headers have already been sent to the client.
  4975. BOOL m_bHeadersSent;
  4976. // Implementation: Handle of the file being transmitted so it can be closed
  4977. // when the async I/O completes
  4978. HANDLE m_hFile;
  4979. public:
  4980. // Implementation: The buffer used to store the response before
  4981. // the data is sent to the client.
  4982. CAtlIsapiBuffer<> m_strContent;
  4983. // Numeric constants for the HTTP status codes used for redirecting client requests.
  4984. enum HTTP_REDIRECT
  4985. {
  4986. HTTP_REDIRECT_MULTIPLE=300,
  4987. HTTP_REDIRECT_MOVED=301,
  4988. HTTP_REDIRECT_FOUND=302,
  4989. HTTP_REDIRECT_SEE_OTHER=303,
  4990. HTTP_REDIRECT_NOT_MODIFIED=304,
  4991. HTTP_REDIRECT_USE_PROXY=305,
  4992. HTTP_REDIRECT_TEMPORARY_REDIRECT=307
  4993. };
  4994. // The default constructor.
  4995. CHttpResponse() throw()
  4996. {
  4997. m_bBufferOutput = TRUE;
  4998. m_dwBufferLimit = ULONG_MAX;
  4999. m_nStatusCode = 200;
  5000. m_pStream = this;
  5001. m_bHeadersSent = FALSE;
  5002. m_bSendOutput = TRUE;
  5003. m_hFile = INVALID_HANDLE_VALUE;
  5004. }
  5005. // The constructor.
  5006. CHttpResponse(IHttpServerContext *pServerContext) throw()
  5007. {
  5008. m_bBufferOutput = TRUE;
  5009. m_dwBufferLimit = ULONG_MAX;
  5010. m_nStatusCode = 200;
  5011. m_pStream = this;
  5012. m_bHeadersSent = FALSE;
  5013. Initialize(pServerContext);
  5014. m_bSendOutput = TRUE;
  5015. m_hFile = INVALID_HANDLE_VALUE;
  5016. }
  5017. // The destructor flushes the buffer if there is content that
  5018. // hasn't yet been sent to the client.
  5019. ~CHttpResponse() throw()
  5020. {
  5021. // if (m_strContent.GetLength())
  5022. Flush(TRUE);
  5023. if (m_hFile && m_hFile != INVALID_HANDLE_VALUE)
  5024. CloseHandle(m_hFile);
  5025. }
  5026. // Call this function to initialize the response object with a pointer to the server context.
  5027. // Returns TRUE on success, FALSE on failure.
  5028. BOOL Initialize(IHttpServerContext *pServerContext) throw()
  5029. {
  5030. ATLASSERT(pServerContext != NULL);
  5031. if (!pServerContext)
  5032. return FALSE;
  5033. m_spServerContext = pServerContext;
  5034. return TRUE;
  5035. }
  5036. // This is called to initialize the CHttpResponse for a child handler. By default, it
  5037. // assumes the parent will be responsible for sending the headers.
  5038. BOOL Initialize(IHttpRequestLookup *pLookup) throw()
  5039. {
  5040. ATLASSERT(pLookup);
  5041. if (!pLookup)
  5042. return FALSE;
  5043. CComPtr<IHttpServerContext> spContext;
  5044. HRESULT hr = pLookup->GetServerContext(&spContext);
  5045. if (FAILED(hr))
  5046. return FALSE;
  5047. if (!Initialize(spContext))
  5048. return FALSE;
  5049. m_bHeadersSent = TRUE;
  5050. return TRUE;
  5051. }
  5052. // Returns a pointer to the IHttpServerContext interface for the current request.
  5053. HRESULT GetServerContext(IHttpServerContext ** ppOut) throw()
  5054. {
  5055. return m_spServerContext.CopyTo(ppOut);
  5056. }
  5057. // Call this function to set buffering options for the response.
  5058. //
  5059. // This function allows you to turn buffering on or off, and to set a size limit
  5060. // on the amount of data that will be buffered before being sent to the client.
  5061. //
  5062. // When you turn off buffering, the current contents of the buffer will be sent to the client.
  5063. // If you need to clear the buffer without sending the contents to the client, call ClearContent instead.
  5064. //
  5065. // When the size of the buffer is reduced below the current size of the buffered content,
  5066. // the entire buffer is flushed.
  5067. void SetBufferOutput(BOOL bBufferOutput, DWORD dwBufferLimit=ATL_ISAPI_BUFFER_SIZE) throw()
  5068. {
  5069. if (m_bBufferOutput && !bBufferOutput)
  5070. {
  5071. // before turning off buffering, flush
  5072. // the current contents
  5073. Flush();
  5074. }
  5075. SetBufferLimit(dwBufferLimit);
  5076. m_bBufferOutput = bBufferOutput;
  5077. }
  5078. // Call this function to determine whether data written to the response object is being buffered or not.
  5079. // Returns TRUE if output is being buffered, FALSE otherwise.
  5080. BOOL GetBufferOutput() throw()
  5081. {
  5082. return m_bBufferOutput;
  5083. }
  5084. // Call this function to determine whether the response headers have been sent
  5085. // Returns TRUE if headers have been sent, FALSE otherwise.
  5086. BOOL HaveSentHeaders() throw()
  5087. {
  5088. return m_bHeadersSent;
  5089. }
  5090. // Call this function to override the m_bHeadersSent state. This is useful
  5091. // when you want child handlers (e.g. from an include or subhandler) to send the headers
  5092. void HaveSentHeaders(BOOL bSent) throw()
  5093. {
  5094. m_bHeadersSent = bSent;
  5095. }
  5096. // Call this function to set a size limit on the amount of data buffered by the reponse object.
  5097. // When the size of the buffer is reduced below the current size of the buffered content,
  5098. // the entire buffer is flushed.
  5099. // See GetBufferLimit.
  5100. void SetBufferLimit(DWORD dwBufferLimit) throw()
  5101. {
  5102. if (m_bBufferOutput)
  5103. {
  5104. if (m_strContent.GetLength() >= dwBufferLimit)
  5105. {
  5106. // new buffer limit is less than the
  5107. // size currently buffered. So flush
  5108. // the current buffer
  5109. Flush();
  5110. }
  5111. }
  5112. m_dwBufferLimit = dwBufferLimit;
  5113. }
  5114. // Returns the current size limit of the buffer in bytes.
  5115. // See SetBufferLimit.
  5116. DWORD GetBufferLimit() throw()
  5117. {
  5118. return m_dwBufferLimit;
  5119. }
  5120. // Returns the current value of the Content-Type header if present, otherwise returns NULL.
  5121. LPCSTR GetContentType() throw()
  5122. {
  5123. // return the content type from the
  5124. // header collection if any
  5125. _ATLTRY
  5126. {
  5127. CStringA strKey("Content-Type");
  5128. int nIndex = m_headers.FindKey(strKey);
  5129. if (nIndex >= 0)
  5130. return m_headers.GetValueAt(nIndex);
  5131. }
  5132. _ATLCATCHALL()
  5133. {
  5134. }
  5135. return NULL;
  5136. }
  5137. // Call this function to set the Content-Type of the HTTP response.
  5138. // Examples of common MIME content types include text/html and text/plain.
  5139. BOOL SetContentType(LPCSTR szContentType) throw()
  5140. {
  5141. _ATLTRY
  5142. {
  5143. if (!m_headers.SetAt("Content-Type", szContentType))
  5144. return m_headers.Add("Content-Type", szContentType);
  5145. }
  5146. _ATLCATCHALL()
  5147. {
  5148. }
  5149. return TRUE;
  5150. }
  5151. // Call this function to set the HTTP status code of the response.
  5152. // If not set explicitly, the default status code is 200 (OK).
  5153. // See GetStatusCode.
  5154. void SetStatusCode(int nCode) throw()
  5155. {
  5156. m_nStatusCode = nCode;
  5157. }
  5158. // Returns the current HTTP status code of the response.
  5159. // See SetStatusCode.
  5160. int GetStatusCode() throw()
  5161. {
  5162. return m_nStatusCode;
  5163. }
  5164. // Call this function to set the Cache-Control http header of the response.
  5165. // Examples of common Cache-Control header values: public, private, max-age=delta-seconds
  5166. BOOL SetCacheControl(LPCSTR szCacheControl) throw()
  5167. {
  5168. _ATLTRY
  5169. {
  5170. if (!m_headers.SetAt("Cache-Control", szCacheControl))
  5171. return m_headers.Add("Cache-Control", szCacheControl);
  5172. }
  5173. _ATLCATCHALL()
  5174. {
  5175. }
  5176. return FALSE;
  5177. }
  5178. // Call this function to set the Expires HTTP header to the absolute date/time
  5179. // specified in the stExpires parameter
  5180. BOOL SetExpiresAbsolute(const SYSTEMTIME& stExpires) throw()
  5181. {
  5182. _ATLTRY
  5183. {
  5184. CStringA strExpires;
  5185. SystemTimeToHttpDate(stExpires, strExpires);
  5186. if (!m_headers.SetAt("Expires", strExpires))
  5187. return m_headers.Add("Expires", strExpires);
  5188. }
  5189. _ATLCATCHALL()
  5190. {
  5191. }
  5192. return FALSE;
  5193. }
  5194. // Call this function to set the Expires HTTP header to a relative date/time
  5195. // value specified in minutes;
  5196. BOOL SetExpires(long lMinutes) throw()
  5197. {
  5198. CFileTime ft;
  5199. GetSystemTimeAsFileTime(&ft);
  5200. // add the specified number of minutes
  5201. ft += CFileTimeSpan(((ULONGLONG)lMinutes)*60*10000000);
  5202. SYSTEMTIME st;
  5203. FileTimeToSystemTime(&ft, &st);
  5204. return SetExpiresAbsolute(st);
  5205. }
  5206. // Call this function to set whether or not to output to client.
  5207. // Intended primarily for HEAD requests
  5208. BOOL SetWriteToClient(BOOL bSendOutput) throw()
  5209. {
  5210. m_bSendOutput = bSendOutput;
  5211. return TRUE;
  5212. }
  5213. // Call this function to determine whether or not the data is
  5214. // sent to the client. Intended primarily for HEAD requests
  5215. BOOL GetWriteToClient() throw()
  5216. {
  5217. return m_bSendOutput;
  5218. }
  5219. // Call this function to write data to the response object.
  5220. //
  5221. // Returns S_OK on success, E_INVALIDARG or E_FAIL on failure.
  5222. //
  5223. // See WriteClient for comments on buffering.
  5224. //
  5225. // szOut A pointer to the first byte of the data to be written.
  5226. //
  5227. // nLen The number of bytes to write. If this parameter is -1,
  5228. // szOut is assumed to be a nul-terminated string and the
  5229. // whole string will be written.
  5230. //
  5231. // pdwWritten A DWORD pointer that can be used to get the number of bytes written.
  5232. // This parameter can be NULL.
  5233. HRESULT WriteStream(LPCSTR szOut, int nLen, DWORD *pdwWritten) throw()
  5234. {
  5235. ATLASSERT(m_spServerContext != NULL);
  5236. if (pdwWritten)
  5237. *pdwWritten = 0;
  5238. if (nLen == -1)
  5239. {
  5240. if (!szOut)
  5241. return E_INVALIDARG;
  5242. nLen = (int) strlen(szOut);
  5243. }
  5244. BOOL bRet = WriteLen(szOut, nLen);
  5245. if (!bRet)
  5246. {
  5247. return AtlHresultFromLastError();
  5248. }
  5249. if (pdwWritten)
  5250. *pdwWritten = nLen;
  5251. return S_OK;
  5252. }
  5253. // Call this function to write data to the response object.
  5254. //
  5255. // Returns TRUE on success. FALSE on failure.
  5256. //
  5257. // If buffering is disabled, data is written directly to the client.
  5258. // If buffering is enabled, this function attempts to write to the buffer.
  5259. // If the buffer is too small to contain its existing data and the new data,
  5260. // the current contents of the buffer are flushed.
  5261. // If the buffer is still too small to contain the new data, that data is written
  5262. // directly to the client. Otherwise the new data is written to the buffer.
  5263. //
  5264. // Any headers that have been set in the response will be sent just before the
  5265. // data is written to the client if no headers have been sent up to that point.
  5266. //
  5267. // szOut A pointer to the first byte of the data to be written.
  5268. //
  5269. // nLen The number of bytes to write.
  5270. BOOL WriteLen(LPCSTR szOut, DWORD dwLen) throw()
  5271. {
  5272. ATLASSERT(m_spServerContext != NULL);
  5273. if (!szOut)
  5274. return FALSE;
  5275. if (m_bBufferOutput)
  5276. {
  5277. if (m_strContent.GetLength()+dwLen >= m_dwBufferLimit)
  5278. {
  5279. if (!Flush())
  5280. return FALSE;
  5281. }
  5282. if (dwLen <= m_dwBufferLimit)
  5283. return m_strContent.Append(szOut, dwLen);
  5284. }
  5285. BOOL bRet = SendHeadersInternal();
  5286. if (bRet && m_bSendOutput)
  5287. bRet = m_spServerContext->WriteClient((void *) szOut, &dwLen);
  5288. return bRet;
  5289. }
  5290. // Call this function to redirect the client to a different resource.
  5291. //
  5292. // Returns TRUE on success, FALSE on failure.
  5293. //
  5294. // szURL A nul-terminated string specifying the resource the client should navigate to.
  5295. //
  5296. // statusCode An HTTP status code from the HTTP_REDIRECT enumeration describing the reason
  5297. // for the redirection.
  5298. //
  5299. // bSendBody Specifies whether to generate and send a response body with the headers.
  5300. //
  5301. // This function allows (and RFC 2616 encourages) a response body to be sent
  5302. // with the following redirect types:
  5303. // HTTP_REDIRECT_MOVED
  5304. // HTTP_REDIRECT_FOUND
  5305. // HTTP_REDIRECT_SEE_OTHER
  5306. // HTTP_REDIRECT_TEMPORARY_REDIRECT
  5307. // No body will be sent with other redirect types.
  5308. //
  5309. // The response body contains a short hypertext note with a hyperlink to the new resource.
  5310. // A meta refresh tag is also included to allow browsers to automatically redirect
  5311. // the user to the resource even if they don't understand the redirect header.
  5312. //
  5313. // See RFC 2616 section 10.3 for more information on redirection.
  5314. BOOL Redirect(LPCSTR szUrl, HTTP_REDIRECT statusCode=HTTP_REDIRECT_MOVED, BOOL bSendBody=TRUE) throw(...)
  5315. {
  5316. CStringA strBody;
  5317. LPCSTR szBody = NULL;
  5318. if (bSendBody &&
  5319. (HTTP_REDIRECT_MOVED == statusCode || HTTP_REDIRECT_FOUND == statusCode ||
  5320. HTTP_REDIRECT_SEE_OTHER == statusCode || HTTP_REDIRECT_TEMPORARY_REDIRECT == statusCode))
  5321. {
  5322. _ATLTRY
  5323. {
  5324. strBody.Format(
  5325. "<html>\r\n"
  5326. "<head>\r\n"
  5327. "<meta http-equiv=\"refresh\" content=\"0; url=%s\">\r\n"
  5328. "</head>\r\n"
  5329. "<body>Please use the following link to access this resource:"
  5330. " <a href=\"%s\">%s</a>\r\n"
  5331. "</body>\r\n"
  5332. "</html>\r\n",
  5333. szUrl, szUrl, szUrl);
  5334. }
  5335. _ATLCATCHALL()
  5336. {
  5337. return FALSE;
  5338. }
  5339. szBody = (LPCSTR) strBody;
  5340. }
  5341. return Redirect(szUrl, szBody, statusCode);
  5342. }
  5343. // Call this function to redirect the client to a different resource.
  5344. //
  5345. // Returns TRUE on success, FALSE on failure.
  5346. //
  5347. // szURL A nul-terminated string specifying the resource the client should navigate to.
  5348. //
  5349. // szBody A nul-terminated string containing the body of the response to be sent to the client.
  5350. //
  5351. // statusCode An HTTP status code from the HTTP_REDIRECT enumeration describing the reason
  5352. // for the redirection.
  5353. //
  5354. // This function allows (and RFC 2616 encourages) a response body to be sent
  5355. // with the following redirect types:
  5356. // HTTP_REDIRECT_MOVED
  5357. // HTTP_REDIRECT_FOUND
  5358. // HTTP_REDIRECT_SEE_OTHER
  5359. // HTTP_REDIRECT_TEMPORARY_REDIRECT
  5360. // No body will be sent with other redirect types.
  5361. //
  5362. // The response body should contain a short hypertext note with a hyperlink to the new resource.
  5363. // You can include a meta refresh tag to allow browsers to automatically redirect
  5364. // the user to the resource even if they don't understand the redirect header.
  5365. //
  5366. // See RFC 2616 section 10.3 for more information on redirection.
  5367. BOOL Redirect(LPCSTR szUrl, LPCSTR szBody, HTTP_REDIRECT statusCode=HTTP_REDIRECT_MOVED) throw()
  5368. {
  5369. // todo: handle multiple
  5370. SetStatusCode(statusCode);
  5371. AppendHeader("Location", szUrl);
  5372. _ATLTRY
  5373. {
  5374. if (!SendHeadersInternal())
  5375. return FALSE;
  5376. }
  5377. _ATLCATCHALL()
  5378. {
  5379. return FALSE;
  5380. }
  5381. if (szBody &&
  5382. (HTTP_REDIRECT_MOVED == statusCode || HTTP_REDIRECT_FOUND == statusCode ||
  5383. HTTP_REDIRECT_SEE_OTHER == statusCode || HTTP_REDIRECT_TEMPORARY_REDIRECT == statusCode))
  5384. {
  5385. Write(szBody);
  5386. return Flush();
  5387. }
  5388. return TRUE;
  5389. }
  5390. // Call this function to append a header to the collection of HTTP headers managed by this object.
  5391. //
  5392. // szName A nul-teminated string containing the name of the HTTP header.
  5393. //
  5394. // szValue A nul-teminated string containing the value of the HTTP header.
  5395. BOOL AppendHeader(LPCSTR szName, LPCSTR szValue) throw()
  5396. {
  5397. BOOL bRet = FALSE;
  5398. _ATLTRY
  5399. {
  5400. bRet = m_headers.Add(szName, szValue);
  5401. }
  5402. _ATLCATCHALL()
  5403. {
  5404. bRet = FALSE;
  5405. }
  5406. return bRet;
  5407. }
  5408. // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
  5409. //
  5410. // pCookie A pointer to a CCookie object describing the cookie to be sent to the client.
  5411. BOOL AppendCookie(const CCookie *pCookie) throw()
  5412. {
  5413. ATLASSERT(pCookie);
  5414. return AppendCookie((const CCookie&)*pCookie);
  5415. }
  5416. // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
  5417. //
  5418. // cookie A reference to a CCookie object describing the cookie to be sent to the client.
  5419. BOOL AppendCookie(const CCookie& cookie) throw()
  5420. {
  5421. CHAR szCookie[ATL_MAX_COOKIE_LEN];
  5422. DWORD dwBuffSize = ATL_MAX_COOKIE_LEN;
  5423. BOOL bRet = FALSE;
  5424. bRet = cookie.Render(szCookie, &dwBuffSize);
  5425. if (bRet)
  5426. {
  5427. bRet = m_headers.Add("Set-Cookie", szCookie);
  5428. }
  5429. if (!bRet && dwBuffSize > 0) //static buffer wasn't big enough.
  5430. {
  5431. //We'll have to try dynamically allocating it
  5432. //allocate a buffer
  5433. CAutoVectorPtr<CHAR> sz;
  5434. if (sz.Allocate(dwBuffSize+1))
  5435. {
  5436. DWORD dwSizeNew = dwBuffSize + 1;
  5437. if (cookie.Render(sz, &dwSizeNew))
  5438. {
  5439. bRet = m_headers.Add("Set-Cookie", (const char *) sz);
  5440. }
  5441. }
  5442. }
  5443. return bRet;
  5444. }
  5445. // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
  5446. //
  5447. // szName A nul-terminated string containing the name of the cookie to be sent to the client.
  5448. //
  5449. // szValue A nul-terminated string containing the value of the cookie to be sent to the client.
  5450. BOOL AppendCookie(LPCSTR szName, LPCSTR szValue) throw()
  5451. {
  5452. BOOL bRet = FALSE;
  5453. _ATLTRY
  5454. {
  5455. CCookie c(szName, szValue);
  5456. bRet = AppendCookie(c);
  5457. }
  5458. _ATLCATCHALL()
  5459. {
  5460. bRet = FALSE;
  5461. }
  5462. return bRet;
  5463. }
  5464. // Call this function to add a Set-Cookie header that removes a cookie value
  5465. // to the collection of HTTP headers managed by this object.
  5466. //
  5467. // szName A nul-terminated string containing the name of the cookie to be deleted
  5468. BOOL DeleteCookie(LPCSTR szName) throw()
  5469. {
  5470. BOOL bRet = FALSE;
  5471. _ATLTRY
  5472. {
  5473. CCookie cookie(szName, "");
  5474. cookie.SetMaxAge(0);
  5475. bRet = AppendCookie(cookie);
  5476. }
  5477. _ATLCATCHALL()
  5478. {
  5479. bRet = FALSE;
  5480. }
  5481. return bRet;
  5482. }
  5483. // Call this function to clear the collection of HTTP response headers maintained by this object.
  5484. //
  5485. // Note that clearing the headers includes removing all cookies associated with the response
  5486. // object. Cookies are sent to the client as Set-Cookie HTTP headers.
  5487. void ClearHeaders() throw()
  5488. {
  5489. m_headers.RemoveAll();
  5490. }
  5491. // Call this function to clear theresponse buffer without sending the contents to the client.
  5492. // If you need to empty the buffer but you do want the current contents sent to the client, call Flush instead.
  5493. void ClearContent() throw()
  5494. {
  5495. m_strContent.Empty();
  5496. }
  5497. // Call this function to send the current headers associated with this object to the client.
  5498. //
  5499. // Returns TRUE on success, FALSE on failure.
  5500. //
  5501. // The response headers are sent to the client using the current status code for the
  5502. // response object. See SetStatusCode and GetStatusCode.
  5503. BOOL SendHeadersInternal(BOOL fKeepConn=FALSE) throw()
  5504. {
  5505. if (m_bHeadersSent)
  5506. return TRUE;
  5507. ATLASSERT(m_spServerContext != NULL);
  5508. CStringA strHeaders;
  5509. RenderHeaders(strHeaders);
  5510. // REVIEW: should fix this to use the user's custom error provider
  5511. CDefaultErrorProvider prov;
  5512. BOOL bRet = FALSE;
  5513. _ATLTRY
  5514. {
  5515. CStringA strStatus = GetStatusHeader(m_nStatusCode, SUBERR_NONE, &prov);
  5516. bRet = m_spServerContext->SendResponseHeader(strHeaders, strStatus, fKeepConn);
  5517. if (bRet)
  5518. m_bHeadersSent = TRUE;
  5519. }
  5520. _ATLCATCHALL()
  5521. {
  5522. bRet = FALSE;
  5523. }
  5524. return bRet;
  5525. }
  5526. // Call this function to get a string containing all the HTTP headers associated with
  5527. // this object in a format suitable for sending to a client.
  5528. //
  5529. // strHeaders A CStringA reference to which will be appended the HTTP headers.
  5530. void RenderHeaders(CStringA& strHeaders) throw()
  5531. {
  5532. _ATLTRY
  5533. {
  5534. for (int i=0; i<m_headers.GetSize(); i++)
  5535. {
  5536. strHeaders += m_headers.GetKeyAt(i);
  5537. strHeaders += ": ";
  5538. strHeaders += m_headers.GetValueAt(i);
  5539. strHeaders += "\r\n";
  5540. }
  5541. strHeaders += "\r\n";
  5542. }
  5543. _ATLCATCHALL()
  5544. {
  5545. }
  5546. }
  5547. // Call this function to empty the response buffer and send its current
  5548. // contents to the client.
  5549. //
  5550. // Returns S_OK on success, or an error HRESULT on failure.
  5551. HRESULT FlushStream() throw()
  5552. {
  5553. if (!Flush())
  5554. return AtlHresultFromLastError();
  5555. return S_OK;
  5556. }
  5557. // Call this function to empty the response buffer and send its current
  5558. // contents to the client.
  5559. //
  5560. // Returns TRUE on success, or FALSE on failure.
  5561. //
  5562. // Any headers that have been set in the response will be sent just before the
  5563. // data is written to the client if no headers have been sent up to that point.
  5564. BOOL Flush(BOOL bFinal=FALSE) throw()
  5565. {
  5566. if (!m_spServerContext)
  5567. return FALSE;
  5568. BOOL bRet = TRUE;
  5569. _ATLTRY
  5570. {
  5571. // if the headers haven't been sent,
  5572. // send them now
  5573. if (!m_bHeadersSent)
  5574. {
  5575. char szProtocol[ATL_URL_MAX_URL_LENGTH];
  5576. DWORD dwProtocolLen = sizeof(szProtocol);
  5577. if (bFinal && m_bBufferOutput && m_dwBufferLimit==ULONG_MAX)
  5578. {
  5579. if (m_spServerContext->GetServerVariable("SERVER_PROTOCOL", szProtocol, &dwProtocolLen) &&
  5580. !strcmp(szProtocol, "HTTP/1.0"))
  5581. AppendHeader("Connection", "Keep-Alive");
  5582. _itoa(m_strContent.GetLength(), szProtocol, 10);
  5583. AppendHeader("Content-Length", szProtocol);
  5584. bRet = SendHeadersInternal(TRUE);
  5585. }
  5586. else
  5587. bRet = SendHeadersInternal();
  5588. }
  5589. if (m_bBufferOutput)
  5590. {
  5591. DWORD dwLen = 0;
  5592. dwLen = m_strContent.GetLength();
  5593. if (dwLen)
  5594. {
  5595. if (m_bSendOutput &&
  5596. m_spServerContext->WriteClient((void *) (LPCSTR) m_strContent, &dwLen) != TRUE)
  5597. {
  5598. m_strContent.Empty();
  5599. return FALSE;
  5600. }
  5601. m_strContent.Empty();
  5602. }
  5603. }
  5604. } // _ATLTRY
  5605. _ATLCATCHALL()
  5606. {
  5607. bRet = FALSE;
  5608. }
  5609. return bRet;
  5610. }
  5611. // Call this function to clear the response object of any headers
  5612. // and the contents of the buffer.
  5613. void ClearResponse() throw()
  5614. {
  5615. m_strContent.Empty();
  5616. m_headers.RemoveAll();
  5617. }
  5618. BOOL AsyncPrep(BOOL fKeepConn=FALSE) throw()
  5619. {
  5620. ATLASSERT(m_spServerContext != NULL);
  5621. return SendHeadersInternal(fKeepConn);
  5622. }
  5623. BOOL AsyncFlush() throw()
  5624. {
  5625. ATLASSERT(m_spServerContext != NULL);
  5626. BOOL bRet = SendHeadersInternal();
  5627. if (bRet && m_bBufferOutput)
  5628. {
  5629. DWORD dwLen = 0;
  5630. dwLen = m_strContent.GetLength();
  5631. if (dwLen)
  5632. {
  5633. if (m_spServerContext->AsyncWriteClient((void *) (LPCSTR) m_strContent, &dwLen) != TRUE)
  5634. return FALSE;
  5635. }
  5636. }
  5637. return bRet;
  5638. }
  5639. BOOL TransmitFile(HANDLE hFile, LPCSTR szContentType="text/plain") throw()
  5640. {
  5641. ATLASSERT(m_spServerContext != NULL);
  5642. ATLASSERT(hFile != NULL && hFile != INVALID_HANDLE_VALUE);
  5643. SetContentType(szContentType);
  5644. if (m_strContent.GetLength())
  5645. if (!Flush())
  5646. return FALSE;
  5647. BOOL bRet = SendHeadersInternal();
  5648. if (bRet)
  5649. {
  5650. bRet = m_spServerContext->TransmitFile(hFile, NULL, NULL, NULL,
  5651. 0, 0, NULL, 0, NULL, 0, HSE_IO_ASYNC);
  5652. }
  5653. return bRet;
  5654. }
  5655. }; // class CHttpResponse
  5656. #define ATLS_FLAG_NONE 0
  5657. #define ATLS_FLAG_ASYNC 1 // handler might do some async handling
  5658. //REVIEW: push_macro/pop_macro don't work in a template definition.
  5659. //these have been moved out of IRequestHandlerImpl temporarily because
  5660. //of placement new usage
  5661. #pragma push_macro("new")
  5662. #undef new
  5663. template <class T>
  5664. class PerThreadWrapper : public CComObjectNoLock<T>
  5665. {
  5666. public:
  5667. void *operator new(size_t /*size*/, void *p) throw()
  5668. {
  5669. return p;
  5670. }
  5671. void operator delete(void * /*p*/) throw()
  5672. {
  5673. }
  5674. STDMETHOD_(ULONG, Release)() throw()
  5675. {
  5676. ULONG l = InternalRelease();
  5677. if (l == 0)
  5678. {
  5679. T *pT = static_cast<T*>(this);
  5680. ATLASSERT(pT->m_spExtension != NULL);
  5681. CIsapiWorker *pWorker = pT->m_spExtension->GetThreadWorker();
  5682. ATLASSERT(pWorker);
  5683. delete this;
  5684. HeapFree(pWorker->m_hHeap, HEAP_NO_SERIALIZE, this);
  5685. }
  5686. return l;
  5687. }
  5688. };
  5689. template <typename THandler>
  5690. inline BOOL CreateRequestHandlerSync(IIsapiExtension *pExtension, IUnknown **ppOut) throw()
  5691. {
  5692. CIsapiWorker *pWorker = pExtension->GetThreadWorker();
  5693. ATLASSERT(pWorker);
  5694. void *pv = HeapAlloc(pWorker->m_hHeap, HEAP_NO_SERIALIZE, sizeof(PerThreadWrapper<THandler>));
  5695. if (!pv)
  5696. return FALSE;
  5697. PerThreadWrapper<THandler> *pHandler = new(pv) PerThreadWrapper<THandler>;
  5698. *ppOut = static_cast<IRequestHandler *>(pHandler);
  5699. pHandler->m_spExtension = pExtension;
  5700. (*ppOut)->AddRef();
  5701. return TRUE;
  5702. }
  5703. #pragma pop_macro("new")
  5704. #define DECLARE_ASYNC_HANDLER() \
  5705. static DWORD GetHandlerFlags() throw() \
  5706. { \
  5707. return ATLS_FLAG_ASYNC; \
  5708. } \
  5709. DWORD GetAsyncFlags() throw() \
  5710. { \
  5711. return ATLSRV_INIT_USEASYNC; \
  5712. }
  5713. #define DECLARE_ASYNC_HANDLER_EX() \
  5714. static DWORD GetHandlerFlags() throw() \
  5715. { \
  5716. return ATLS_FLAG_ASYNC; \
  5717. } \
  5718. DWORD GetAsyncFlags() throw() \
  5719. { \
  5720. return (ATLSRV_INIT_USEASYNC|ATLSRV_INIT_USEASYNC_EX); \
  5721. }
  5722. template <typename THandler>
  5723. class IRequestHandlerImpl : public IRequestHandler
  5724. {
  5725. public:
  5726. HINSTANCE m_hInstHandler;
  5727. CComPtr<IServiceProvider> m_spServiceProvider;
  5728. CComPtr<IHttpServerContext> m_spServerContext;
  5729. CComPtr<IIsapiExtension> m_spExtension;
  5730. DWORD m_dwAsyncFlags;
  5731. IRequestHandlerImpl() throw()
  5732. :m_hInstHandler(NULL)
  5733. {
  5734. m_dwAsyncFlags = 0;
  5735. }
  5736. HTTP_CODE GetFlags(DWORD *pdwStatus) throw(...)
  5737. {
  5738. THandler *pT = static_cast<THandler *>(this);
  5739. if (pdwStatus)
  5740. {
  5741. *pdwStatus = pT->GetAsyncFlags();
  5742. if (pT->CachePage())
  5743. *pdwStatus |= ATLSRV_INIT_USECACHE;
  5744. #ifdef _DEBUG
  5745. if (*pdwStatus & (ATLSRV_INIT_USEASYNC|ATLSRV_INIT_USEASYNC_EX))
  5746. ATLASSERT(pT->GetHandlerFlags() & ATLS_FLAG_ASYNC);
  5747. #endif
  5748. }
  5749. return HTTP_SUCCESS;
  5750. }
  5751. HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider) throw()
  5752. {
  5753. ATLASSERT(pRequestInfo != NULL);
  5754. ATLASSERT(pProvider != NULL);
  5755. ATLASSERT(pRequestInfo->hInstDll != NULL);
  5756. ATLASSERT(pRequestInfo->pServerContext != NULL);
  5757. // Initialize our internal references to required services
  5758. m_hInstHandler = pRequestInfo->hInstDll;
  5759. m_spServiceProvider = pProvider;
  5760. m_spServerContext = pRequestInfo->pServerContext;
  5761. return HTTP_SUCCESS;
  5762. }
  5763. HTTP_CODE InitializeChild(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider, IHttpRequestLookup * /*pLookup*/) throw()
  5764. {
  5765. return InitializeHandler(pRequestInfo, pProvider);
  5766. }
  5767. void UninitializeHandler() throw()
  5768. {
  5769. }
  5770. HTTP_CODE HandleRequest(
  5771. AtlServerRequest* /*pRequestInfo*/,
  5772. IServiceProvider* /*pServiceProvider*/) throw()
  5773. {
  5774. return HTTP_SUCCESS;
  5775. }
  5776. DWORD GetAsyncFlags() throw()
  5777. {
  5778. return m_dwAsyncFlags;
  5779. }
  5780. void SetAsyncFlags(DWORD dwAsyncFlags) throw()
  5781. {
  5782. ATLASSERT((dwAsyncFlags & ~(ATLSRV_INIT_USEASYNC|ATLSRV_INIT_USEASYNC_EX)) == 0);
  5783. m_dwAsyncFlags = dwAsyncFlags;
  5784. }
  5785. BOOL CachePage() throw()
  5786. {
  5787. return FALSE;
  5788. }
  5789. static DWORD GetHandlerFlags() throw()
  5790. {
  5791. return ATLS_FLAG_NONE;
  5792. }
  5793. // Used to create new instance of this object. A pointer to this
  5794. // function is stored in the handler map in user's code.
  5795. static BOOL CreateRequestHandler(IIsapiExtension *pExtension, IUnknown **ppOut) throw()
  5796. {
  5797. ATLASSERT(ppOut != NULL);
  5798. if (ppOut == NULL)
  5799. return false;
  5800. *ppOut = NULL;
  5801. if (THandler::GetHandlerFlags() & ATLS_FLAG_ASYNC)
  5802. {
  5803. THandler *pHandler = NULL;
  5804. ATLTRY(pHandler = new CComObjectNoLock<THandler>);
  5805. if (!pHandler)
  5806. return FALSE;
  5807. *ppOut = static_cast<IRequestHandler *>(pHandler);
  5808. pHandler->m_spExtension = pExtension;
  5809. (*ppOut)->AddRef();
  5810. }
  5811. else
  5812. {
  5813. if (!CreateRequestHandlerSync<THandler>(pExtension, ppOut))
  5814. return FALSE;
  5815. }
  5816. return TRUE;
  5817. }
  5818. // Used to initialize the class
  5819. // function is stored in the handler map in user's code.
  5820. static BOOL InitRequestHandlerClass(IHttpServerContext *pContext, IIsapiExtension *pExt) throw()
  5821. {
  5822. pContext; // unused
  5823. pExt; // unused
  5824. return TRUE;
  5825. }
  5826. // Used to uninitialize the class
  5827. // function is stored in the handler map in user's code.
  5828. static void UninitRequestHandlerClass() throw()
  5829. {
  5830. return;
  5831. }
  5832. };
  5833. struct CRequestStats
  5834. {
  5835. long m_lTotalRequests;
  5836. long m_lFailedRequests;
  5837. __int64 m_liTotalResponseTime;
  5838. long m_lAvgResponseTime;
  5839. long m_lCurrWaiting;
  5840. long m_lMaxWaiting;
  5841. long m_lActiveThreads;
  5842. CRequestStats() throw()
  5843. {
  5844. m_lTotalRequests = 0;
  5845. m_lFailedRequests = 0;
  5846. m_liTotalResponseTime = 0;
  5847. m_lAvgResponseTime = 0;
  5848. m_lCurrWaiting = 0;
  5849. m_lMaxWaiting = 0;
  5850. m_lActiveThreads = 0;
  5851. }
  5852. void RequestHandled(AtlServerRequest *pRequestInfo, BOOL bSuccess) throw()
  5853. {
  5854. InterlockedIncrement(&m_lTotalRequests);
  5855. if (!bSuccess)
  5856. InterlockedIncrement(&m_lFailedRequests);
  5857. long lTicks;
  5858. #ifndef ATL_NO_MMSYS
  5859. lTicks = (long) (timeGetTime() - pRequestInfo->dwStartTicks);
  5860. #else
  5861. lTicks = GetTickCount();
  5862. #endif
  5863. m_liTotalResponseTime += lTicks;
  5864. long lAv = (long) (m_liTotalResponseTime / m_lTotalRequests);
  5865. InterlockedExchange(&m_lAvgResponseTime, lAv);
  5866. InterlockedDecrement(&m_lActiveThreads);
  5867. }
  5868. long GetTotalRequests() throw()
  5869. {
  5870. return m_lTotalRequests;
  5871. }
  5872. long GetFailedRequests() throw()
  5873. {
  5874. return m_lFailedRequests;
  5875. }
  5876. long GetAvgResponseTime() throw()
  5877. {
  5878. return m_lAvgResponseTime;
  5879. }
  5880. void OnRequestReceived() throw()
  5881. {
  5882. InterlockedIncrement(&m_lCurrWaiting);
  5883. if (m_lCurrWaiting > m_lMaxWaiting)
  5884. InterlockedExchange(&m_lMaxWaiting, m_lCurrWaiting);
  5885. }
  5886. void OnRequestDequeued() throw()
  5887. {
  5888. InterlockedDecrement(&m_lCurrWaiting);
  5889. InterlockedIncrement(&m_lActiveThreads);
  5890. }
  5891. long GetCurrWaiting() throw()
  5892. {
  5893. return m_lCurrWaiting;
  5894. }
  5895. long GetMaxWaiting() throw()
  5896. {
  5897. return m_lCurrWaiting;
  5898. }
  5899. long GetActiveThreads() throw()
  5900. {
  5901. return m_lActiveThreads;
  5902. }
  5903. };
  5904. class CStdRequestStats : public CRequestStats
  5905. {
  5906. public:
  5907. HRESULT Initialize() throw()
  5908. {
  5909. return S_OK;
  5910. }
  5911. void Uninitialize() throw()
  5912. {
  5913. }
  5914. };
  5915. #define PERF_REQUEST_OBJECT 100
  5916. struct CPerfRequestStatObject : public CPerfObject,
  5917. public CRequestStats
  5918. {
  5919. DECLARE_PERF_OBJECT_EX(PERF_REQUEST_OBJECT, IDS_PERFMON_REQUEST, IDS_PERFMON_REQUEST_HELP, PERF_DETAIL_NOVICE, 0, sizeof(CPerfRequestStatObject), MAX_PATH, -1);
  5920. BEGIN_COUNTER_MAP(CPerfRequestStatObject)
  5921. DEFINE_COUNTER(m_lTotalRequests, IDS_PERFMON_REQUEST_TOTAL, IDS_PERFMON_REQUEST_TOTAL_HELP, PERF_COUNTER_RAWCOUNT, -1)
  5922. DEFINE_COUNTER(m_lFailedRequests, IDS_PERFMON_REQUEST_FAILED, IDS_PERFMON_REQUEST_FAILED_HELP, PERF_COUNTER_RAWCOUNT, -1)
  5923. DEFINE_COUNTER(m_lTotalRequests, IDS_PERFMON_REQUEST_RATE, IDS_PERFMON_REQUEST_RATE_HELP, PERF_COUNTER_COUNTER, -1)
  5924. DEFINE_COUNTER(m_lAvgResponseTime, IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME, IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP, PERF_COUNTER_RAWCOUNT, -1)
  5925. DEFINE_COUNTER(m_lCurrWaiting, IDS_PERFMON_REQUEST_CURR_WAITING, IDS_PERFMON_REQUEST_CURR_WAITING_HELP, PERF_COUNTER_RAWCOUNT, -1)
  5926. DEFINE_COUNTER(m_lMaxWaiting, IDS_PERFMON_REQUEST_MAX_WAITING, IDS_PERFMON_REQUEST_MAX_WAITING_HELP, PERF_COUNTER_RAWCOUNT, -1)
  5927. DEFINE_COUNTER(m_lActiveThreads, IDS_PERFMON_REQUEST_ACTIVE_THREADS, IDS_PERFMON_REQUEST_ACTIVE_THREADS, PERF_COUNTER_RAWCOUNT, -1)
  5928. END_COUNTER_MAP()
  5929. };
  5930. class CRequestPerfMon : public CPerfMon
  5931. {
  5932. public:
  5933. BEGIN_PERF_MAP(_T("ATL Server:Request"))
  5934. CHAIN_PERF_OBJECT(CPerfRequestStatObject)
  5935. END_PERF_MAP()
  5936. };
  5937. class CPerfMonRequestStats
  5938. {
  5939. CRequestPerfMon m_PerfMon;
  5940. CPerfRequestStatObject * m_pPerfObjectInstance;
  5941. CPerfRequestStatObject * m_pPerfObjectTotal;
  5942. public:
  5943. CPerfMonRequestStats() throw()
  5944. {
  5945. m_pPerfObjectInstance = NULL;
  5946. m_pPerfObjectTotal = NULL;
  5947. }
  5948. HRESULT Initialize() throw()
  5949. {
  5950. HRESULT hr;
  5951. m_pPerfObjectInstance = NULL;
  5952. m_pPerfObjectTotal = NULL;
  5953. hr = m_PerfMon.Initialize();
  5954. if (SUCCEEDED(hr))
  5955. {
  5956. CPerfLock lock(&m_PerfMon);
  5957. if (FAILED(hr = lock.GetStatus()))
  5958. {
  5959. return hr;
  5960. }
  5961. HINSTANCE hInst = _AtlBaseModule.GetModuleInstance();
  5962. WCHAR szName[MAX_PATH];
  5963. if (GetModuleFileNameW(hInst, szName, MAX_PATH) == 0)
  5964. {
  5965. return E_FAIL;
  5966. }
  5967. szName[MAX_PATH-1] = 0;
  5968. hr = m_PerfMon.CreateInstanceByName(PERF_REQUEST_OBJECT, L"_Total", reinterpret_cast<CPerfObject**>(&m_pPerfObjectTotal));
  5969. if (FAILED(hr))
  5970. {
  5971. return hr;
  5972. }
  5973. hr = m_PerfMon.CreateInstanceByName(PERF_REQUEST_OBJECT, szName, reinterpret_cast<CPerfObject**>(&m_pPerfObjectInstance));
  5974. if (FAILED(hr))
  5975. {
  5976. m_PerfMon.ReleaseInstance(m_pPerfObjectTotal);
  5977. m_pPerfObjectTotal = NULL;
  5978. return hr;
  5979. }
  5980. return S_OK;
  5981. }
  5982. return hr;
  5983. }
  5984. void Uninitialize() throw()
  5985. {
  5986. if (m_pPerfObjectInstance)
  5987. m_PerfMon.ReleaseInstance(m_pPerfObjectInstance);
  5988. if (m_pPerfObjectTotal)
  5989. m_PerfMon.ReleaseInstance(m_pPerfObjectTotal);
  5990. m_pPerfObjectInstance = NULL;
  5991. m_pPerfObjectTotal = NULL;
  5992. m_PerfMon.UnInitialize();
  5993. }
  5994. void RequestHandled(AtlServerRequest *pRequestInfo, BOOL bSuccess) throw()
  5995. {
  5996. CPerfLock lock(&m_PerfMon);
  5997. if (m_pPerfObjectInstance != NULL)
  5998. m_pPerfObjectInstance->RequestHandled(pRequestInfo, bSuccess);
  5999. if (m_pPerfObjectTotal != NULL)
  6000. m_pPerfObjectTotal->RequestHandled(pRequestInfo, bSuccess);
  6001. }
  6002. long GetTotalRequests() throw()
  6003. {
  6004. if (m_pPerfObjectInstance != NULL)
  6005. return m_pPerfObjectInstance->GetTotalRequests();
  6006. return 0;
  6007. }
  6008. long GetFailedRequests() throw()
  6009. {
  6010. if (m_pPerfObjectInstance != NULL)
  6011. return m_pPerfObjectInstance->GetFailedRequests();
  6012. return 0;
  6013. }
  6014. long GetAvgResponseTime() throw()
  6015. {
  6016. if (m_pPerfObjectInstance != NULL)
  6017. return m_pPerfObjectInstance->GetAvgResponseTime();
  6018. return 0;
  6019. }
  6020. void OnRequestReceived() throw()
  6021. {
  6022. if (m_pPerfObjectInstance != NULL)
  6023. m_pPerfObjectInstance->OnRequestReceived();
  6024. if (m_pPerfObjectTotal != NULL)
  6025. m_pPerfObjectTotal->OnRequestReceived();
  6026. }
  6027. void OnRequestDequeued() throw()
  6028. {
  6029. if (m_pPerfObjectInstance != NULL)
  6030. m_pPerfObjectInstance->OnRequestDequeued();
  6031. if (m_pPerfObjectTotal != NULL)
  6032. m_pPerfObjectTotal->OnRequestDequeued();
  6033. }
  6034. long GetCurrWaiting() throw()
  6035. {
  6036. if (m_pPerfObjectInstance != NULL)
  6037. return m_pPerfObjectInstance->GetCurrWaiting();
  6038. return 0;
  6039. }
  6040. long GetMaxWaiting() throw()
  6041. {
  6042. if (m_pPerfObjectInstance != NULL)
  6043. return m_pPerfObjectInstance->GetMaxWaiting();
  6044. return 0;
  6045. }
  6046. long GetActiveThreads() throw()
  6047. {
  6048. if (m_pPerfObjectInstance != NULL)
  6049. return m_pPerfObjectInstance->GetActiveThreads();
  6050. return 0;
  6051. }
  6052. };
  6053. class CNoRequestStats
  6054. {
  6055. protected:
  6056. public:
  6057. HRESULT Initialize() throw()
  6058. {
  6059. return S_OK;
  6060. }
  6061. void Uninitialize() throw()
  6062. {
  6063. }
  6064. void RequestHandled(AtlServerRequest * /*pRequestInfo*/, BOOL /*bSuccess*/) throw()
  6065. {
  6066. }
  6067. long GetTotalRequests() throw()
  6068. {
  6069. return 0;
  6070. }
  6071. long GetFailedRequests() throw()
  6072. {
  6073. return 0;
  6074. }
  6075. long GetAvgResponseTime() throw()
  6076. {
  6077. return 0;
  6078. }
  6079. void OnRequestReceived() throw()
  6080. {
  6081. }
  6082. void OnRequestDequeued() throw()
  6083. {
  6084. }
  6085. long GetCurrWaiting() throw()
  6086. {
  6087. return 0;
  6088. }
  6089. long GetMaxWaiting() throw()
  6090. {
  6091. return 0;
  6092. }
  6093. long GetActiveThreads() throw()
  6094. {
  6095. return 0;
  6096. }
  6097. };
  6098. inline LPSTR StripHandlerComment(LPSTR szLine) throw()
  6099. {
  6100. if (!memcmp(szLine, "<!--", 4))
  6101. {
  6102. szLine += 4;
  6103. while (_istspace(*szLine))
  6104. szLine++;
  6105. LPSTR szEndComment = strstr(szLine, "-->");
  6106. if (szEndComment)
  6107. *szEndComment = '\0';
  6108. return szLine;
  6109. }
  6110. return NULL;
  6111. }
  6112. struct ATLServerDllInfo
  6113. {
  6114. GETATLHANDLERBYNAME pfnGetHandler;
  6115. UNINITIALIZEATLHANDLERS pfnUninitHandlers;
  6116. INITIALIZEATLHANDLERS pfnInitHandlers;
  6117. IIsapiExtension *pExtension;
  6118. IHttpServerContext *pContext;
  6119. };
  6120. class CDllCachePeer
  6121. {
  6122. public:
  6123. struct DllInfo : public ATLServerDllInfo
  6124. {
  6125. operator=(const DllInfo& right) throw()
  6126. {
  6127. pfnGetHandler = right.pfnGetHandler;
  6128. pfnUninitHandlers = right.pfnUninitHandlers;
  6129. pfnInitHandlers = right.pfnInitHandlers;
  6130. pExtension = right.pExtension;
  6131. pContext = right.pContext;
  6132. }
  6133. };
  6134. BOOL Add(HINSTANCE hInst, DllInfo *pInfo) throw(...)
  6135. {
  6136. pInfo->pfnInitHandlers = (INITIALIZEATLHANDLERS) GetProcAddress(hInst, ATLS_FUNCID_INITIALIZEHANDLERS);
  6137. pInfo->pfnGetHandler = (GETATLHANDLERBYNAME) GetProcAddress(hInst, ATLS_FUNCID_GETATLHANDLERBYNAME);
  6138. if (!pInfo->pfnGetHandler)
  6139. return FALSE;
  6140. pInfo->pfnUninitHandlers = (UNINITIALIZEATLHANDLERS) GetProcAddress(hInst, ATLS_FUNCID_UNINITIALIZEHANDLERS);
  6141. if (pInfo->pfnInitHandlers)
  6142. {
  6143. pInfo->pfnInitHandlers(pInfo->pContext, pInfo->pExtension);
  6144. pInfo->pContext = NULL; // won't be valid after this call
  6145. }
  6146. return TRUE;
  6147. }
  6148. void Remove(HINSTANCE /*hInst*/, DllInfo *pInfo) throw(...)
  6149. {
  6150. if (pInfo->pfnUninitHandlers)
  6151. (*pInfo->pfnUninitHandlers)();
  6152. }
  6153. };
  6154. inline bool operator==(const CDllCachePeer::DllInfo& left, const CDllCachePeer::DllInfo& right) throw()
  6155. {
  6156. return ( (left.pfnGetHandler == right.pfnGetHandler) &&
  6157. (left.pfnUninitHandlers == right.pfnUninitHandlers) &&
  6158. (left.pfnInitHandlers == right.pfnInitHandlers) &&
  6159. (left.pExtension == right.pExtension) &&
  6160. (left.pContext == right.pContext)
  6161. );
  6162. }
  6163. // Helper function to impersonate the client
  6164. // on the current thread
  6165. inline BOOL AtlImpersonateClient(IHttpServerContext *pServerContext) throw()
  6166. {
  6167. // impersonate the calling client on the current thread
  6168. HANDLE hToken;
  6169. if (!pServerContext->GetImpersonationToken(&hToken))
  6170. return FALSE;
  6171. if (!SetThreadToken(NULL, hToken))
  6172. return FALSE;
  6173. return TRUE;
  6174. }
  6175. // Helper class to set the thread impersonation token
  6176. // This is mainly used internally to ensure that we
  6177. // don't forget to revert to the process impersonation
  6178. // level
  6179. class CSetThreadToken
  6180. {
  6181. public:
  6182. BOOL Initialize(AtlServerRequest *pRequestInfo) throw()
  6183. {
  6184. return AtlImpersonateClient(pRequestInfo->pServerContext);
  6185. }
  6186. ~CSetThreadToken() throw()
  6187. {
  6188. RevertToSelf();
  6189. }
  6190. };
  6191. //REVIEW: push_macro/pop_macro don't work in a template definition.
  6192. //this has been moved out of temporarily because
  6193. //of placement new usage
  6194. #pragma push_macro("new")
  6195. #undef new
  6196. template <class Base>
  6197. class _CComObjectHeap : public Base
  6198. {
  6199. public:
  6200. typedef Base _BaseClass;
  6201. HANDLE m_hHeap;
  6202. _CComObjectHeap(HANDLE hHeap) throw()
  6203. {
  6204. m_hHeap = hHeap;
  6205. }
  6206. // Set refcount to 1 to protect destruction
  6207. ~_CComObjectHeap() throw()
  6208. {
  6209. m_dwRef = 1L;
  6210. FinalRelease();
  6211. #ifdef _ATL_DEBUG_INTERFACES
  6212. _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
  6213. #endif
  6214. }
  6215. //If InternalAddRef or InternalRelease is undefined then your class
  6216. //doesn't derive from CComObjectRoot
  6217. STDMETHOD_(ULONG, AddRef)()throw() {return InternalAddRef();}
  6218. STDMETHOD_(ULONG, Release)()throw()
  6219. {
  6220. ULONG l = InternalRelease();
  6221. if (l == 0)
  6222. {
  6223. HANDLE hHeap = m_hHeap;;
  6224. this->~_CComObjectHeap();
  6225. HeapFree(hHeap, 0, this);
  6226. }
  6227. return l;
  6228. }
  6229. //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
  6230. STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw()
  6231. {return _InternalQueryInterface(iid, ppvObject);}
  6232. };
  6233. inline CServerContext* CreateServerContext(HANDLE hRequestHeap) throw()
  6234. {
  6235. // Allocate a fixed block size to avoid fragmentation
  6236. void *pv = HeapAlloc(hRequestHeap, HEAP_ZERO_MEMORY,
  6237. max(sizeof(AtlServerRequest), sizeof(_CComObjectHeap<CServerContext>)));
  6238. if (!pv)
  6239. return FALSE;
  6240. _CComObjectHeap<CServerContext>* pContext = new(pv) _CComObjectHeap<CServerContext>(hRequestHeap);
  6241. return pContext;
  6242. }
  6243. #pragma pop_macro("new")
  6244. // _AtlGetHandlerName
  6245. // get handler name from stencil file. Ignore all server side comments
  6246. // szFileName - the file from which to extract the handler name
  6247. // szHandlerName - buffer into which handler name will be copied,
  6248. // it is assumed to be of size MAX_PATH+ATL_MAX_HANDLER_NAME+2
  6249. inline HTTP_CODE _AtlGetHandlerName(LPCSTR szFileName, LPSTR szHandlerName) throw()
  6250. {
  6251. szHandlerName[0] = '\0';
  6252. CAtlFile cfFile;
  6253. HRESULT hr;
  6254. _ATLTRY
  6255. {
  6256. hr = cfFile.Create(CA2TEX<MAX_PATH+1>(szFileName), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
  6257. if (FAILED(hr))
  6258. return HTTP_ERROR(500, ISE_SUBERR_LOADFILEFAIL);
  6259. }
  6260. _ATLCATCHALL()
  6261. {
  6262. return ISE_SUBERR_OUTOFMEM; // CA2TEX threw
  6263. }
  6264. if (cfFile.m_h == NULL || GetFileType(cfFile.m_h) != FILE_TYPE_DISK)
  6265. {
  6266. if (hr == AtlHresultFromWin32(ERROR_FILE_NOT_FOUND))
  6267. return HTTP_ERROR(404, SUBERR_NONE);
  6268. else
  6269. return HTTP_ERROR(500, IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL);
  6270. }
  6271. HTTP_CODE hcErr = HTTP_SUCCESS;
  6272. DWORD dwRead=0;
  6273. LPSTR szHandler = "handler";
  6274. LPSTR pszHandlerPos = NULL;
  6275. LPSTR pszHandlerName = szHandlerName;
  6276. char szBuf[4097];
  6277. LPSTR szCurly = NULL;
  6278. LPSTR pszBuf = NULL;
  6279. bool bInQuote = false;
  6280. // state:
  6281. // 0 = default/unknown
  6282. // 1 = have "{"
  6283. // 2 = have "{{" -- skip spaces
  6284. // 3 = have "{{" -- check "handler"
  6285. // 4 = have "handler" -- skip spaces
  6286. // 5 = have "handler" -- get name
  6287. // 6 = scan until first '}'
  6288. // 7 = better be '}'
  6289. // 8 = done
  6290. int nState = 0;
  6291. do
  6292. {
  6293. hr = cfFile.Read(szBuf, sizeof(szBuf)-1, dwRead);
  6294. if (hr != S_OK)
  6295. {
  6296. return HTTP_ERROR(500, ISE_SUBERR_READFILEFAIL); // failed reading
  6297. }
  6298. szBuf[dwRead] = '\0';
  6299. pszBuf = szBuf;
  6300. while (*pszBuf && nState != 8)
  6301. {
  6302. switch (nState)
  6303. {
  6304. case 0:
  6305. // 0 = default/unknown
  6306. // look for the first curly
  6307. szCurly = strchr(pszBuf, '{');
  6308. if (!szCurly)
  6309. {
  6310. // skip to the end of the buffer
  6311. pszBuf = szBuf+dwRead-1;
  6312. }
  6313. else
  6314. {
  6315. pszBuf = szCurly;
  6316. nState = 1;
  6317. }
  6318. break;
  6319. case 1:
  6320. // 1 = have "{"
  6321. if (*pszBuf == '{')
  6322. {
  6323. nState = 2;
  6324. }
  6325. else
  6326. {
  6327. nState = 0; // if the next character is not a '{', start over
  6328. }
  6329. break;
  6330. case 2:
  6331. if (!isspace(*pszBuf))
  6332. {
  6333. pszHandlerPos = szHandler;
  6334. pszBuf--;
  6335. nState = 3;
  6336. }
  6337. break;
  6338. case 3:
  6339. // 3 = partial handler "h..."
  6340. if (*pszBuf != *pszHandlerPos)
  6341. {
  6342. // not a handler, skip tag
  6343. nState = 6;
  6344. }
  6345. else
  6346. {
  6347. pszHandlerPos++;
  6348. if (!*pszHandlerPos) // at the end of the "handler" part
  6349. nState = 4;
  6350. }
  6351. break;
  6352. case 4:
  6353. // 4 = have "handler" -- skip spaces
  6354. if (!isspace(*pszBuf))
  6355. {
  6356. if (*pszBuf == '\"')
  6357. {
  6358. bInQuote = true;
  6359. }
  6360. else
  6361. {
  6362. pszBuf--;
  6363. }
  6364. nState = 5;
  6365. }
  6366. break;
  6367. case 5:
  6368. // 5 = have "handler" -- get name
  6369. if (isspace(*pszBuf) && !bInQuote)
  6370. {
  6371. if (*(pszHandlerName-1) != '/')
  6372. {
  6373. // end of the name -- jump to getting the first '}'
  6374. nState = 6;
  6375. }
  6376. else
  6377. {
  6378. nState = 4;
  6379. }
  6380. }
  6381. else if (*pszBuf == '}')
  6382. {
  6383. // end of the name -- jump to getting the second '}'
  6384. nState = 7;
  6385. }
  6386. else if (*pszBuf == '\"')
  6387. {
  6388. if (bInQuote)
  6389. {
  6390. bInQuote = false;
  6391. }
  6392. else
  6393. {
  6394. hcErr = HTTP_FAIL;
  6395. nState = 8;
  6396. }
  6397. }
  6398. else
  6399. {
  6400. // ensure we don't overwrite the buffer
  6401. if (pszHandlerName-szHandlerName >= MAX_PATH+ATL_MAX_HANDLER_NAME_LEN+1)
  6402. {
  6403. hcErr = HTTP_FAIL;
  6404. nState = 8;
  6405. }
  6406. else
  6407. {
  6408. *pszHandlerName++ = *pszBuf;
  6409. }
  6410. }
  6411. break;
  6412. case 6:
  6413. // 6 = scan until first '}'
  6414. if (*pszBuf == '}')
  6415. nState = 7;
  6416. break;
  6417. case 7:
  6418. // 7 = better be '}'
  6419. if (*pszBuf != '}')
  6420. {
  6421. hcErr = HTTP_ERROR(500, ISE_SUBERR_BAD_HANDLER_TAG);
  6422. nState = 8;
  6423. }
  6424. if (*szHandlerName)
  6425. nState = 8;
  6426. else
  6427. nState = 0;
  6428. break;
  6429. default:
  6430. __assume( 0 ); // the optimizer will not generate code for this branch
  6431. }
  6432. pszBuf++;
  6433. }
  6434. } while (dwRead != 0 && nState != 8);
  6435. *pszHandlerName = '\0';
  6436. return hcErr;
  6437. }
  6438. // _AtlCrackHandler cracks a request path of the form dll_path/handler_name into its
  6439. // consituent parts
  6440. // szHandlerDllName - the full handler path of the form "dll_path/handler_name"
  6441. // szDllPath - the DLL path (should be of length MAX_PATH+1)
  6442. // szHandlerName - the handler name (should be of length ATL_MAX_HANDLER_NAME_LEN+1)
  6443. //
  6444. inline BOOL _AtlCrackHandler(
  6445. LPCSTR szHandlerDllName,
  6446. LPSTR szDllPath,
  6447. LPDWORD pdwDllPathLen,
  6448. LPSTR szHandlerName,
  6449. LPDWORD pdwHandlerNameLen) throw()
  6450. {
  6451. ATLASSERT( szHandlerDllName != NULL );
  6452. ATLASSERT( szDllPath != NULL );
  6453. ATLASSERT( pdwDllPathLen != NULL );
  6454. ATLASSERT( szHandlerName != NULL );
  6455. ATLASSERT( pdwHandlerNameLen != NULL );
  6456. BOOL bRet = TRUE;
  6457. // skip leading spaces
  6458. while (*szHandlerDllName && isspace(*szHandlerDllName))
  6459. ++szHandlerDllName;
  6460. // get the handler name
  6461. LPSTR szSlash = strchr(szHandlerDllName, '/');
  6462. LPSTR szEnd = NULL;
  6463. LPSTR szSlashEnd = NULL;
  6464. // if it is of the form <dll_name>/<handler_name>
  6465. if (szSlash)
  6466. {
  6467. szEnd = szSlash;
  6468. // skip trailing spaces on <dll_name>
  6469. while (szEnd>szHandlerDllName && isspace(*(szEnd-1)))
  6470. --szEnd;
  6471. szSlash++;
  6472. // skip leading whitespace
  6473. while (*szSlash && isspace(*szSlash))
  6474. szSlash++;
  6475. // right trim szSlash;
  6476. szSlashEnd = szSlash;
  6477. while (*szSlashEnd && !isspace(*szSlashEnd))
  6478. szSlashEnd++;
  6479. }
  6480. else // only the <dll_name>
  6481. {
  6482. szSlash = "Default";
  6483. szSlashEnd = szSlash+sizeof("Default")-1;
  6484. // do it this way to handle paths with spaces
  6485. // (e.g. "some path\subdirectory one\subdirectory two\dll_name.dll")
  6486. szEnd = (LPSTR) (szHandlerDllName+strlen(szHandlerDllName));
  6487. // skip trailing spaces
  6488. while (szEnd>szHandlerDllName && isspace(*(szEnd-1)))
  6489. --szEnd;
  6490. }
  6491. // if the dll path is quoted, strip the quotes
  6492. if (*szHandlerDllName == '\"' && *(szEnd-1) == '\"' && szEnd > szHandlerDllName+2)
  6493. {
  6494. szHandlerDllName++;
  6495. szEnd--;
  6496. }
  6497. if (*pdwDllPathLen > (DWORD)(szEnd-szHandlerDllName))
  6498. {
  6499. memcpy(szDllPath, szHandlerDllName, szEnd-szHandlerDllName);
  6500. szDllPath[szEnd-szHandlerDllName] = '\0';
  6501. *pdwDllPathLen = (DWORD)(szEnd-szHandlerDllName);
  6502. }
  6503. else
  6504. {
  6505. *pdwDllPathLen = (DWORD)(szEnd-szHandlerDllName)+1;
  6506. bRet = FALSE;
  6507. }
  6508. if (*pdwHandlerNameLen > (DWORD)(szSlashEnd-szSlash))
  6509. {
  6510. memcpy(szHandlerName, szSlash, (szSlashEnd-szSlash));
  6511. szHandlerName[szSlashEnd-szSlash] = '\0';
  6512. *pdwHandlerNameLen = (DWORD)(szSlashEnd-szSlash);
  6513. }
  6514. else
  6515. {
  6516. *pdwHandlerNameLen = (DWORD)(szSlashEnd-szSlash)+1;
  6517. bRet = FALSE;
  6518. }
  6519. return bRet;
  6520. }
  6521. inline HTTP_CODE _AtlLoadRequestHandler(
  6522. LPCSTR szDllPath,
  6523. LPCSTR szHandlerName,
  6524. IHttpServerContext *pServerContext,
  6525. HINSTANCE *phInstance,
  6526. IRequestHandler **ppHandler,
  6527. IIsapiExtension *pExtension,
  6528. IDllCache *pDllCache) throw(...)
  6529. {
  6530. *phInstance = NULL;
  6531. *ppHandler = NULL;
  6532. ATLServerDllInfo DllInfo;
  6533. DllInfo.pExtension = pExtension;
  6534. DllInfo.pContext = pServerContext;
  6535. if (!IsFullPathA(szDllPath))
  6536. {
  6537. CHAR szFileName[MAX_PATH];
  6538. if (!GetScriptFullFileName(szDllPath, szFileName, pServerContext))
  6539. {
  6540. return HTTP_FAIL;
  6541. }
  6542. *phInstance = pDllCache->Load(szFileName, (void *)&DllInfo);
  6543. }
  6544. else
  6545. {
  6546. *phInstance = pDllCache->Load(szDllPath, (void *)&DllInfo);
  6547. }
  6548. if (!*phInstance)
  6549. {
  6550. ATLTRACE( "LoadLibrary failed: '%s' with error: %d\r\n", szDllPath, GetLastError() );
  6551. return HTTP_ERROR(500, ISE_SUBERR_LOADLIB);
  6552. }
  6553. CComPtr<IUnknown> spUnk;
  6554. if (!DllInfo.pfnGetHandler ||
  6555. !DllInfo.pfnGetHandler(szHandlerName, pExtension, &spUnk) ||
  6556. spUnk->QueryInterface(__uuidof(IRequestHandler), (void **)ppHandler))
  6557. {
  6558. pDllCache->Free(*phInstance);
  6559. *phInstance = NULL;
  6560. return HTTP_ERROR(500, ISE_SUBERR_HANDLER_NOT_FOUND);
  6561. }
  6562. return HTTP_SUCCESS;
  6563. } // _AtlLoadRequestHandler
  6564. class CTransferServerContext : public CComObjectRootEx<CComMultiThreadModel>,
  6565. public CWrappedServerContext
  6566. {
  6567. public:
  6568. char m_szFileName[MAX_PATH+1];
  6569. char m_szQueryString[ATL_URL_MAX_PATH_LENGTH+1];
  6570. IWriteStream *m_pStream;
  6571. BEGIN_COM_MAP(CTransferServerContext)
  6572. COM_INTERFACE_ENTRY(IHttpServerContext)
  6573. END_COM_MAP()
  6574. CTransferServerContext() throw()
  6575. {
  6576. m_pStream = NULL;
  6577. }
  6578. BOOL Initialize(LPCSTR szUrl, IWriteStream *pStream, IHttpServerContext *pParent) throw()
  6579. {
  6580. m_pStream = pStream;
  6581. m_spParent = pParent;
  6582. m_szFileName[0] = '\0';
  6583. long nUrlLen = (long)strlen(szUrl);
  6584. if (!IsFullPathA(szUrl))
  6585. {
  6586. DWORD dwLen = MAX_PATH;
  6587. BOOL bRet = m_spParent->GetServerVariable(
  6588. "APPL_PHYSICAL_PATH",
  6589. m_szFileName,
  6590. &dwLen);
  6591. if (!bRet)
  6592. return FALSE;
  6593. }
  6594. // check for query params
  6595. LPCSTR szMark = strchr(szUrl, '?');
  6596. if (szMark)
  6597. {
  6598. long nPathLen = (long) (szMark - szUrl);
  6599. if (strlen(m_szFileName) + nPathLen < MAX_PATH)
  6600. {
  6601. if (m_szFileName[0])
  6602. strncat(m_szFileName, szUrl, nPathLen);
  6603. else
  6604. {
  6605. memcpy(m_szFileName, szUrl, nPathLen);
  6606. m_szFileName[nPathLen] = '\0';
  6607. }
  6608. }
  6609. else
  6610. return FALSE; // path would overwrite buffer
  6611. // save query params
  6612. if (strlen(szMark + 1) < ATL_URL_MAX_PATH_LENGTH)
  6613. strcpy(m_szQueryString, szMark+1);
  6614. else
  6615. return FALSE; // url would overwrite buffer
  6616. }
  6617. else
  6618. {
  6619. // no query string
  6620. if (strlen(m_szFileName) + nUrlLen < MAX_PATH)
  6621. {
  6622. if (m_szFileName[0])
  6623. strcat(m_szFileName, szUrl);
  6624. else
  6625. strcpy(m_szFileName, szUrl);
  6626. }
  6627. else
  6628. return FALSE; // path would be too long
  6629. m_szQueryString[0] = '\0';
  6630. }
  6631. return TRUE;
  6632. }
  6633. BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
  6634. {
  6635. HRESULT hr = m_pStream->WriteStream((LPCSTR) pvBuffer, *pdwBytes, pdwBytes);
  6636. return SUCCEEDED(hr);
  6637. }
  6638. LPCSTR GetQueryString() throw()
  6639. {
  6640. ATLASSERT(m_spParent);
  6641. return m_szQueryString;
  6642. }
  6643. LPCSTR GetScriptPathTranslated() throw()
  6644. {
  6645. ATLASSERT(m_spParent);
  6646. return m_szFileName;
  6647. }
  6648. LPCSTR GetPathTranslated() throw()
  6649. {
  6650. ATLASSERT(m_spParent);
  6651. return m_szFileName;
  6652. }
  6653. // Asynchronous writes will not work properly in a child handler
  6654. BOOL AsyncWriteClient(void * /*pvBuffer*/, DWORD * /*pdwBytes*/) throw()
  6655. {
  6656. ATLASSERT(FALSE);
  6657. return FALSE;
  6658. }
  6659. // These next few methods are to protect against attempting to parse form data twice
  6660. // We tell the new handler that it was a GET request
  6661. LPCSTR GetRequestMethod() throw()
  6662. {
  6663. ATLASSERT(m_spParent);
  6664. return "GET";
  6665. }
  6666. // The handler should not query these methods -- they are only useful if attempting to
  6667. // parse form data, which is not allowed in child handlers.
  6668. BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/) throw()
  6669. {
  6670. return FALSE;
  6671. }
  6672. BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/) throw()
  6673. {
  6674. return FALSE;
  6675. }
  6676. DWORD GetTotalBytes() throw()
  6677. {
  6678. ATLASSERT(FALSE);
  6679. return 0;
  6680. }
  6681. DWORD GetAvailableBytes() throw()
  6682. {
  6683. ATLASSERT(FALSE);
  6684. return 0;
  6685. }
  6686. BYTE *GetAvailableData() throw()
  6687. {
  6688. ATLASSERT(FALSE);
  6689. return NULL;
  6690. }
  6691. LPCSTR GetContentType() throw()
  6692. {
  6693. ATLASSERT(FALSE);
  6694. return 0;
  6695. }
  6696. };
  6697. inline HTTP_CODE _AtlTransferRequest(
  6698. AtlServerRequest *pRequest,
  6699. IServiceProvider *pServiceProvider,
  6700. IWriteStream *pWriteStream,
  6701. IHttpRequestLookup *pLookup,
  6702. LPCSTR szNewUrl,
  6703. WORD nCodePage,
  6704. bool bContinueAfterProcess = false,
  6705. void *pState = NULL) throw(...)
  6706. {
  6707. ATLASSERT(pRequest != NULL);
  6708. AtlServerRequest* pRequestInfo = NULL;
  6709. HTTP_CODE dwErr = HTTP_SUCCESS;
  6710. CComPtr<IStencilCache> spStencilCache;
  6711. if (pRequest->pServerContext == NULL)
  6712. return HTTP_ERROR(500, 0);
  6713. pServiceProvider->QueryService(
  6714. __uuidof(IStencilCache),
  6715. __uuidof(IStencilCache),
  6716. (void**)&spStencilCache
  6717. );
  6718. if (!spStencilCache)
  6719. return HTTP_ERROR(500, 0);
  6720. CComObjectStackEx<CTransferServerContext> serverContext;
  6721. serverContext.Initialize(szNewUrl, pWriteStream, pRequest->pServerContext);
  6722. CStencilState* _pState = reinterpret_cast<CStencilState*>(pState);
  6723. if (_pState && _pState->pIncludeInfo)
  6724. {
  6725. pRequestInfo = _pState->pIncludeInfo;
  6726. _pState->pIncludeInfo = NULL;
  6727. }
  6728. else
  6729. {
  6730. ATLASSERT(spStencilCache != NULL);
  6731. ATLASSERT(pRequest->pDllCache != NULL);
  6732. ATLASSERT(pRequest->pExtension != NULL);
  6733. pRequestInfo = pRequest->pExtension->CreateRequest();
  6734. if (pRequestInfo == NULL)
  6735. return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
  6736. pRequestInfo->dwRequestState = ATLSRV_STATE_BEGIN;
  6737. pRequestInfo->dwRequestType = ATLSRV_REQUEST_STENCIL;
  6738. pRequestInfo->pDllCache = pRequest->pDllCache;
  6739. pRequestInfo->pExtension = pRequest->pExtension;
  6740. pRequestInfo->pServerContext = &serverContext;
  6741. if (_pState)
  6742. pRequestInfo->pUserData = _pState->pParentInfo->pUserData;
  6743. else
  6744. pRequestInfo->pUserData = pRequest->pUserData;
  6745. // Extract the file extension of the included file by searching
  6746. // for the first '.' from the right.
  6747. // Can't use _tcsrchr because we have to use the stencil's codepage
  6748. LPSTR szDot = NULL;
  6749. LPSTR szMark = serverContext.m_szFileName;
  6750. while (*szMark)
  6751. {
  6752. if (*szMark == '.')
  6753. szDot = szMark;
  6754. szMark = CharNextExA(nCodePage, szMark, 0);
  6755. }
  6756. if (szDot && _stricmp(szDot, c_AtlSRFExtension) == 0)
  6757. {
  6758. dwErr = pRequest->pExtension->LoadDispatchFile(
  6759. serverContext.m_szFileName,
  6760. pRequestInfo
  6761. );
  6762. if (dwErr)
  6763. return dwErr;
  6764. CComPtr<IHttpRequestLookup> spLookup;
  6765. DWORD dwStatus;
  6766. if (pLookup)
  6767. {
  6768. dwErr = pRequestInfo->pHandler->GetFlags(&dwStatus);
  6769. if (dwErr)
  6770. return dwErr;
  6771. if (dwStatus & (ATLSRV_INIT_USEASYNC | ATLSRV_INIT_USEASYNC_EX))
  6772. {
  6773. CComObjectNoLock<CTransferServerContext>* pServerContext = NULL;
  6774. ATLTRY(pServerContext = new CComObjectNoLock<CTransferServerContext>);
  6775. if (pServerContext == NULL)
  6776. return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
  6777. pServerContext->Initialize(szNewUrl, pWriteStream, pRequest->pServerContext);
  6778. pServerContext->AddRef();
  6779. pRequestInfo->pServerContext = pServerContext;
  6780. }
  6781. dwErr = pRequestInfo->pHandler->InitializeChild(
  6782. pRequestInfo,
  6783. pServiceProvider,
  6784. pLookup);
  6785. if (dwErr)
  6786. return dwErr;
  6787. }
  6788. }
  6789. else if (szDot && _stricmp(szDot, ".dll") == 0)
  6790. {
  6791. // Get the handler name if they used the asdf.dll?Handler=Default notation
  6792. // REVIEW : case sensitivity on the "Handler"?
  6793. char szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1] = { '\0' };
  6794. LPSTR szStart = strstr(serverContext.m_szQueryString, "Handler");
  6795. if (szStart)
  6796. {
  6797. szStart += 8; // Skip past "Handler" and the "="
  6798. LPSTR szEnd = strchr(szStart, '&');
  6799. if (szEnd)
  6800. {
  6801. memcpy(szHandlerName, szStart, min((szEnd-szStart), ATL_MAX_HANDLER_NAME_LEN));
  6802. szHandlerName[min((szEnd-szStart), ATL_MAX_HANDLER_NAME_LEN)] = '\0';
  6803. }
  6804. else
  6805. {
  6806. strcpy(szHandlerName, szStart);
  6807. }
  6808. }
  6809. else
  6810. {
  6811. memcpy(szHandlerName, "Default", sizeof("Default"));
  6812. }
  6813. pRequestInfo->dwRequestType = ATLSRV_REQUEST_DLL;
  6814. dwErr = pRequest->pExtension->LoadRequestHandler(
  6815. serverContext.m_szFileName,
  6816. szHandlerName,
  6817. pRequestInfo->pServerContext,
  6818. &pRequestInfo->hInstDll,
  6819. &pRequestInfo->pHandler
  6820. );
  6821. if (dwErr != HTTP_SUCCESS)
  6822. return dwErr;
  6823. ATLASSERT(pLookup);
  6824. dwErr = pRequestInfo->pHandler->InitializeChild(
  6825. pRequestInfo,
  6826. pServiceProvider,
  6827. pLookup
  6828. );
  6829. }
  6830. pRequestInfo->pfnHandleRequest = &IRequestHandler::HandleRequest;
  6831. }
  6832. if (pRequestInfo)
  6833. {
  6834. if (!dwErr)
  6835. {
  6836. if (pRequestInfo->pServerContext == NULL)
  6837. pRequestInfo->pServerContext = &serverContext;
  6838. ATLASSERT(pRequestInfo->pfnHandleRequest != NULL);
  6839. dwErr = (pRequestInfo->pHandler->*pRequestInfo->pfnHandleRequest)(pRequestInfo, pServiceProvider);
  6840. if (pRequestInfo->pServerContext == &serverContext)
  6841. pRequestInfo->pServerContext = NULL;
  6842. if (IsAsyncStatus(dwErr))
  6843. {
  6844. ATLASSERT(pState); // state is required for async
  6845. if (IsAsyncContinueStatus(dwErr))
  6846. {
  6847. _pState->pIncludeInfo = pRequestInfo;
  6848. pRequestInfo->dwRequestState = ATLSRV_STATE_CONTINUE;
  6849. }
  6850. else if (IsAsyncDoneStatus(dwErr))
  6851. pRequest->pExtension->FreeRequest(pRequestInfo);
  6852. }
  6853. else
  6854. pRequest->pExtension->FreeRequest(pRequestInfo);
  6855. }
  6856. }
  6857. else
  6858. dwErr = HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
  6859. if (dwErr == HTTP_SUCCESS && bContinueAfterProcess)
  6860. return dwErr;
  6861. return HTTP_SUCCESS_NO_PROCESS;
  6862. }
  6863. //
  6864. // Used to terminate process when buffer security check fails
  6865. //
  6866. inline void __cdecl AtlsSecErrHandlerFunc(int nCode, void * /* pv */)
  6867. {
  6868. nCode;
  6869. #if defined(_M_IX86)
  6870. //
  6871. // only valid code
  6872. //
  6873. ATLASSERT( nCode == _SECERR_BUFFER_OVERRUN );
  6874. #endif
  6875. //
  6876. // a buffer overflow has occurred in your code
  6877. //
  6878. ATLASSERT( FALSE );
  6879. //
  6880. // terminate process (safest thing to do)
  6881. //
  6882. TerminateProcess( GetCurrentProcess(), 1 );
  6883. }
  6884. //
  6885. // Class CIsapiExtension
  6886. // The main ISAPI Extension implementation.
  6887. // Template parameters
  6888. // ThreadPoolClass: Specifies the thread pool that will be used by the
  6889. // extension to queue incoming requests. CThreadPool is the
  6890. // default and is declared and implemented in ATLUTIL.H. This class
  6891. // templatizes on a worker thread class. The worker thread class
  6892. // represents an abstraction of a thread that will be used to
  6893. // process requests as they are dequeued from the pool's work queue.
  6894. // You would change this parameter if you wanted to use a completely
  6895. // different thread pool, or, more commonly, if you wanted to use
  6896. // a different worker thread class. Request processing code can
  6897. // access a pointer to the worker thread class, which allows the
  6898. // request handling code to easily access per-thread data.
  6899. // CRequestStatClass: Specifies the class to be used to track request statistics
  6900. // CNoRequestStats is the default which is a noop class.
  6901. // You would change this parameter to provide a class that will
  6902. // track request statistics for you. ATL provides CStdRequestStats
  6903. // and CPerfRequestStatObject but these classes should be used
  6904. // with caution because they require interlocked operations to
  6905. // keep track of request statistics which can affect server performance.
  6906. // HttpUserErrorTextProvider: This class provides error text messages
  6907. // and headers, including resource IDs of error messages to the
  6908. // isapi extension's error handling functions. You would change this
  6909. // parameter if you wanted to provide your own error headers and/or
  6910. // messages in response to error encountered during request processing.
  6911. // WorkerThreadClass: The worker thread that will be used for this extension.
  6912. // The worker thread will be used to sweep any caches used by the extension
  6913. // and perform other periodic maintanence work while the extension is
  6914. // loaded. You would provide your own worker thread class to customize
  6915. // when the worker thread times out.
  6916. // CPageCacheStats, CStencilCacheStats: These two classes are used to keep
  6917. // statistics about the page and stencil caches. You could change these
  6918. // paramters if you wanted to track statistics for these caches. ATL
  6919. // provides CPerfStatClass and CStdStatClass to store the stat data but
  6920. // using these classes can affect server performance because they use
  6921. // interlocked operations internally to store the data.
  6922. template < class ThreadPoolClass=CThreadPool<CIsapiWorker>,
  6923. class CRequestStatClass=CNoRequestStats,
  6924. class HttpUserErrorTextProvider=CDefaultErrorProvider,
  6925. class WorkerThreadTraits=DefaultThreadTraits,
  6926. class CPageCacheStats=CNoStatClass,
  6927. class CStencilCacheStats=CNoStatClass>
  6928. class CIsapiExtension :
  6929. public IServiceProvider, public IIsapiExtension, public IRequestStats
  6930. {
  6931. #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
  6932. DWORD m_dwCriticalIsapiError;
  6933. #endif // ATL_NO_CRITICAL_ISAPI_ERROR
  6934. protected:
  6935. typedef CWorkerThread<WorkerThreadTraits> extWorkerType;
  6936. extWorkerType m_WorkerThread;
  6937. ThreadPoolClass m_ThreadPool;
  6938. CDllCache<extWorkerType, CDllCachePeer> m_DllCache;
  6939. CFileCache<extWorkerType, CPageCacheStats, CPageCachePeer> m_PageCache;
  6940. CComObjectGlobal<CStencilCache<extWorkerType, CStencilCacheStats > > m_StencilCache;
  6941. HttpUserErrorTextProvider m_UserErrorProvider;
  6942. HANDLE m_hRequestHeap;
  6943. CComCriticalSection m_critSec;
  6944. // Dynamic services stuff
  6945. struct ServiceNode
  6946. {
  6947. HINSTANCE hInst;
  6948. IUnknown *punk;
  6949. GUID guidService;
  6950. IID riid;
  6951. ServiceNode() throw()
  6952. {
  6953. }
  6954. ServiceNode(const ServiceNode& that) throw()
  6955. :hInst(that.hInst), punk(that.punk), guidService(that.guidService), riid(that.riid)
  6956. {
  6957. }
  6958. };
  6959. class CServiceEqualHelper
  6960. {
  6961. public:
  6962. static bool IsEqual(const ServiceNode& t1, const ServiceNode& t2) throw()
  6963. {
  6964. return (InlineIsEqualGUID(t1.guidService, t2.guidService) != 0 &&
  6965. InlineIsEqualGUID(t1.riid, t2.riid) != 0);
  6966. }
  6967. };
  6968. CSimpleArray<ServiceNode, CServiceEqualHelper> m_serviceMap;
  6969. public:
  6970. DWORD m_dwTlsIndex;
  6971. CWin32Heap m_heap;
  6972. CRequestStatClass m_reqStats;
  6973. AtlServerRequest *CreateRequest() throw()
  6974. {
  6975. // Allocate a fixed block size to avoid fragmentation
  6976. AtlServerRequest *pRequest = (AtlServerRequest *) HeapAlloc(m_hRequestHeap,
  6977. HEAP_ZERO_MEMORY, max(sizeof(AtlServerRequest), sizeof(_CComObjectHeap<CServerContext>)));
  6978. if (!pRequest)
  6979. return NULL;
  6980. pRequest->cbSize = sizeof(AtlServerRequest);
  6981. return pRequest;
  6982. }
  6983. void FreeRequest(AtlServerRequest *pRequest) throw()
  6984. {
  6985. _ReleaseAtlServerRequest(pRequest);
  6986. HeapFree(m_hRequestHeap, 0, pRequest);
  6987. }
  6988. CIsapiExtension() throw()
  6989. {
  6990. m_hRequestHeap = NULL;
  6991. #ifdef _DEBUG
  6992. m_bDebug = FALSE;
  6993. #endif
  6994. #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
  6995. m_dwCriticalIsapiError = 0;
  6996. #endif // ATL_NO_CRITICAL_ISAPI_ERROR
  6997. }
  6998. HTTP_CODE TransferRequest(
  6999. AtlServerRequest *pRequest,
  7000. IServiceProvider *pServiceProvider,
  7001. IWriteStream *pWriteStream,
  7002. IHttpRequestLookup *pLookup,
  7003. LPCSTR szNewUrl,
  7004. WORD nCodePage,
  7005. bool bContinueAfterProcess = false,
  7006. void *pState = NULL
  7007. ) throw(...)
  7008. {
  7009. return _AtlTransferRequest(pRequest, pServiceProvider, pWriteStream,
  7010. pLookup, szNewUrl, nCodePage, bContinueAfterProcess, pState);
  7011. }
  7012. #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
  7013. DWORD ReturnCriticalError(EXTENSION_CONTROL_BLOCK *pECB) throw()
  7014. {
  7015. UINT uResId = 0;
  7016. LPCSTR szHeader = NULL;
  7017. m_UserErrorProvider.GetErrorText(500,
  7018. ISE_SUBERR_ISAPISTARTUPFAILED,
  7019. &szHeader,
  7020. &uResId);
  7021. _ATLTRY
  7022. {
  7023. CStringA strStatus, strBody;
  7024. strStatus.Format("500 %s", szHeader);
  7025. if (uResId)
  7026. {
  7027. // load the body string from a resource
  7028. if (!strBody.LoadString(uResId))
  7029. {
  7030. strBody = "<html><body>A critical error has occurred initializing this ISAPI extension.</body></html>";
  7031. }
  7032. }
  7033. HSE_SEND_HEADER_EX_INFO hex;
  7034. hex.pszStatus = (LPCSTR)strStatus;
  7035. hex.pszHeader = NULL;
  7036. hex.cchStatus = (DWORD)strStatus.GetLength();
  7037. hex.cchHeader = 0;
  7038. hex.fKeepConn = FALSE;
  7039. pECB->ServerSupportFunction(pECB->ConnID,
  7040. HSE_REQ_SEND_RESPONSE_HEADER_EX,
  7041. &hex,
  7042. NULL,
  7043. NULL);
  7044. DWORD dwBodyLen = strBody.GetLength();
  7045. pECB->WriteClient(pECB->ConnID,
  7046. (void *) (LPCSTR) strBody,
  7047. &dwBodyLen,
  7048. NULL);
  7049. }
  7050. _ATLCATCHALL()
  7051. {
  7052. //REALLY BAD!
  7053. return HSE_STATUS_ERROR;
  7054. }
  7055. return HSE_STATUS_SUCCESS;
  7056. }
  7057. #endif // ATL_NO_CRITICAL_ISAPI_ERROR
  7058. DWORD HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) throw()
  7059. {
  7060. #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
  7061. if (GetCriticalIsapiError() != 0)
  7062. {
  7063. return ReturnCriticalError(lpECB);
  7064. }
  7065. #endif // ATL_NO_CRITICAL_ISAPI_ERROR
  7066. AtlServerRequest *pRequestInfo = NULL;
  7067. pRequestInfo = CreateRequest();
  7068. if (pRequestInfo == NULL)
  7069. return HSE_STATUS_ERROR;
  7070. CServerContext *pServerContext = NULL;
  7071. ATLTRY(pServerContext = CreateServerContext(m_hRequestHeap));
  7072. if (pServerContext == NULL)
  7073. {
  7074. FreeRequest(pRequestInfo);
  7075. return HSE_STATUS_ERROR;
  7076. }
  7077. pServerContext->Initialize(lpECB);
  7078. pServerContext->AddRef();
  7079. pRequestInfo->pServerContext = pServerContext;
  7080. pRequestInfo->dwRequestType = ATLSRV_REQUEST_UNKNOWN;
  7081. pRequestInfo->dwRequestState = ATLSRV_STATE_BEGIN;
  7082. pRequestInfo->pExtension = static_cast<IIsapiExtension *>(this);
  7083. pRequestInfo->pDllCache = static_cast<IDllCache *>(&m_DllCache);
  7084. #ifndef ATL_NO_MMSYS
  7085. pRequestInfo->dwStartTicks = timeGetTime();
  7086. #else
  7087. pRequestInfo->dwStartTicks = GetTickCount();
  7088. #endif
  7089. pRequestInfo->pECB = lpECB;
  7090. m_reqStats.OnRequestReceived();
  7091. if (m_ThreadPool.QueueRequest(pRequestInfo))
  7092. return HSE_STATUS_PENDING;
  7093. // QueueRequest failed
  7094. FreeRequest(pRequestInfo);
  7095. return HSE_STATUS_ERROR;
  7096. }
  7097. BOOL QueueRequest(AtlServerRequest * pRequestInfo) throw()
  7098. {
  7099. return m_ThreadPool.QueueRequest(pRequestInfo);
  7100. }
  7101. CIsapiWorker *GetThreadWorker() throw()
  7102. {
  7103. return (CIsapiWorker *) TlsGetValue(m_dwTlsIndex);
  7104. }
  7105. BOOL SetThreadWorker(CIsapiWorker *pWorker) throw()
  7106. {
  7107. return TlsSetValue(m_dwTlsIndex, (void*)pWorker);
  7108. }
  7109. // Configuration functions -- override in base class if another value is desired
  7110. virtual LPCSTR GetExtensionDesc() throw() { return "VC Server Classes"; }
  7111. virtual int GetNumPoolThreads() throw() { return 0; }
  7112. virtual int GetPoolStackSize() throw() { return 0; }
  7113. virtual HANDLE GetIOCompletionHandle() throw() { return INVALID_HANDLE_VALUE; }
  7114. virtual DWORD GetDllCacheTimeout() throw() { return ATL_DLL_CACHE_TIMEOUT; }
  7115. virtual DWORD GetStencilCacheTimeout() throw() { return ATL_STENCIL_CACHE_TIMEOUT; }
  7116. virtual LONGLONG GetStencilLifespan() throw() { return ATL_STENCIL_LIFESPAN; }
  7117. BOOL OnThreadAttach() throw()
  7118. {
  7119. return SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
  7120. }
  7121. void OnThreadTerminate() throw()
  7122. {
  7123. CoUninitialize();
  7124. }
  7125. #ifndef ATL_NO_CRITICAL_ISAPI_ERROR
  7126. BOOL SetCriticalIsapiError(DWORD dwErr = 1) throw()
  7127. {
  7128. m_dwCriticalIsapiError = dwErr;
  7129. return TRUE;
  7130. }
  7131. DWORD GetCriticalIsapiError() throw()
  7132. {
  7133. return m_dwCriticalIsapiError;
  7134. }
  7135. #else
  7136. BOOL SetCriticalIsapiError(DWORD dwErr = 1) throw()
  7137. {
  7138. return FALSE;
  7139. }
  7140. DWORD GetCriticalIsapiError() throw()
  7141. {
  7142. return 0;
  7143. }
  7144. #endif // ATL_NO_CRITICAL_ISAPI_ERROR
  7145. BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer) throw()
  7146. {
  7147. // allocate a Tls slot for storing per thread data
  7148. m_dwTlsIndex = TlsAlloc();
  7149. // create a private heap for request data
  7150. // this heap has to be thread safe to allow for
  7151. // async processing of requests
  7152. m_hRequestHeap = HeapCreate(0, 0, 0);
  7153. if (!m_hRequestHeap)
  7154. {
  7155. ATLTRACE(atlTraceISAPI, 0, _T("Failed creating request heap. Using process heap\n"));
  7156. m_hRequestHeap = GetProcessHeap();
  7157. if (!m_hRequestHeap)
  7158. {
  7159. return SetCriticalIsapiError();
  7160. }
  7161. }
  7162. // create a private heap (synchronized) for
  7163. // allocations. This reduces fragmentation overhead
  7164. // as opposed to the process heap
  7165. HANDLE hHeap = HeapCreate(0, 0, 0);
  7166. if (!hHeap)
  7167. {
  7168. ATLTRACE(atlTraceISAPI, 0, _T("Failed creating extension heap. Using process heap\n"));
  7169. hHeap = GetProcessHeap();
  7170. m_heap.Attach(hHeap, false);
  7171. }
  7172. else
  7173. {
  7174. m_heap.Attach(hHeap, true);
  7175. }
  7176. hHeap = NULL;
  7177. if (S_OK != m_reqStats.Initialize())
  7178. {
  7179. ATLTRACE(atlTraceISAPI,
  7180. 0,
  7181. _T("Initialization failed for request statistics perfmon support.\n")
  7182. _T("Check request statistics perfmon dll registration\n") );
  7183. return SetCriticalIsapiError();
  7184. }
  7185. if (S_OK != m_WorkerThread.Initialize())
  7186. {
  7187. return SetCriticalIsapiError();
  7188. }
  7189. if (m_critSec.Init() != S_OK)
  7190. {
  7191. m_WorkerThread.Shutdown();
  7192. return SetCriticalIsapiError();
  7193. }
  7194. if (S_OK != m_ThreadPool.Initialize(static_cast<IIsapiExtension*>(this), GetNumPoolThreads(), GetPoolStackSize(), GetIOCompletionHandle()))
  7195. {
  7196. m_WorkerThread.Shutdown();
  7197. m_critSec.Term();
  7198. return SetCriticalIsapiError();
  7199. }
  7200. if (FAILED(m_DllCache.Initialize(&m_WorkerThread, GetDllCacheTimeout())))
  7201. {
  7202. m_WorkerThread.Shutdown();
  7203. m_ThreadPool.Shutdown();
  7204. m_critSec.Term();
  7205. return SetCriticalIsapiError();
  7206. }
  7207. if (FAILED(m_PageCache.Initialize(&m_WorkerThread)))
  7208. {
  7209. m_WorkerThread.Shutdown();
  7210. m_ThreadPool.Shutdown();
  7211. m_DllCache.Uninitialize();
  7212. m_critSec.Term();
  7213. return SetCriticalIsapiError();
  7214. }
  7215. if (S_OK != m_StencilCache.Initialize(static_cast<IServiceProvider*>(this),
  7216. &m_WorkerThread,
  7217. GetStencilCacheTimeout(),
  7218. GetStencilLifespan()))
  7219. {
  7220. m_WorkerThread.Shutdown();
  7221. m_ThreadPool.Shutdown();
  7222. m_DllCache.Uninitialize();
  7223. m_PageCache.Uninitialize();
  7224. m_critSec.Term();
  7225. return SetCriticalIsapiError();
  7226. }
  7227. pVer->dwExtensionVersion = HSE_VERSION;
  7228. strcpy(pVer->lpszExtensionDesc, GetExtensionDesc());
  7229. return TRUE;
  7230. }
  7231. BOOL TerminateExtension(DWORD /*dwFlags*/) throw()
  7232. {
  7233. m_critSec.Lock();
  7234. for (int i=0; i < m_serviceMap.GetSize(); i++)
  7235. {
  7236. ATLASSERT(m_serviceMap[i].punk != NULL);
  7237. m_serviceMap[i].punk->Release();
  7238. m_DllCache.ReleaseModule(m_serviceMap[i].hInst);
  7239. }
  7240. m_critSec.Unlock();
  7241. m_ThreadPool.Shutdown();
  7242. m_StencilCache.Uninitialize();
  7243. m_DllCache.Uninitialize();
  7244. m_PageCache.Uninitialize();
  7245. m_WorkerThread.Shutdown();
  7246. m_reqStats.Uninitialize();
  7247. m_critSec.Term();
  7248. // free the request heap
  7249. if (m_hRequestHeap != GetProcessHeap())
  7250. HeapDestroy(m_hRequestHeap);
  7251. // free the Tls slot that we allocated
  7252. TlsFree(m_dwTlsIndex);
  7253. return TRUE;
  7254. }
  7255. static void WINAPI AsyncCallback(LPEXTENSION_CONTROL_BLOCK /*lpECB*/,
  7256. PVOID pContext,
  7257. DWORD cbIO,
  7258. DWORD dwError) throw(...)
  7259. {
  7260. ATLASSERT(pContext);
  7261. AtlServerRequest *pRequestInfo = reinterpret_cast<AtlServerRequest*>(pContext);
  7262. ATLASSERT(pRequestInfo);
  7263. if (pRequestInfo->m_hMutex)
  7264. {
  7265. // synchronize in case the previous async_noflush call isn't finished
  7266. // setting up state for the next call.
  7267. DWORD dwStatus = WaitForSingleObject(pRequestInfo->m_hMutex, ATLS_ASYNC_MUTEX_TIMEOUT);
  7268. if (dwStatus != WAIT_OBJECT_0 && dwStatus != WAIT_ABANDONED)
  7269. {
  7270. _ATLTRY
  7271. {
  7272. pRequestInfo->pExtension->RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
  7273. }
  7274. _ATLCATCHALL()
  7275. {
  7276. ATLTRACE(_T("Warning: Uncaught user exception thrown and caught in AsyncCallback.\n"));
  7277. _ATLRETHROW;
  7278. }
  7279. return;
  7280. }
  7281. }
  7282. if (pRequestInfo->pfnAsyncComplete != NULL)
  7283. ATLTRY((*pRequestInfo->pfnAsyncComplete)(pRequestInfo, cbIO, dwError));
  7284. if (pRequestInfo->dwRequestState == ATLSRV_STATE_DONE)
  7285. {
  7286. pRequestInfo->pExtension->RequestComplete(pRequestInfo, HTTP_ERROR_CODE(HTTP_SUCCESS), 0);
  7287. }
  7288. else if (pRequestInfo->dwRequestState == ATLSRV_STATE_CACHE_DONE)
  7289. {
  7290. CloseHandle(pRequestInfo->hFile);
  7291. pRequestInfo->pFileCache->ReleaseFile(pRequestInfo->hEntry);
  7292. pRequestInfo->pExtension->RequestComplete(pRequestInfo, HTTP_ERROR_CODE(HTTP_SUCCESS), 0);
  7293. }
  7294. else
  7295. {
  7296. HANDLE hMutex = pRequestInfo->m_hMutex;
  7297. pRequestInfo->pExtension->QueueRequest(pRequestInfo);
  7298. if (hMutex)
  7299. ReleaseMutex(hMutex);
  7300. }
  7301. }
  7302. void HandleError(IHttpServerContext *pServerContext, DWORD dwStatus, DWORD dwSubStatus) throw()
  7303. {
  7304. RenderError(pServerContext, dwStatus, dwSubStatus, &m_UserErrorProvider);
  7305. }
  7306. void RequestComplete(AtlServerRequest *pRequestInfo, DWORD dwStatus, DWORD dwSubStatus) throw(...)
  7307. {
  7308. ATLASSERT(pRequestInfo);
  7309. if (pRequestInfo->pHandler != NULL)
  7310. pRequestInfo->pHandler->UninitializeHandler();
  7311. DWORD dwReqStatus = dwStatus;
  7312. if (!dwReqStatus)
  7313. dwReqStatus = 200;
  7314. if (dwStatus >= 400)
  7315. {
  7316. if (dwSubStatus != SUBERR_NO_PROCESS)
  7317. HandleError(pRequestInfo->pServerContext, dwStatus, dwSubStatus);
  7318. m_reqStats.RequestHandled(pRequestInfo, FALSE);
  7319. }
  7320. else
  7321. m_reqStats.RequestHandled(pRequestInfo, TRUE);
  7322. CComPtr<IHttpServerContext> spServerContext = pRequestInfo->pServerContext;
  7323. FreeRequest(pRequestInfo);
  7324. spServerContext->DoneWithSession(dwReqStatus);
  7325. }
  7326. HTTP_CODE GetHandlerName(LPCSTR szFileName, LPSTR szHandlerName) throw()
  7327. {
  7328. return _AtlGetHandlerName(szFileName, szHandlerName);
  7329. }
  7330. HTTP_CODE LoadDispatchFile(LPCSTR szFileName, AtlServerRequest *pRequestInfo) throw()
  7331. {
  7332. CStencil *pStencil = NULL;
  7333. HCACHEITEM hStencil = NULL;
  7334. // Must have space for the path to the handler + the maximum size
  7335. // of the handler, plus the '/' plus the '\0'
  7336. CHAR szDllPath[MAX_PATH+1];
  7337. CHAR szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1];
  7338. pRequestInfo->pHandler = NULL;
  7339. pRequestInfo->hInstDll = NULL;
  7340. m_StencilCache.LookupStencil(szFileName, &hStencil);
  7341. // Stencil was found, check to see if it needs to be refreshed
  7342. if (hStencil)
  7343. {
  7344. m_StencilCache.GetStencil(hStencil, (void **) &pStencil);
  7345. pStencil->GetHandlerName(szDllPath, szHandlerName);
  7346. CFileTime cftCurr;
  7347. CFileTime cftLastChecked;
  7348. cftCurr = CFileTime::GetCurrentTime();
  7349. pStencil->GetLastChecked(&cftLastChecked);
  7350. CFileTimeSpan d(ATL_STENCIL_CHECK_TIMEOUT * CFileTime::Millisecond);
  7351. if (cftLastChecked + d < cftCurr)
  7352. {
  7353. CComPtr<IStencilCacheControl> spCacheCtrl;
  7354. m_StencilCache.QueryInterface(__uuidof(IStencilCacheControl), (void **)&spCacheCtrl);
  7355. if (spCacheCtrl)
  7356. {
  7357. CFileTime cftLastModified;
  7358. pStencil->GetLastModified(&cftLastModified);
  7359. // Resource based stencils have a last modified filetime of 0
  7360. if (cftLastModified != 0)
  7361. {
  7362. // for file base stencils, we check whether the file
  7363. // has been modified since being put in the cache
  7364. WIN32_FILE_ATTRIBUTE_DATA fad;
  7365. pStencil->SetLastChecked(&cftCurr);
  7366. BOOL bRet = GetFileAttributesExA(szFileName, GetFileExInfoStandard, &fad);
  7367. if ((bRet && cftLastModified < fad.ftLastWriteTime) ||
  7368. !bRet)
  7369. {
  7370. // the file has changed or an error has occurred trying to read the file,
  7371. // so remove it from the cache and force a reload
  7372. spCacheCtrl->RemoveStencil(hStencil);
  7373. pStencil = NULL;
  7374. hStencil = NULL;
  7375. }
  7376. }
  7377. }
  7378. }
  7379. }
  7380. if (!hStencil)
  7381. {
  7382. CHAR szHandlerDllName[MAX_PATH+ATL_MAX_HANDLER_NAME_LEN+2] = { '\0' };
  7383. // not in the cache, so open the file
  7384. HTTP_CODE hcErr = GetHandlerName(szFileName, szHandlerDllName);
  7385. if (hcErr)
  7386. return hcErr;
  7387. DWORD dwDllPathLen = MAX_PATH+1;
  7388. DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
  7389. if (!_AtlCrackHandler(szHandlerDllName, szDllPath, &dwDllPathLen, szHandlerName, &dwHandlerNameLen))
  7390. {
  7391. HTTP_ERROR(500, ISE_SUBERR_HANDLER_NOT_FOUND);
  7392. }
  7393. ATLASSERT(*szHandlerName);
  7394. ATLASSERT(*szDllPath);
  7395. if (!*szHandlerName)
  7396. return HTTP_ERROR(500, ISE_SUBERR_HANDLER_NOT_FOUND);
  7397. }
  7398. else
  7399. m_StencilCache.ReleaseStencil(hStencil);
  7400. return LoadRequestHandler(szDllPath, szHandlerName, pRequestInfo->pServerContext,
  7401. &pRequestInfo->hInstDll, &pRequestInfo->pHandler);
  7402. }
  7403. HTTP_CODE LoadDllHandler(LPCSTR szFileName, AtlServerRequest *pRequestInfo) throw()
  7404. {
  7405. _ATLTRY
  7406. {
  7407. HTTP_CODE hcErr = HTTP_SUCCESS;
  7408. CHAR szHandler[ATL_MAX_HANDLER_NAME_LEN+1] = { 'D', 'e', 'f', 'a', 'u', 'l', 't', '\0' };
  7409. LPCSTR szQueryString = pRequestInfo->pServerContext->GetQueryString();
  7410. if (szQueryString != NULL)
  7411. {
  7412. LPCSTR szHdlr = strstr(szQueryString, "Handler=");
  7413. if (szHdlr != NULL)
  7414. {
  7415. if ((szHdlr == szQueryString) ||
  7416. ((szHdlr > szQueryString) && (*(szHdlr-1) == '&')))
  7417. {
  7418. int nCnt = 0;
  7419. LPSTR pszHandler = szHandler;
  7420. szHdlr += sizeof("Handler=")-1;
  7421. while (*szHdlr && *szHdlr != '&')
  7422. {
  7423. if (nCnt < ATL_MAX_HANDLER_NAME_LEN)
  7424. {
  7425. *pszHandler++ = *szHdlr++;
  7426. nCnt++;
  7427. }
  7428. else
  7429. {
  7430. hcErr = HTTP_ERROR(500, ISE_SUBERR_HANDLER_NOT_FOUND);
  7431. break;
  7432. }
  7433. }
  7434. if (hcErr == HTTP_SUCCESS)
  7435. {
  7436. *pszHandler = '\0';
  7437. }
  7438. }
  7439. }
  7440. }
  7441. if (hcErr == HTTP_SUCCESS)
  7442. {
  7443. CHAR szFile[_MAX_PATH+ATL_MAX_HANDLER_NAME_LEN+1];
  7444. strcpy(szFile, szFileName);
  7445. hcErr = LoadRequestHandler(szFile, szHandler, pRequestInfo->pServerContext, &pRequestInfo->hInstDll, &pRequestInfo->pHandler);
  7446. }
  7447. return hcErr;
  7448. }
  7449. _ATLCATCHALL()
  7450. {
  7451. return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
  7452. }
  7453. }
  7454. BOOL TransmitFromCache(AtlServerRequest* pRequestInfo) throw()
  7455. {
  7456. if (strcmp(pRequestInfo->pServerContext->GetRequestMethod(), "GET"))
  7457. return FALSE;
  7458. char szUrl[ATL_URL_MAX_URL_LENGTH + 1];
  7459. LPCSTR szPathInfo = pRequestInfo->pServerContext->GetPathInfo();
  7460. LPCSTR szQueryString = pRequestInfo->pServerContext->GetQueryString();
  7461. LPSTR szTo = szUrl;
  7462. while (*szPathInfo)
  7463. {
  7464. *szTo++ = *szPathInfo++;
  7465. }
  7466. *szTo++ = '?';
  7467. while (*szQueryString)
  7468. {
  7469. *szTo++ = *szQueryString++;
  7470. }
  7471. *szTo = '\0';
  7472. HCACHEITEM hEntry;
  7473. if (S_OK == m_PageCache.LookupFile(szUrl, &hEntry))
  7474. {
  7475. LPSTR szFileName;
  7476. CPageCachePeer::PeerInfo *pInfo;
  7477. m_PageCache.GetFile(hEntry, &szFileName, (void **)&pInfo);
  7478. CAtlFile file;
  7479. HRESULT hr = E_FAIL;
  7480. _ATLTRY
  7481. {
  7482. CA2CTEX<MAX_PATH+1> strFile(szFileName);
  7483. hr = file.Create(strFile,
  7484. GENERIC_READ,
  7485. FILE_SHARE_READ,
  7486. OPEN_EXISTING,
  7487. FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED);
  7488. }
  7489. _ATLCATCHALL()
  7490. {
  7491. return FALSE;
  7492. }
  7493. if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
  7494. return FALSE;
  7495. pRequestInfo->pServerContext->SendResponseHeader(
  7496. (LPCSTR)pInfo->strHeader, (LPCSTR)pInfo->strStatus, FALSE);
  7497. HANDLE hFile = file.Detach();
  7498. BOOL bRet = FALSE;
  7499. pRequestInfo->dwRequestState = ATLSRV_STATE_CACHE_DONE;
  7500. pRequestInfo->hFile = hFile;
  7501. pRequestInfo->hEntry = hEntry;
  7502. pRequestInfo->pFileCache = &m_PageCache;
  7503. bRet = pRequestInfo->pServerContext->TransmitFile(
  7504. hFile, // The file to transmit
  7505. AsyncCallback, pRequestInfo, // The async callback and context
  7506. pInfo->strStatus, // HTTP status code
  7507. 0, // Send entire file
  7508. 0, // Start at the beginning of the file
  7509. NULL, 0, // Head and length
  7510. NULL, 0, // Tail and length
  7511. HSE_IO_ASYNC | HSE_IO_DISCONNECT_AFTER_SEND // Send asynchronously
  7512. );
  7513. if (!bRet)
  7514. {
  7515. m_PageCache.ReleaseFile(hEntry);
  7516. CloseHandle(hFile);
  7517. }
  7518. return TRUE;
  7519. }
  7520. else
  7521. return FALSE;
  7522. }
  7523. #ifdef _DEBUG
  7524. BOOL m_bDebug;
  7525. // F5 debugging support for VS7
  7526. BOOL ProcessDebug(AtlServerRequest *pRequestInfo) throw()
  7527. {
  7528. _ATLTRY
  7529. {
  7530. if (!_stricmp(pRequestInfo->pServerContext->GetRequestMethod(), "debug"))
  7531. {
  7532. DWORD dwHeadersLen = 0;
  7533. CStringA strHeaders;
  7534. pRequestInfo->pServerContext->GetServerVariable("ALL_HTTP", NULL, &dwHeadersLen);
  7535. BOOL bRet = pRequestInfo->pServerContext->GetServerVariable("ALL_HTTP", strHeaders.GetBuffer(dwHeadersLen), &dwHeadersLen);
  7536. if (!bRet)
  7537. {
  7538. RequestComplete(pRequestInfo, 501, 0);
  7539. return FALSE;
  7540. }
  7541. strHeaders.ReleaseBuffer(dwHeadersLen - 1);
  7542. LPCSTR szCur = (LPCSTR)strHeaders;
  7543. while(*szCur)
  7544. {
  7545. if (!strncmp(szCur, "HTTP_COMMAND:", 13))
  7546. {
  7547. szCur += 13;
  7548. break;
  7549. }
  7550. szCur = strchr(szCur, '\n');
  7551. if (!szCur)
  7552. {
  7553. RequestComplete(pRequestInfo, 501, 0);
  7554. return FALSE;
  7555. }
  7556. szCur++;
  7557. }
  7558. if (!_strnicmp(szCur, "start-debug", sizeof("start-debug")-sizeof('\0')))
  7559. {
  7560. CCritSecLock Lock(m_critSec.m_sec);
  7561. if (m_bDebug)
  7562. {
  7563. HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_ALREADY_DEBUGGING);
  7564. RequestComplete(pRequestInfo, 204, DBG_SUBERR_ALREADY_DEBUGGING); // Already being debugged by another process
  7565. return FALSE;
  7566. }
  7567. CHttpRequest HttpRequest;
  7568. HttpRequest.Initialize(pRequestInfo->pServerContext);
  7569. HttpRequest.InitFromPost();
  7570. LPCSTR szString;
  7571. szString = HttpRequest.FormVars.Lookup("DebugSessionID");
  7572. if (!szString || !*szString)
  7573. {
  7574. HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_INVALID_SESSION);
  7575. RequestComplete(pRequestInfo, 204, DBG_SUBERR_INVALID_SESSION);
  7576. return FALSE;
  7577. }
  7578. CA2W szSessionID(szString);
  7579. if (!szSessionID)
  7580. {
  7581. HandleError(pRequestInfo->pServerContext, 500, ISE_SUBERR_OUTOFMEM);
  7582. RequestComplete(pRequestInfo, 500, ISE_SUBERR_OUTOFMEM);
  7583. return FALSE;
  7584. }
  7585. DWORD dwPid = GetCurrentProcessId();
  7586. LPWSTR szPoint = szSessionID;
  7587. while (szPoint && *szPoint && wcsncmp(szPoint, L"autoattachclsid=", 16))
  7588. {
  7589. szPoint = wcschr(szPoint, ';');
  7590. if (szPoint)
  7591. szPoint++;
  7592. }
  7593. if (!szPoint || !*szPoint)
  7594. {
  7595. HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_BAD_ID);
  7596. RequestComplete(pRequestInfo, 204, DBG_SUBERR_BAD_ID);
  7597. return FALSE;
  7598. }
  7599. szPoint += (sizeof("autoattachclsid=") - 1);
  7600. WCHAR szClsid[39];
  7601. wcsncpy(szClsid, szPoint, 38);
  7602. szClsid[38] = '\0';
  7603. CLSID clsidDebugAutoAttach = CLSID_NULL;
  7604. HRESULT hr = CLSIDFromString(szClsid, &clsidDebugAutoAttach);
  7605. if (hr != S_OK)
  7606. {
  7607. HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_BAD_ID);
  7608. RequestComplete(pRequestInfo, 204, DBG_SUBERR_BAD_ID);
  7609. return FALSE;
  7610. }
  7611. CComPtr<IDebugAutoAttach> spDebugAutoAttach;
  7612. hr = CoCreateInstance(clsidDebugAutoAttach, NULL, CLSCTX_LOCAL_SERVER, __uuidof(IDebugAutoAttach), (void**)&spDebugAutoAttach);
  7613. if (FAILED(hr))
  7614. {
  7615. if (hr == E_ACCESSDENIED)
  7616. RequestComplete(pRequestInfo, 401, 0);
  7617. else
  7618. {
  7619. HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_COCREATE);
  7620. RequestComplete(pRequestInfo, 204, DBG_SUBERR_COCREATE);
  7621. }
  7622. return FALSE;
  7623. }
  7624. hr = spDebugAutoAttach->AutoAttach(GUID_NULL, dwPid, AUTOATTACH_PROGRAM_WIN32, 0, szSessionID);
  7625. if (FAILED(hr))
  7626. {
  7627. // HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_ATTACH);
  7628. char szRetBuf[256];
  7629. DWORD dwLen = wsprintfA(szRetBuf, "204 HRESULT=0x%.08X;ErrorString=Unable to attach to worker process", hr);
  7630. pRequestInfo->pServerContext->SendResponseHeader(NULL, szRetBuf, FALSE);
  7631. pRequestInfo->pServerContext->WriteClient(szRetBuf, &dwLen);
  7632. RequestComplete(pRequestInfo, 204, DBG_SUBERR_ATTACH);
  7633. return FALSE;
  7634. }
  7635. m_bDebug = TRUE;
  7636. HandleError(pRequestInfo->pServerContext, 200, SUBERR_NONE);
  7637. RequestComplete(pRequestInfo, 200, SUBERR_NONE);
  7638. return FALSE;
  7639. }
  7640. else if (!_strnicmp(szCur, "stop-debug", sizeof("stop-debug")-sizeof('\0')))
  7641. {
  7642. m_bDebug = FALSE;
  7643. HandleError(pRequestInfo->pServerContext, 200, SUBERR_NONE);
  7644. RequestComplete(pRequestInfo, 200, SUBERR_NONE);
  7645. return FALSE;
  7646. }
  7647. else
  7648. {
  7649. RequestComplete(pRequestInfo, 501, SUBERR_NONE); // Not Implemented
  7650. return FALSE;
  7651. }
  7652. }
  7653. return TRUE;
  7654. }
  7655. _ATLCATCHALL()
  7656. {
  7657. return FALSE;
  7658. }
  7659. }
  7660. #endif
  7661. BOOL DispatchStencilCall(AtlServerRequest *pRequestInfo) throw(...)
  7662. {
  7663. CSetThreadToken sec;
  7664. m_reqStats.OnRequestDequeued();
  7665. if (!sec.Initialize(pRequestInfo))
  7666. {
  7667. RequestComplete(pRequestInfo, 500, ISE_SUBERR_IMPERSONATIONFAILED);
  7668. return FALSE;
  7669. }
  7670. #ifdef _DEBUG
  7671. if (!ProcessDebug(pRequestInfo))
  7672. return TRUE;
  7673. #endif
  7674. if (pRequestInfo->m_hMutex)
  7675. {
  7676. // synchronize in case the previous async_noflush call isn't finished
  7677. // setting up state for the next call.
  7678. DWORD dwStatus = WaitForSingleObject(pRequestInfo->m_hMutex, ATLS_ASYNC_MUTEX_TIMEOUT);
  7679. if (dwStatus != WAIT_OBJECT_0 && dwStatus != WAIT_ABANDONED)
  7680. {
  7681. RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
  7682. return FALSE;
  7683. }
  7684. }
  7685. #ifdef _DEBUG
  7686. bool bAsyncAllowed = false;
  7687. #endif
  7688. HTTP_CODE hcErr = HTTP_SUCCESS;
  7689. if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN)
  7690. {
  7691. if (TransmitFromCache(pRequestInfo)) // Page is in the cache, send it and bail
  7692. { // Async Callback will handle freeing pRequestInfo
  7693. return TRUE;
  7694. }
  7695. // get the srf filename
  7696. LPCSTR szFileName = pRequestInfo->pServerContext->GetScriptPathTranslated();
  7697. if (!szFileName)
  7698. {
  7699. RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
  7700. return FALSE;
  7701. }
  7702. LPCSTR szDot = szFileName + strlen(szFileName) - 1;
  7703. // load a handler
  7704. if (_stricmp(szDot - ATLS_EXTENSION_LEN, c_AtlSRFExtension) == 0)
  7705. {
  7706. pRequestInfo->dwRequestType = ATLSRV_REQUEST_STENCIL;
  7707. hcErr = LoadDispatchFile(szFileName, pRequestInfo);
  7708. }
  7709. else if (_stricmp(szDot - 3, ".dll") == 0)
  7710. {
  7711. pRequestInfo->dwRequestType = ATLSRV_REQUEST_DLL;
  7712. hcErr = LoadDllHandler(szFileName, pRequestInfo);
  7713. }
  7714. else
  7715. {
  7716. hcErr = HTTP_FAIL;
  7717. }
  7718. if (hcErr)
  7719. {
  7720. RequestComplete(pRequestInfo, HTTP_ERROR_CODE(hcErr), HTTP_SUBERROR_CODE(hcErr));
  7721. return TRUE;
  7722. }
  7723. pRequestInfo->pfnHandleRequest = &IRequestHandler::HandleRequest;
  7724. // initialize the handler
  7725. DWORD dwStatus = 0;
  7726. hcErr = pRequestInfo->pHandler->GetFlags(&dwStatus);
  7727. if (dwStatus & ATLSRV_INIT_USECACHE &&
  7728. !strcmp(pRequestInfo->pServerContext->GetRequestMethod(), "GET"))
  7729. {
  7730. CComObjectNoLock<CCacheServerContext> *pCacheServerContext = NULL;
  7731. ATLTRY(pCacheServerContext = new CComObjectNoLock<CCacheServerContext>);
  7732. if (pCacheServerContext == NULL)
  7733. {
  7734. RequestComplete(pRequestInfo, 500, ISE_SUBERR_OUTOFMEM);
  7735. return FALSE;
  7736. }
  7737. pCacheServerContext->Initialize(pRequestInfo->pServerContext, &m_PageCache);
  7738. pCacheServerContext->AddRef();
  7739. pRequestInfo->pServerContext = pCacheServerContext;
  7740. }
  7741. if (dwStatus & (ATLSRV_INIT_USEASYNC | ATLSRV_INIT_USEASYNC_EX))
  7742. {
  7743. #ifdef _DEBUG
  7744. bAsyncAllowed = true;
  7745. #endif
  7746. if (!pRequestInfo->pServerContext->RequestIOCompletion(AsyncCallback, (DWORD *)pRequestInfo))
  7747. {
  7748. RequestComplete(pRequestInfo, 500, SUBERR_NONE);
  7749. return FALSE;
  7750. }
  7751. }
  7752. if (dwStatus & ATLSRV_INIT_USEASYNC_EX)
  7753. {
  7754. pRequestInfo->m_hMutex = CreateMutex(NULL, FALSE, NULL);
  7755. if (pRequestInfo->m_hMutex == NULL)
  7756. {
  7757. RequestComplete(pRequestInfo, 500, ISE_SUBERR_SYSOBJFAIL);
  7758. return FALSE;
  7759. }
  7760. DWORD dwStatus = WaitForSingleObject(pRequestInfo->m_hMutex, 10000);
  7761. if (dwStatus != WAIT_OBJECT_0 && dwStatus != WAIT_ABANDONED)
  7762. {
  7763. RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
  7764. return FALSE;
  7765. }
  7766. }
  7767. hcErr = pRequestInfo->pHandler->InitializeHandler(pRequestInfo, static_cast<IServiceProvider*>(this));
  7768. }
  7769. #ifdef _DEBUG
  7770. else // pRequestInfo->dwRequestState != ATLSRV_STATE_BEGIN
  7771. {
  7772. bAsyncAllowed = true;
  7773. }
  7774. #endif
  7775. ATLASSERT(pRequestInfo->pfnHandleRequest != NULL);
  7776. if (hcErr == HTTP_SUCCESS)
  7777. hcErr = (pRequestInfo->pHandler->*pRequestInfo->pfnHandleRequest)(pRequestInfo, static_cast<IServiceProvider*>(this));
  7778. if (hcErr == HTTP_SUCCESS_NO_CACHE)
  7779. {
  7780. CComPtr<IPageCacheControl> spControl;
  7781. HRESULT hr = pRequestInfo->pServerContext->QueryInterface(__uuidof(IPageCacheControl), (void **)&spControl);
  7782. if (hr == S_OK)
  7783. spControl->Cache(FALSE);
  7784. }
  7785. #ifdef _DEBUG
  7786. // must use ATLSRV_INIT_USEASYNC to use ASYNC returns
  7787. if (IsAsyncStatus(hcErr))
  7788. ATLASSERT(bAsyncAllowed);
  7789. // must use ATLSRV_INIT_USEASYNC_EX to use NOFLUSH returns
  7790. if (IsAsyncNoFlushStatus(hcErr))
  7791. ATLASSERT(pRequestInfo->m_hMutex);
  7792. #endif
  7793. // save hMutex in case pRequestInfo is deleted by AsyncCallback after
  7794. // we call StartAsyncFlush but before we check to see if we need to
  7795. // call ReleaseMutex
  7796. HANDLE hMutex = pRequestInfo->m_hMutex;
  7797. if (IsAsyncStatus(hcErr))
  7798. {
  7799. if (IsAsyncDoneStatus(hcErr))
  7800. pRequestInfo->dwRequestState = ATLSRV_STATE_DONE;
  7801. else
  7802. pRequestInfo->dwRequestState = ATLSRV_STATE_CONTINUE;
  7803. if (IsAsyncFlushStatus(hcErr) && !StartAsyncFlush(pRequestInfo))
  7804. {
  7805. RequestComplete(pRequestInfo, 500, SUBERR_NONE);
  7806. pRequestInfo = NULL;
  7807. }
  7808. }
  7809. else
  7810. {
  7811. RequestComplete(pRequestInfo, HTTP_ERROR_CODE(hcErr), HTTP_SUBERROR_CODE(hcErr));
  7812. pRequestInfo = NULL;
  7813. }
  7814. if (hMutex)
  7815. ReleaseMutex(hMutex);
  7816. return TRUE;
  7817. }
  7818. BOOL StartAsyncFlush(AtlServerRequest *pRequestInfo) throw()
  7819. {
  7820. if (pRequestInfo->pszBuffer == NULL || pRequestInfo->dwBufferLen == 0)
  7821. {
  7822. ATLASSERT(FALSE);
  7823. return FALSE;
  7824. }
  7825. return pRequestInfo->pServerContext->AsyncWriteClient(
  7826. LPVOID(pRequestInfo->pszBuffer),
  7827. &pRequestInfo->dwBufferLen);
  7828. }
  7829. long GetTotalRequests() throw()
  7830. {
  7831. return m_reqStats.GetTotalRequests();
  7832. }
  7833. long GetFailedRequests() throw()
  7834. {
  7835. return m_reqStats.GetFailedRequests();
  7836. }
  7837. long GetAvgResponseTime() throw()
  7838. {
  7839. return m_reqStats.GetAvgResponseTime();
  7840. }
  7841. long GetCurrWaiting() throw()
  7842. {
  7843. return m_reqStats.GetCurrWaiting();
  7844. }
  7845. long GetMaxWaiting() throw()
  7846. {
  7847. return m_reqStats.GetMaxWaiting();
  7848. }
  7849. long GetActiveThreads() throw()
  7850. {
  7851. return m_reqStats.GetActiveThreads();
  7852. }
  7853. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
  7854. {
  7855. if (!ppv)
  7856. return E_POINTER;
  7857. if (InlineIsEqualGUID(riid, __uuidof(IRequestStats)))
  7858. {
  7859. *ppv = static_cast<IUnknown*>(static_cast<IRequestStats*>(this));
  7860. AddRef();
  7861. return S_OK;
  7862. }
  7863. if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
  7864. InlineIsEqualGUID(riid, __uuidof(IServiceProvider)))
  7865. {
  7866. *ppv = static_cast<IUnknown*>(static_cast<IServiceProvider*>(this));
  7867. AddRef();
  7868. return S_OK;
  7869. }
  7870. if (InlineIsEqualGUID(riid, __uuidof(IIsapiExtension)))
  7871. {
  7872. *ppv = static_cast<IUnknown*>(static_cast<IIsapiExtension*>(this));
  7873. AddRef();
  7874. return S_OK;
  7875. }
  7876. return E_NOINTERFACE;
  7877. }
  7878. ULONG STDMETHODCALLTYPE AddRef() throw()
  7879. {
  7880. return 1;
  7881. }
  7882. ULONG STDMETHODCALLTYPE Release() throw()
  7883. {
  7884. return 1;
  7885. }
  7886. virtual HRESULT STDMETHODCALLTYPE QueryService(
  7887. REFGUID guidService,
  7888. REFIID riid,
  7889. void **ppvObject) throw()
  7890. {
  7891. if (!ppvObject)
  7892. return E_POINTER;
  7893. if (InlineIsEqualGUID(guidService, __uuidof(IDllCache)))
  7894. return m_DllCache.QueryInterface(riid, ppvObject);
  7895. else if (InlineIsEqualGUID(guidService, __uuidof(IStencilCache)))
  7896. return m_StencilCache.QueryInterface(riid, ppvObject);
  7897. else if (InlineIsEqualGUID(guidService, __uuidof(IThreadPoolConfig)))
  7898. return m_ThreadPool.QueryInterface(riid, ppvObject);
  7899. else if (InlineIsEqualGUID(guidService, __uuidof(IAtlMemMgr)))
  7900. {
  7901. *ppvObject = static_cast<IAtlMemMgr *>(&m_heap);
  7902. return S_OK;
  7903. }
  7904. #ifndef ATL_NO_SOAP
  7905. else if (InlineIsEqualGUID(guidService, __uuidof(ISAXXMLReader)))
  7906. {
  7907. CIsapiWorker *p = GetThreadWorker();
  7908. ATLASSERT( p != NULL );
  7909. return p->m_spReader->QueryInterface(riid, ppvObject);
  7910. }
  7911. #endif
  7912. // otherwise look it up in the servicemap
  7913. return GetService(guidService, riid, ppvObject);
  7914. }
  7915. virtual HRESULT AddService(REFGUID guidService, REFIID riid, IUnknown *punkService, HINSTANCE hInstance) throw()
  7916. {
  7917. if (!m_DllCache.AddRefModule(hInstance))
  7918. return E_FAIL;
  7919. if (!punkService)
  7920. return E_INVALIDARG;
  7921. CCritSecLock Lock(m_critSec.m_sec);
  7922. ServiceNode srvNode;
  7923. srvNode.hInst = hInstance;
  7924. srvNode.punk = punkService;
  7925. memcpy(&srvNode.guidService, &guidService, sizeof(guidService));
  7926. memcpy(&srvNode.riid, &riid, sizeof(riid));
  7927. // if the service is already there, return S_FALSE
  7928. int nIndex = m_serviceMap.Find(srvNode);
  7929. if (nIndex >= 0)
  7930. return S_FALSE;
  7931. if (!m_serviceMap.Add(srvNode))
  7932. return E_OUTOFMEMORY;
  7933. punkService->AddRef();
  7934. return S_OK;
  7935. }
  7936. virtual HRESULT RemoveService(REFGUID guidService, REFIID riid) throw()
  7937. {
  7938. CCritSecLock Lock(m_critSec.m_sec);
  7939. ServiceNode srvNode;
  7940. memcpy(&srvNode.guidService, &guidService, sizeof(guidService));
  7941. memcpy(&srvNode.riid, &riid, sizeof(riid));
  7942. int nIndex = m_serviceMap.Find(srvNode);
  7943. if (nIndex < 0)
  7944. return S_FALSE;
  7945. ATLASSERT(m_serviceMap[nIndex].punk != NULL);
  7946. m_serviceMap[nIndex].punk->Release();
  7947. HINSTANCE hInstRemove = m_serviceMap[nIndex].hInst;
  7948. m_serviceMap.RemoveAt(nIndex);
  7949. if (!m_DllCache.ReleaseModule(hInstRemove))
  7950. return S_FALSE;
  7951. return S_OK;
  7952. }
  7953. HRESULT GetService(REFGUID guidService, REFIID riid, void **ppvObject) throw()
  7954. {
  7955. if (!ppvObject)
  7956. return E_POINTER;
  7957. *ppvObject = NULL;
  7958. if (!m_serviceMap.GetSize())
  7959. return E_NOINTERFACE;
  7960. ServiceNode srvNode;
  7961. memcpy(&srvNode.guidService, &guidService, sizeof(guidService));
  7962. memcpy(&srvNode.riid, &riid, sizeof(riid));
  7963. CCritSecLock Lock(m_critSec.m_sec);
  7964. int nIndex = m_serviceMap.Find(srvNode);
  7965. if (nIndex < 0)
  7966. return E_NOINTERFACE;
  7967. ATLASSERT(m_serviceMap[nIndex].punk != NULL);
  7968. return m_serviceMap[nIndex].punk->QueryInterface(riid, ppvObject);
  7969. }
  7970. HTTP_CODE LoadRequestHandler(LPCSTR szDllPath, LPCSTR szHandlerName, IHttpServerContext *pServerContext,
  7971. HINSTANCE *phInstance, IRequestHandler **ppHandler) throw()
  7972. {
  7973. return _AtlLoadRequestHandler(szDllPath, szHandlerName, pServerContext,
  7974. phInstance, ppHandler, this, static_cast<IDllCache*>(&m_DllCache));
  7975. } // LoadRequestHandler
  7976. }; // class CIsapiExtension
  7977. //===========================================================================================
  7978. // IMPORTANT NOTE TO USERS:
  7979. // DO NOT ASSUME *ANYTHING* ABOUT THE STRUCTURE OF THESE MAPS/ENTRIES/FUNCTIONS--THEY CAN
  7980. // AND *WILL* CHANGE IN THE FUTURE. CORRECT USAGE MANDATES THAT YOU USE THE MACROS PROVIDED.
  7981. // ABSOLUTELY NO GUARANTEES ABOUT BACKWARD COMPATABILITY ARE MADE FOR MANUALLY DEFINED
  7982. // HANDLERS OR FUNCTIONS.
  7983. //===========================================================================================
  7984. typedef BOOL (*CREATEHANDLERFUNC)(IIsapiExtension *pExtension, IUnknown **ppOut);
  7985. typedef BOOL (*INITHANDLERFUNC)(IHttpServerContext*, IIsapiExtension*);
  7986. typedef void (*UNINITHANDLERFUNC)();
  7987. struct _HANDLER_ENTRY
  7988. {
  7989. LPCSTR szName;
  7990. CREATEHANDLERFUNC pfnCreate;
  7991. INITHANDLERFUNC pfnInit;
  7992. UNINITHANDLERFUNC pfnUninit;
  7993. };
  7994. // definitions of data segments and _HANDLER_ENTRY delimiters
  7995. #pragma data_seg(push)
  7996. #pragma data_seg("ATLS$A")
  7997. __declspec(selectany) ATL::_HANDLER_ENTRY * __phdlrA = NULL;
  7998. #pragma data_seg("ATLS$Z")
  7999. __declspec(selectany) ATL::_HANDLER_ENTRY * __phdlrZ = NULL;
  8000. #pragma data_seg("ATLS$C")
  8001. #pragma data_seg(pop)
  8002. #ifndef HANDLER_ENTRY_PRAGMA
  8003. #if defined(_M_IX86)
  8004. #define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:___phdlrEntry_" #class "_" #line))
  8005. #elif defined(_M_AMD64) || defined(_M_IA64)
  8006. #define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:__phdlrEntry_" #class "_" #line))
  8007. #else
  8008. #error Unknown Platform. define HANDLER_ENTRY_PRAGMA
  8009. #endif
  8010. #endif // HANDLER_ENTRY_PRAGMA
  8011. // DECLARE_REQUEST_HANDLER macro
  8012. #define __DECLARE_REQUEST_HANDLER_INTERNAL(handlerName, className, lineNum) \
  8013. __declspec(selectany) ATL::_HANDLER_ENTRY __hdlrEntry_ ## className ## _ ## lineNum = { handlerName, className::CreateRequestHandler, className::InitRequestHandlerClass, className::UninitRequestHandlerClass }; \
  8014. extern "C" __declspec(allocate("ATLS$C")) __declspec(selectany) \
  8015. ATL::_HANDLER_ENTRY * __phdlrEntry_ ## className ## _ ## lineNum = &__hdlrEntry_ ## className ## _ ## lineNum; \
  8016. HANDLER_ENTRY_PRAGMA(className, lineNum) \
  8017. __if_not_exists(GetAtlHandlerByName) \
  8018. { \
  8019. extern "C" ATL_NOINLINE inline BOOL __declspec(dllexport) __stdcall GetAtlHandlerByName(LPCSTR szHandlerName, IIsapiExtension *pExtension, IUnknown **ppHandler) throw() \
  8020. { \
  8021. *ppHandler = NULL; \
  8022. ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
  8023. while (pEntry != &__phdlrZ) \
  8024. { \
  8025. if (*pEntry && (*pEntry)->szName) \
  8026. { \
  8027. if (strcmp((*pEntry)->szName, szHandlerName)==0) \
  8028. { \
  8029. return (*(*pEntry)->pfnCreate)(pExtension, ppHandler); \
  8030. } \
  8031. } \
  8032. pEntry++; \
  8033. } \
  8034. return FALSE; \
  8035. } \
  8036. extern "C" ATL_NOINLINE inline BOOL __declspec(dllexport) __stdcall InitializeAtlHandlers(IHttpServerContext *pContext, IIsapiExtension *pExt) throw() \
  8037. { \
  8038. ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
  8039. BOOL bRet = TRUE; \
  8040. while (pEntry != &__phdlrZ) \
  8041. { \
  8042. if (*pEntry && (*pEntry)->szName && (*pEntry)->pfnInit) \
  8043. { \
  8044. bRet = (*(*pEntry)->pfnInit)(pContext, pExt); \
  8045. if (!bRet) \
  8046. break; \
  8047. } \
  8048. pEntry++; \
  8049. } \
  8050. if (!bRet) \
  8051. { \
  8052. if (pEntry == &__phdlrA) \
  8053. return FALSE; \
  8054. do \
  8055. { \
  8056. pEntry--; \
  8057. (*(*pEntry)->pfnUninit)(); \
  8058. } \
  8059. while (pEntry != &__phdlrA); \
  8060. } \
  8061. return bRet; \
  8062. } \
  8063. extern "C" ATL_NOINLINE inline void __declspec(dllexport) __stdcall UninitializeAtlHandlers() throw() \
  8064. {\
  8065. ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
  8066. while (pEntry != &__phdlrZ) \
  8067. { \
  8068. if (*pEntry && (*pEntry)->szName && (*pEntry)->pfnUninit) \
  8069. { \
  8070. (*(*pEntry)->pfnUninit)(); \
  8071. } \
  8072. pEntry++; \
  8073. } \
  8074. } \
  8075. }
  8076. // TODO (jasjitg): When __COUNTER__ becomes available, replace __LINE__ with that
  8077. #define __DECLARE_REQUEST_HANDLER(handlerName, className, lineNum) __DECLARE_REQUEST_HANDLER_INTERNAL(handlerName, className, lineNum)
  8078. #define DECLARE_REQUEST_HANDLER(handlerName, className) __DECLARE_REQUEST_HANDLER(handlerName, className, __COUNTER__)
  8079. #define BEGIN_HANDLER_MAP()
  8080. #define HANDLER_ENTRY(handlerName, className) DECLARE_REQUEST_HANDLER(handlerName, className)
  8081. #define END_HANDLER_MAP()
  8082. #define HANDLER_ENTRY_SDL(handlerString, handlerClass, sdlClassName)\
  8083. __declspec(selectany) LPCSTR s_szClassName##handlerClass=handlerString;\
  8084. typedef CSDLGenerator<handlerClass, s_szClassName##handlerClass> sdlClassName; \
  8085. HANDLER_ENTRY(handlerString, handlerClass)\
  8086. HANDLER_ENTRY(#sdlClassName, sdlClassName)
  8087. //
  8088. // Use this class to check the authorization level of a client who is making
  8089. // a request to this application. This class checks for the stronger authentication
  8090. // levels (NTLM and Negotiate). You can call it directly from an implementation
  8091. // of HandleRequest to check authorization before handling a request.
  8092. #define MAX_AUTH_TYPE 50
  8093. #define MAX_NAME_LEN 255
  8094. template <class T>
  8095. class CVerifyAuth
  8096. {
  8097. public:
  8098. HTTP_CODE IsAuthorized(AtlServerRequest *pInfo, const SID* psidAuthGroup) throw()
  8099. {
  8100. HTTP_CODE hcErr = HTTP_UNAUTHORIZED;
  8101. char szAuthType[MAX_AUTH_TYPE];
  8102. DWORD dwSize = MAX_AUTH_TYPE;
  8103. if (pInfo->pServerContext->GetServerVariable("AUTH_TYPE",
  8104. szAuthType, &dwSize))
  8105. {
  8106. if (szAuthType[0] && (!_stricmp(szAuthType, "NTLM")
  8107. || !_stricmp(szAuthType, "Negotiate")))
  8108. {
  8109. // if we were passed a group name
  8110. // we check to see that the logged on user is part
  8111. // of that group, else we just return success.
  8112. if (psidAuthGroup)
  8113. {
  8114. T* pT = static_cast<T*>(this);
  8115. if (pT->CheckAccount(pInfo->pServerContext, psidAuthGroup))
  8116. hcErr = HTTP_SUCCESS;
  8117. else
  8118. hcErr = pT->HandleError(pInfo);
  8119. }
  8120. else
  8121. hcErr = HTTP_SUCCESS;
  8122. }
  8123. }
  8124. return hcErr;
  8125. }
  8126. bool CheckAccount(IHttpServerContext *pContext, const SID *psidAuthGroup) throw()
  8127. {
  8128. pContext; // unused
  8129. psidAuthGroup; // unused
  8130. return true;
  8131. }
  8132. HTTP_CODE HandleError(AtlServerRequest *pRequestInfo) throw()
  8133. {
  8134. pRequestInfo; // unused
  8135. return HTTP_FAIL;
  8136. }
  8137. bool CheckAuthAccount(IHttpServerContext *pContext, const SID* psidAuthGroup) throw()
  8138. {
  8139. ATLASSERT(pContext);
  8140. ATLASSERT(psidAuthGroup);
  8141. if (!pContext || !psidAuthGroup)
  8142. return false;
  8143. HANDLE hToken = INVALID_HANDLE_VALUE;
  8144. if (!pContext->GetImpersonationToken(&hToken) ||
  8145. hToken == INVALID_HANDLE_VALUE)
  8146. return false;
  8147. CAccessToken tok;
  8148. tok.Attach(hToken, true);
  8149. bool bIsMember;
  8150. bool bRet = tok.CheckTokenMembership(CSid(psidAuthGroup), &bIsMember);
  8151. if (!bRet)
  8152. return false;
  8153. return bIsMember;
  8154. }
  8155. };
  8156. // Checks that the user that is logging on is in the required group
  8157. class CDefaultAuth :
  8158. public CVerifyAuth<CDefaultAuth>
  8159. {
  8160. public:
  8161. bool CheckAccount(IHttpServerContext *pContext, const SID* psidAuthGroup) throw()
  8162. {
  8163. return CheckAuthAccount(pContext, psidAuthGroup);
  8164. }
  8165. HTTP_CODE HandleError(AtlServerRequest *pRequestInfo) throw()
  8166. {
  8167. ATLASSERT(pRequestInfo); // should always be valid
  8168. CHttpResponse response(pRequestInfo->pServerContext);
  8169. response.Write(GetErrorResponse());
  8170. response.Flush();
  8171. return HTTP_SUCCESS_NO_PROCESS;
  8172. }
  8173. virtual LPCSTR GetErrorResponse() throw()
  8174. {
  8175. static const char *szResponse = "<html><body>"
  8176. "<H1 align=center>NOT AUTHORIZED</H1><p>"
  8177. "</body></html>";
  8178. return szResponse;
  8179. }
  8180. };
  8181. } // namespace ATL
  8182. #pragma warning(pop)
  8183. #endif // __ATLISAPI_H__