home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / isapi.cpp < prev    next >
C/C++ Source or Header  |  1998-06-17  |  56KB  |  2,291 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. // This module is unique -- no need for PCH
  12.  
  13. #include <limits.h>
  14.  
  15. #ifdef _AFXDLL
  16. #include <afx.h>
  17. #include <afxwin.h>
  18. #include <afxdb.h>
  19. #include <afxpriv.h>
  20. #endif
  21.  
  22. #include <afxisapi.h>
  23. #include <afxres.h>
  24. #include <stdio.h>
  25. #include <malloc.h>
  26. #include "dispimpl.h"
  27.  
  28. ///////////////////////////////////////////////////////////////////////
  29. // Globals
  30.  
  31. // pointers to single global server and filter objects
  32.  
  33. static CHttpServer* pServer = NULL;
  34. static CHttpFilter* pFilter = NULL;
  35.  
  36. // stock server extension strings
  37.  
  38. static const TCHAR szGet[] = _T("GET");
  39. static const TCHAR szPost[] = _T("POST");
  40. static const TCHAR szDecimalFormat[] = _T("%d");
  41. static const TCHAR szFloatFormat[] = _T("%f");
  42.  
  43. // stock HTML tags
  44.  
  45. static const TCHAR szContentType[] = _T("Content-Type: text/html\r\n");
  46. static const TCHAR szEndBody[] = _T("</body></html>");
  47. static const TCHAR szStartTitle[] = _T("<html><head><title>");
  48. static const TCHAR szEndTitle[] = _T("</title></head><body>");
  49. static const TCHAR szDefaultTitle[] = _T("Default MFC Web Server Extension");
  50.  
  51. // error texts
  52. // these aren't localized as they are part of the HTTP spec
  53.  
  54. typedef struct _httpstatinfo {
  55.     DWORD   dwCode;
  56.     LPCTSTR pstrString;
  57. } HTTPStatusInfo;
  58.  
  59. static HTTPStatusInfo statusStrings[] = {
  60.     { HTTP_STATUS_OK,               _T("OK") },
  61.     { HTTP_STATUS_CREATED,          _T("Created") },
  62.     { HTTP_STATUS_ACCEPTED,         _T("Accepted") },
  63.     { HTTP_STATUS_NO_CONTENT,       _T("No Content") },
  64.     { HTTP_STATUS_TEMP_REDIRECT,    _T("Moved Temporarily") },
  65.     { HTTP_STATUS_REDIRECT,         _T("Moved Permanently") },
  66.     { HTTP_STATUS_NOT_MODIFIED,     _T("Not Modified") },
  67.     { HTTP_STATUS_BAD_REQUEST,      _T("Bad Request") },
  68.     { HTTP_STATUS_AUTH_REQUIRED,    _T("Unauthorized") },
  69.     { HTTP_STATUS_FORBIDDEN,        _T("Forbidden") },
  70.     { HTTP_STATUS_NOT_FOUND,        _T("Not Found") },
  71.     { HTTP_STATUS_SERVER_ERROR,     _T("Internal Server Error") },
  72.     { HTTP_STATUS_NOT_IMPLEMENTED,  _T("Not Implemented") },
  73.     { HTTP_STATUS_BAD_GATEWAY,      _T("Bad Gateway") },
  74.     { HTTP_STATUS_SERVICE_NA,       _T("Service Unavailable") },
  75.     { 0, NULL }
  76. };
  77.  
  78. /////////////////////////////////////////////////////////////////////////////
  79. // Root of all parse maps
  80.  
  81. const AFXIS_DATADEF AFX_PARSEMAP CHttpServer::parseMap =
  82. {
  83.     &CHttpServer::GetNumMapEntries,
  84. #ifdef _AFXDLL
  85.     &CHttpServer::_GetBaseParseMap,
  86. #else
  87.     NULL,
  88. #endif
  89.     &CHttpServer::_parseEntries[0],
  90. };
  91.  
  92. AFX_PARSEMAP_ENTRY CHttpServer::_parseEntries[] =
  93. {
  94.     { NULL, NULL, NULL }    // nothing here
  95. };
  96.  
  97. #ifdef _AFXDLL
  98. const AFX_PARSEMAP* CHttpServer::_GetBaseParseMap()
  99. {
  100.     return NULL;
  101. }
  102. #endif
  103.  
  104. UINT PASCAL CHttpServer::GetNumMapEntries()
  105. {
  106.     return 0;
  107. }
  108.  
  109. const AFX_PARSEMAP* CHttpServer::GetParseMap() const
  110. {
  111.     return NULL;
  112. }
  113.  
  114. AFX_PARSEMAP::~AFX_PARSEMAP()
  115. {
  116.     // walk through parse map to find any dying parsed parameter entries
  117.  
  118.     UINT iEntry;
  119.     UINT cEntries = (*pfnGetNumMapEntries)();
  120.     AFX_PARSEMAP_ENTRY* pCurrent = (AFX_PARSEMAP_ENTRY*) lpEntries;
  121.  
  122.     for (iEntry = 0; iEntry < cEntries; iEntry++, pCurrent++)
  123.     {
  124.         if (pCurrent->pfn == NULL)
  125.         {
  126.             delete (AFX_PARSEMAP_ENTRY_PARAMS*) pCurrent->pszFnName;
  127.             pCurrent->pszFnName = NULL;
  128.  
  129.             free(pCurrent->pszParamInfo);
  130.             pCurrent->pszParamInfo = NULL;
  131.         }
  132.     }
  133. }
  134.  
  135. AFX_PARSEMAP_ENTRY_PARAMS::~AFX_PARSEMAP_ENTRY_PARAMS()
  136. {
  137.     delete [] ppszInfo;
  138.     delete [] ppszDefaults;
  139.     delete [] ppszValues;
  140. }
  141.  
  142. ///////////////////////////////////////////////////////////////////////
  143. // Entry points for HTTP Server Extensions
  144.  
  145. extern "C" DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
  146. {
  147. #ifdef _AFXDLL
  148.     AFX_MANAGE_STATE(AfxGetStaticModuleState());
  149. #endif
  150.     DWORD dwRet;
  151.  
  152.     ISAPIASSERT(pServer != NULL);
  153.     if (pServer == NULL)
  154.     {
  155.         dwRet = HSE_STATUS_ERROR;
  156.         pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
  157.     }
  158.     else
  159.         dwRet = pServer->HttpExtensionProc(pECB);
  160.  
  161.     return dwRet;
  162. }
  163.  
  164. extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
  165. {
  166. #ifdef _AFXDLL
  167.     AFX_MANAGE_STATE(AfxGetStaticModuleState());
  168. #endif
  169.     BOOL bRet;
  170.  
  171.     ISAPIASSERT(pServer != NULL);
  172.     if (pServer == NULL)
  173.         bRet = FALSE;
  174.     else
  175.         bRet = pServer->GetExtensionVersion(pVer);
  176.  
  177.     return bRet;
  178. }
  179.  
  180. extern "C" BOOL WINAPI TerminateExtension(DWORD dwFlags)
  181. {
  182. #ifdef _AFXDLL
  183.     AFX_MANAGE_STATE(AfxGetStaticModuleState());
  184. #endif
  185.  
  186.     if (pServer == NULL)
  187.         return TRUE;
  188.     return pServer->TerminateExtension(dwFlags);
  189. }
  190.  
  191. ///////////////////////////////////////////////////////////////////////
  192. // CHttpAsyncContext implementation
  193.  
  194. class CHttpAsyncContext
  195. {
  196. public:
  197.     CHttpAsyncContext(HANDLE hFile, LPVOID pvHeader, DWORD dwHeaderLen,
  198.                     LPVOID pvTail, DWORD dwTailLen);
  199.     virtual ~CHttpAsyncContext();
  200.  
  201. protected:
  202.     LPVOID m_pvHeader;
  203.     DWORD   m_dwHeaderLen;
  204.     LPVOID m_pvTail;
  205.     DWORD m_dwTailLen;
  206.     HANDLE m_hFile;
  207. };
  208.  
  209. CHttpAsyncContext::CHttpAsyncContext(HANDLE hFile, LPVOID pvHeader,
  210.         DWORD dwHeaderLen, LPVOID pvTail, DWORD dwTailLen)
  211.     : m_hFile(hFile)
  212. {
  213.     if (pvHeader == NULL || dwHeaderLen == 0)
  214.     {
  215.         m_pvHeader = NULL;
  216.         dwHeaderLen = 0;
  217.     }
  218.     else
  219.     {
  220.         m_pvHeader = new BYTE[dwHeaderLen+1];
  221.         memcpy(m_pvHeader, pvHeader, dwHeaderLen);
  222.         m_dwHeaderLen = dwHeaderLen;
  223.     }
  224.  
  225.     if (pvTail == NULL || dwTailLen == 0)
  226.     {
  227.         m_pvTail = NULL;
  228.         dwTailLen = 0;
  229.     }
  230.     else
  231.     {
  232.         m_pvTail = new BYTE[dwTailLen+1];
  233.         memcpy(m_pvTail, pvTail, dwTailLen);
  234.         m_dwTailLen = dwTailLen;
  235.     }
  236. }
  237.  
  238. CHttpAsyncContext::~CHttpAsyncContext()
  239. {
  240.     if (m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE)
  241.         CloseHandle(m_hFile);
  242.  
  243.     delete [] m_pvTail;
  244.     delete [] m_pvHeader;
  245. }
  246.  
  247. ///////////////////////////////////////////////////////////////////////
  248. // CHttpServerContext implementation
  249.  
  250. void CHttpServerContext::Reset()
  251. {
  252. #ifdef _DEBUG
  253.     m_dwOldEndOfHeaders = 0;
  254. #endif
  255.     m_pStream->Reset();
  256. }
  257.  
  258. DWORD CHttpServerContext::SetChunkSize(DWORD dwNewSize)
  259. {
  260.     DWORD dwReturn = m_dwChunkSize;
  261.     m_dwChunkSize = dwNewSize;
  262.     return dwReturn;
  263. }
  264.  
  265. DWORD CHttpServerContext::GetChunkSize() const
  266. {
  267.     return m_dwChunkSize;
  268. }
  269.  
  270. VOID WINAPI AfxIOCallback(EXTENSION_CONTROL_BLOCK* pECB,
  271.     PVOID pvContext, DWORD /* cbIO */, DWORD /* dwError */)
  272. {
  273.     if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL))
  274.         ISAPITRACE("HttpExtensionProc io callback error\n");
  275.  
  276.     CHttpAsyncContext* pInfo = (CHttpAsyncContext*) pvContext;
  277.     delete pInfo;
  278. }
  279.  
  280. BOOL CHttpServerContext::TransmitFile(HANDLE hFile,
  281.     DWORD dwFlags /* = HSE_IO_DISCONNECT_AFTER_SEND */,
  282.     LPVOID pvHeader /* = NULL */, DWORD dwHeaderLen /* = 0 */,
  283.     LPVOID pvTail /* = NULL */, DWORD dwTailLen /* = 0 */)
  284. {
  285.     if (hFile == INVALID_HANDLE_VALUE)
  286.         return FALSE;
  287.  
  288.     ISAPIASSERT(dwHeaderLen == 0 || pvHeader != NULL);
  289.     ISAPIASSERT(dwTailLen == 0 || pvTail != NULL);
  290.  
  291.     if (dwTailLen == 0 && pvTail != NULL)
  292.         dwTailLen = lstrlen((LPCTSTR) pvTail);
  293.  
  294.     if (dwHeaderLen == 0 && pvHeader != NULL)
  295.         dwHeaderLen = lstrlen((LPCTSTR) pvHeader);
  296.  
  297.     CHttpAsyncContext* pInfo =
  298.         new CHttpAsyncContext(hFile, pvHeader, dwTailLen, pvTail, dwHeaderLen);
  299.  
  300.     HSE_TF_INFO info;
  301.  
  302.     info.pszStatusCode = NULL;
  303.     info.hFile = hFile;
  304.     info.dwFlags = dwFlags;
  305.     info.BytesToWrite = 0;
  306.     info.Offset = 0;
  307.     info.pfnHseIO = AfxIOCallback;
  308.     info.pContext = pInfo;
  309.     info.pHead = pvHeader;
  310.     info.HeadLength = dwHeaderLen;
  311.     info.pTail = pvTail;
  312.     info.TailLength = dwTailLen;
  313.  
  314.     BOOL bResult = ServerSupportFunction(HSE_REQ_TRANSMIT_FILE, &info, 0, 0);
  315.  
  316.     if (bResult)
  317.     {
  318.         m_dwStatusCode = HSE_STATUS_PENDING;
  319.         m_bSendHeaders = FALSE;
  320.     }
  321.     else
  322.         delete pInfo;
  323.  
  324.     return bResult;
  325. }
  326.  
  327. ///////////////////////////////////////////////////////////////////////
  328. // CHttpServer implementation
  329.  
  330. BOOL CHttpServer::TerminateExtension(DWORD /* dwFlags */)
  331. {
  332.     // okay to unload at any time, by default
  333.     return TRUE;
  334. }
  335.  
  336. LPTSTR CHttpServer::GetQuery(CHttpServerContext* pCtxt, LPTSTR lpszQuery)
  337. {
  338.     // If the request is a POST, get all of the data.  First get what's
  339.     // already available, then read any additional data via the
  340.     // ReadClient() call.
  341.  
  342.     memcpy(lpszQuery, (LPCTSTR) pCtxt->m_pECB->lpbData, pCtxt->m_pECB->cbAvailable);
  343.     lpszQuery[pCtxt->m_pECB->cbAvailable] = '\0';
  344.  
  345.     if (pCtxt->m_pECB->cbAvailable < pCtxt->m_pECB->cbTotalBytes)
  346.     {
  347.         LPCTSTR pstrTarget = lpszQuery + pCtxt->m_pECB->cbAvailable;
  348.         DWORD cbRemaining = pCtxt->m_pECB->cbTotalBytes - pCtxt->m_pECB->cbAvailable;
  349.         DWORD cbRead;
  350.  
  351.         while (cbRemaining > 0)
  352.         {
  353.             cbRead = cbRemaining;
  354.             if (!pCtxt->ReadClient((LPVOID) pstrTarget, &cbRead))
  355.             {
  356.                 ISAPITRACE("Error: only %d of %d bytes read!\n",
  357.                     pCtxt->m_pECB->cbTotalBytes - cbRemaining,
  358.                     pCtxt->m_pECB->cbTotalBytes);
  359.                 return NULL;
  360.             }
  361.  
  362.             if (cbRead == 0)
  363.                 break;
  364.  
  365.             pstrTarget += cbRead;
  366.             cbRemaining -= cbRead;
  367.         }
  368.     }
  369.  
  370.     pCtxt->m_dwBytesReceived = pCtxt->m_pECB->cbTotalBytes;
  371.     return lpszQuery;
  372. }
  373.  
  374. BOOL CHttpServer::OnWriteBody(CHttpServerContext* pCtxt, LPBYTE pbContent,
  375.                                 DWORD dwSize, DWORD dwReserved /* = 0 */)
  376. {
  377.     BOOL bRetVal;
  378.  
  379.     if (pCtxt->m_dwChunkSize == 0)
  380.         bRetVal = pCtxt->WriteClient(pbContent, &dwSize, dwReserved);
  381.     else
  382.     {
  383.         LPBYTE pbStart = pbContent;
  384.         DWORD dwBytesLeft = dwSize;
  385.         bRetVal = TRUE;
  386.  
  387.         while (bRetVal && (dwBytesLeft > 0))
  388.         {
  389.             DWORD dwThisChunk = min(dwBytesLeft, pCtxt->m_dwChunkSize);
  390.             bRetVal = pCtxt->WriteClient(pbStart, &dwThisChunk, dwReserved);
  391.             pbStart += dwThisChunk;
  392.             dwBytesLeft -= dwThisChunk;
  393.         }
  394.     }
  395.  
  396.     return bRetVal;
  397. }
  398.  
  399. DWORD CHttpServer::HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
  400. {
  401.     DWORD dwRet = HSE_STATUS_SUCCESS;
  402.     LPTSTR pszPostBuffer = NULL;
  403.     LPTSTR pszQuery;
  404.     LPTSTR pszCommand = NULL;
  405.     int nMethodRet;
  406.     LPTSTR pstrLastChar;
  407.     DWORD cbStream = 0;
  408.     BYTE* pbStream = NULL;
  409.     CHttpServerContext ctxtCall(pECB);
  410.  
  411.     pECB->dwHttpStatusCode = 0;
  412.  
  413.     ISAPIASSERT(NULL != pServer);
  414.     if (pServer == NULL)
  415.     {
  416.         dwRet = HSE_STATUS_ERROR;
  417.         goto CleanUp;
  418.     }
  419.  
  420.     // get the query
  421.  
  422.     if (lstrcmpi(pECB->lpszMethod, szGet) == 0)
  423.     {
  424.         pszQuery = pECB->lpszQueryString;
  425.         ctxtCall.m_dwBytesReceived = lstrlen(pszQuery);
  426.     }
  427.     else if (lstrcmpi(pECB->lpszMethod, szPost) == 0)
  428.     {
  429.         pszCommand = pECB->lpszQueryString;
  430.         pszPostBuffer = new TCHAR[pECB->cbTotalBytes + 1];
  431.         pszQuery =  GetQuery(&ctxtCall, pszPostBuffer);
  432.         if (pszQuery == NULL)
  433.         {
  434.             dwRet = HSE_STATUS_ERROR;
  435.             goto CleanUp;
  436.         }
  437.     }
  438.     else
  439.     {
  440.         ISAPITRACE1("Error: Unrecognized method: %s\n", pECB->lpszMethod);
  441.         dwRet = HSE_STATUS_ERROR;
  442.         goto CleanUp;
  443.     }
  444.  
  445.     // trim junk that some browsers put at the very end
  446.  
  447.     pstrLastChar = pszQuery + ctxtCall.m_dwBytesReceived -1;
  448.     while ((*pstrLastChar == ' ' || *pstrLastChar == '\n' ||
  449.            *pstrLastChar == '\r') && pstrLastChar > pszQuery)
  450.     {
  451.         *pstrLastChar-- = '\0';
  452.     }
  453.  
  454.     // do something about it
  455.  
  456.     if (!pServer->InitInstance(&ctxtCall))
  457.         dwRet = HSE_STATUS_ERROR;
  458.     else
  459.     {
  460.         pECB->dwHttpStatusCode = HTTP_STATUS_OK;
  461.         try {
  462.             nMethodRet = pServer->CallFunction(&ctxtCall, pszQuery, pszCommand);
  463.         }
  464.         catch (...)
  465.         {
  466.             ISAPITRACE1("Error: command %s caused an unhandled exception!\n",
  467.                 pszQuery);
  468.             nMethodRet = callNoStackSpace;
  469.         }
  470.  
  471.         // was an error caused by trying to dispatch?
  472.  
  473.         if (nMethodRet != callOK && pECB->dwHttpStatusCode == HTTP_STATUS_OK)
  474.         {
  475.             dwRet = HSE_STATUS_ERROR;
  476.             switch (nMethodRet)
  477.             {
  478.             case callNoStream:
  479.                 pECB->dwHttpStatusCode = HTTP_STATUS_NO_CONTENT;
  480.                 break;
  481.  
  482.             case callParamRequired:
  483.             case callBadParamCount:
  484.             case callBadParam:
  485.                 pECB->dwHttpStatusCode = HTTP_STATUS_BAD_REQUEST;
  486.                 break;
  487.  
  488.             case callBadCommand:
  489.                 pECB->dwHttpStatusCode = HTTP_STATUS_NOT_IMPLEMENTED;
  490.                 break;
  491.  
  492.             case callNoStackSpace:
  493.             default:
  494.                 pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
  495.                 break;
  496.             }
  497.         }
  498.  
  499.         // if there was no error or the user said they handled
  500.         // the error, prepare to spit out the generated HTML
  501.  
  502.         if (nMethodRet == callOK ||
  503.             OnParseError(&ctxtCall, nMethodRet))
  504.         {
  505.             cbStream = ctxtCall.m_pStream->GetStreamSize();
  506.             pbStream = ctxtCall.m_pStream->Detach();
  507.         }
  508.     }
  509.  
  510. CleanUp:
  511.     // if there was an error, return an appropriate status
  512.     TCHAR szResponse[64];
  513.     BuildStatusCode(szResponse, pECB->dwHttpStatusCode);
  514.  
  515.     DWORD dwSize = cbStream - ctxtCall.m_dwEndOfHeaders;
  516.     LPBYTE pbContent = NULL;
  517.     BYTE cSaved = 0;
  518.  
  519.     if (pbStream != NULL && ctxtCall.m_bSendHeaders)
  520.     {
  521.         cSaved = pbStream[ctxtCall.m_dwEndOfHeaders];
  522.         pbStream[ctxtCall.m_dwEndOfHeaders] = '\0';
  523.         pbContent = &pbStream[ctxtCall.m_dwEndOfHeaders];
  524.     }
  525.  
  526.     if (ctxtCall.m_bSendHeaders &&
  527.         !ctxtCall.ServerSupportFunction(HSE_REQ_SEND_RESPONSE_HEADER,
  528.             szResponse, 0, (LPDWORD) pbStream) &&
  529.         ::GetLastError() != 10054)  // WSAECONNRESET
  530.     {
  531.         pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
  532.         dwRet = HSE_STATUS_ERROR;
  533. #ifdef _DEBUG
  534.         DWORD dwCause = ::GetLastError();
  535.         ISAPITRACE1("Error: Unable to write headers: 0x%8.8X!\n", dwCause);
  536. #endif
  537.     }
  538.     else
  539.     {
  540.         if (pbContent != NULL)
  541.         {
  542.             BOOL bWorked = TRUE;
  543.  
  544.             // write a newline to separate content from headers
  545.  
  546.             if (ctxtCall.m_bSendHeaders)
  547.             {
  548.                 *pbContent = cSaved;
  549.                 DWORD dwNewLineSize = 2;
  550.                 bWorked = ctxtCall.WriteClient(_T("\r\n"), &dwNewLineSize, 0);
  551.             }
  552.  
  553.             if (!bWorked || !OnWriteBody(&ctxtCall, pbContent, dwSize))
  554.             {
  555.                 dwRet = HSE_STATUS_ERROR;
  556.                 pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
  557. #ifdef _DEBUG
  558.                 DWORD dwCause = ::GetLastError();
  559.                 ISAPITRACE1("Error: Unable to write content body: 0x%8.8X!\n",
  560.                     dwCause);
  561. #endif
  562.             }
  563.         }
  564.         else
  565.             ISAPITRACE("Error: No body content!\n");
  566.     }
  567.  
  568.     if (pbStream != NULL)
  569.         ctxtCall.m_pStream->Free(pbStream);
  570.  
  571.     if (ctxtCall.m_dwStatusCode != DWORD(-1))
  572.         dwRet = ctxtCall.m_dwStatusCode;
  573.     if (dwRet == HSE_STATUS_SUCCESS)
  574.         pECB->dwHttpStatusCode = HTTP_STATUS_OK;
  575.  
  576.     if (pszPostBuffer != NULL)
  577.         delete [] pszPostBuffer;
  578.  
  579.     return dwRet;
  580. }
  581.  
  582. void CHttpServer::BuildStatusCode(LPTSTR pszResponse, DWORD dwCode)
  583. {
  584.     ISAPIASSERT(pszResponse != NULL);
  585.     HTTPStatusInfo* pInfo = statusStrings;
  586.  
  587.     while (pInfo->pstrString != NULL)
  588.     {
  589.         if (dwCode == pInfo->dwCode)
  590.             break;
  591.         pInfo++;
  592.     }
  593.  
  594.     if (pInfo->pstrString != NULL)
  595.         wsprintf(pszResponse, _T("%d %s"), dwCode, pInfo->pstrString);
  596.     else
  597.     {
  598.         ISAPIASSERT(dwCode != HTTP_STATUS_OK);
  599.         ISAPITRACE1("Warning: Nonstandard status code %d\n", dwCode);
  600.         BuildStatusCode(pszResponse, HTTP_STATUS_OK);
  601.     }
  602. }
  603.  
  604. BOOL CHttpServer::GetExtensionVersion(HSE_VERSION_INFO *pVer)
  605. {
  606.     pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
  607.     pVer->lpszExtensionDesc[0] = '\0';
  608.     return TRUE;
  609. }
  610.  
  611.  
  612. CHttpServer::CHttpServer(TCHAR cDelimiter /* = '&' */)
  613.     : m_cTokenDelimiter(cDelimiter)
  614. {
  615.     ISAPIASSERT(pServer == NULL);   // only one server instance
  616.     pServer = this;
  617.  
  618.     // allocate our critical section directly to avoid bogus traces
  619.     m_pCritSec = (LPCRITICAL_SECTION)
  620.         LocalAlloc(LPTR, sizeof(CRITICAL_SECTION));
  621.     ::InitializeCriticalSection(m_pCritSec);
  622. }
  623.  
  624. CHttpServer::~CHttpServer()
  625. {
  626.     if (m_pCritSec != NULL)
  627.     {
  628.         ::DeleteCriticalSection(m_pCritSec);
  629.         LocalFree(m_pCritSec);
  630.     }
  631.     pServer = NULL;
  632. }
  633.  
  634. BOOL CHttpServer::OnParseError(CHttpServerContext* pCtxt, int nMethodRet)
  635. {
  636.     UNUSED(nMethodRet);
  637.     UINT nResource = 0;
  638.  
  639.     if (pCtxt->m_pStream != NULL)
  640.     {
  641.         TCHAR szBuffer[132];
  642.         TCHAR szTitle[256];
  643.         TCHAR szFormat[256];
  644.         LPCTSTR pszObject = NULL;
  645.  
  646.         switch (pCtxt->m_pECB->dwHttpStatusCode)
  647.         {
  648.         case HTTP_STATUS_BAD_REQUEST:
  649.             nResource = AFX_IDS_HTTP_BAD_REQUEST;
  650.             if (pCtxt->m_pECB->lpszQueryString)
  651.                 pszObject = pCtxt->m_pECB->lpszQueryString;
  652.             else
  653.                 pszObject = pCtxt->m_pECB->lpszPathInfo;
  654.             break;
  655.  
  656.         case HTTP_STATUS_AUTH_REQUIRED:
  657.             nResource = AFX_IDS_HTTP_AUTH_REQUIRED;
  658.             break;
  659.  
  660.         case HTTP_STATUS_FORBIDDEN:
  661.             nResource = AFX_IDS_HTTP_FORBIDDEN;
  662.             break;
  663.  
  664.         case HTTP_STATUS_NOT_FOUND:
  665.             nResource = AFX_IDS_HTTP_NOT_FOUND;
  666.             break;
  667.  
  668.         case HTTP_STATUS_SERVER_ERROR:
  669.             nResource = AFX_IDS_HTTP_SERVER_ERROR;
  670.             break;
  671.  
  672.         case HTTP_STATUS_NOT_IMPLEMENTED:
  673.             nResource = AFX_IDS_HTTP_NOT_IMPLEMENTED;
  674.             pszObject = pCtxt->m_pECB->lpszQueryString;
  675.             break;
  676.  
  677.         default:
  678.             nResource = AFX_IDS_HTTP_NO_TEXT;
  679.             pszObject = (LPCTSTR) pCtxt->m_pECB->dwHttpStatusCode;
  680.             break;
  681.         }
  682.  
  683.         HINSTANCE hRes;
  684.         hRes = AfxGetResourceHandle();
  685.  
  686. #ifdef _AFXDLL
  687.         if (AfxLoadString(nResource, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])) > 0)
  688. #else
  689.  
  690.         if (::LoadString(hRes, nResource, szBuffer,
  691.             sizeof(szBuffer)/sizeof(szBuffer[0])) > 0)
  692. #endif
  693.         {
  694.             pCtxt->Reset();
  695.             CHttpServer::StartContent(pCtxt);
  696.  
  697.             if (::LoadString(hRes, AFX_IDS_HTTP_TITLE,
  698.                 szTitle, sizeof(szTitle)/sizeof(szTitle[0])) > 0)
  699.             {
  700.                 TCHAR szTitleCopy[64];
  701.                 wsprintf(szTitleCopy, szTitle, pCtxt->m_pECB->dwHttpStatusCode);
  702.                 *pCtxt << szTitleCopy;
  703.             }
  704.  
  705.             if (pszObject != NULL)
  706.             {
  707.                 wsprintf(szFormat, szBuffer, pszObject);
  708.                 *pCtxt << szFormat;
  709.             }
  710.             else
  711.                 *pCtxt << szBuffer;
  712.             CHttpServer::EndContent(pCtxt);
  713.         }
  714.         else
  715.         {
  716.             ISAPITRACE1("Error: Couldn't load string %d\n", nResource);
  717.             nResource = 0;
  718.         }
  719.     }
  720.  
  721.     if (nResource == 0)
  722.         ISAPITRACE1("Error: Unhandled parsing error code %d\n", nMethodRet);
  723.  
  724.     return nResource != 0;
  725. }
  726.  
  727. void CHttpServer::AddHeader(CHttpServerContext* pCtxt,
  728.     LPCTSTR pszString) const
  729. {
  730. #ifdef _DEBUG
  731.     // Can't call AddHeader() after writing directly to the stream.
  732.     ISAPIASSERT(pCtxt->m_dwOldEndOfHeaders == pCtxt->m_pStream->GetStreamSize());
  733. #endif
  734.  
  735.     *pCtxt << pszString;
  736.     pCtxt->m_dwEndOfHeaders = pCtxt->m_pStream->GetStreamSize();
  737.  
  738. #ifdef _DEBUG
  739.     pCtxt->m_dwOldEndOfHeaders = pCtxt->m_dwEndOfHeaders;
  740. #endif
  741. }
  742.  
  743. void CHttpServer::StartContent(CHttpServerContext* pCtxt) const
  744. {
  745.     AddHeader(pCtxt, szContentType);
  746. }
  747.  
  748. void CHttpServer::WriteTitle(CHttpServerContext* pCtxt) const
  749. {
  750.     *pCtxt << szStartTitle;
  751.     *pCtxt << GetTitle();
  752.     *pCtxt << szEndTitle;
  753. }
  754.  
  755. LPCTSTR CHttpServer::GetTitle() const
  756. {
  757.     return szDefaultTitle;
  758. }
  759.  
  760. void CHttpServer::EndContent(CHttpServerContext* pCtxt) const
  761. {
  762.     *pCtxt << szEndBody;
  763. }
  764.  
  765. BOOL CHttpServer::InitInstance(CHttpServerContext*)
  766. {
  767.     return TRUE;
  768. }
  769.  
  770. int CHttpServer::CallFunction(CHttpServerContext* pCtxt,
  771.     LPTSTR pszQuery, LPTSTR pszCommand)
  772. {
  773.     int nRet;
  774.  
  775.     AFX_PARSEMAP_ENTRY* pParams;
  776.     const AFX_PARSEMAP* pMap;
  777.     const AFX_PARSEMAP_ENTRY* pFn;
  778.  
  779.     ISAPIASSERT(pCtxt->m_pStream == NULL);
  780.     pCtxt->m_pStream = ConstructStream();
  781.     if (pCtxt->m_pStream == NULL)
  782.         nRet = callNoStream;
  783.     else
  784.     {
  785.         ISAPIASSERT(pszQuery != NULL);
  786.         if (pszQuery == NULL)
  787.             nRet = callBadCommand;
  788.         else
  789.         {
  790.             LPTSTR pszMethod;
  791.             LPTSTR pszParams;
  792.  
  793.             // did the user specify a command via "MfcISAPICommand"?
  794.  
  795.             LPTSTR pszHiddenCommand = _tcschr(pszQuery, '=');
  796.             if (pszHiddenCommand != NULL)
  797.             {
  798.                 *pszHiddenCommand = '\0';
  799.  
  800.                 // is it there?
  801.  
  802.                 if (lstrcmpi(pszQuery, _T("MfcISAPICommand")) == 0)
  803.                 {
  804.                     // did they have a method, too?
  805.  
  806.                     pszMethod = pszHiddenCommand+1;
  807.                     if (*pszMethod == '\0')
  808.                         pszParams = pszMethod;
  809.                     else
  810.                     {
  811.                         pszParams = _tcschr(pszMethod, m_cTokenDelimiter);
  812.                         if (pszParams != NULL && *pszParams != '\0')
  813.                             *pszParams++ = '\0';
  814.                     }
  815.  
  816.                     // if we find it, we can call it
  817.  
  818.                     pFn = LookUp(pszMethod, pMap, pParams);
  819.                     if (pFn != NULL)
  820.                         goto MakeTheCall;
  821.                 }
  822.  
  823.                 // we didn't find the command, or we didn't have
  824.                 // "MfcISAPICommand", so we'll try and process things
  825.                 // normally...
  826.  
  827.                 *pszHiddenCommand = '=';
  828.             }
  829.  
  830.             if (pszCommand == NULL)
  831.             {
  832.                 // got something via a GET method
  833.                 // is the first thing a parameter or a command?
  834.  
  835.                 LPTSTR pszEquals;
  836.                 LPTSTR pszQMark;
  837.  
  838.                 pszParams = _tcschr(pszQuery, m_cTokenDelimiter);
  839.                 pszQMark = _tcschr(pszQuery, '?');
  840.  
  841.                 // Parameters start at the first delimiter
  842.  
  843.                 if (pszParams == NULL || (pszQMark != NULL && (pszQMark < pszParams)))
  844.                 {
  845.                     pszParams = pszQMark;
  846.  
  847.                     // if the command ends in question mark
  848.                     // and nothing else, ignore it
  849.                     if (pszQMark != NULL && pszQMark[1] == '\0')
  850.                     {
  851.                         *pszQMark = '\0';
  852.                         pszParams = NULL;
  853.                     }
  854.                 }
  855.  
  856.                 // Does an equals sign show up before the first delimiter?
  857.  
  858.                 pszEquals = _tcschr(pszQuery, '=');
  859.                 if (pszEquals == NULL || pszEquals > pszParams)
  860.                 {
  861.                     // It might be a command. If it isn't blank,
  862.                     // try and find it in the parameter map--if
  863.                     // we can't, then assume it is a parameter.
  864.  
  865.                     pszMethod = pszQuery;
  866.                     if (*pszMethod != '\0')
  867.                     {
  868.                         TCHAR cTemp = 0;
  869.                         if (pszParams != NULL)
  870.                         {
  871.                             cTemp = *pszParams;
  872.                             *pszParams++ = '\0';
  873.                         }
  874.  
  875.                         pFn = LookUp(pszMethod, pMap, pParams);
  876.                         if (pFn != NULL)
  877.                             goto MakeTheCall;
  878.  
  879.                         if (pszParams != NULL)
  880.                             *--pszParams = cTemp;
  881.                     }
  882.                 }
  883.  
  884.                 // we can be sure it's a parameter
  885.                 if (pszQMark == NULL || pszQMark >= pszParams)
  886.                 {
  887.                     // default command, params as supplied
  888.                     pszMethod = NULL;
  889.                     pszParams = pszQuery;
  890.                 }
  891.                 else
  892.                 {
  893.                     pszMethod = pszQuery;
  894.                     *pszQMark++ = '\0';
  895.                     pszParams = pszQMark;
  896.                 }
  897.             }
  898.             else
  899.             {
  900.                 // with a POST, the verb arrives seperately
  901.                 pszMethod = pszCommand;
  902.                 pszParams = pszQuery;
  903.             }
  904.  
  905.             // is it a default verb?
  906.             if (pszMethod != NULL && lstrlen(pszMethod) == 0)
  907.                 pszMethod = NULL;
  908.  
  909.             // is it a useless parameter?
  910.             if (pszParams != NULL && lstrlen(pszParams) == 0)
  911.                 pszParams = NULL;
  912.  
  913.             pFn = LookUp(pszMethod, pMap, pParams);
  914.  
  915. MakeTheCall:
  916.             if (pFn == NULL)
  917.                 nRet = callBadCommand;
  918.             else
  919.             {
  920.                 pCtxt->m_pStream->InitStream();
  921.                 nRet = CallMemberFunc(pCtxt, pFn, pParams, pszParams);
  922.             }
  923.         }
  924.     }
  925.  
  926.     return nRet;
  927. }
  928.  
  929. CHtmlStream* CHttpServer::ConstructStream()
  930. {
  931.     return new CHtmlStream();
  932. }
  933.  
  934. const AFX_PARSEMAP_ENTRY* CHttpServer::LookUp(LPCTSTR pszMethod,
  935.     const AFX_PARSEMAP*& pMap, AFX_PARSEMAP_ENTRY*& pParams,
  936.     AFX_PISAPICMD pCmdDefault /* = NULL */)
  937. {
  938.     UINT iEntry;
  939.     LPCTSTR pszFnName;
  940.     const AFX_PARSEMAP* pParseMap = GetParseMap();
  941.     const AFX_PARSEMAP* pBaseMap;
  942.     const AFX_PARSEMAP_ENTRY* pRet = NULL;
  943.  
  944.     while (pParseMap != NULL && pRet == NULL)
  945.     {
  946.         UINT cEntries = (*pParseMap->pfnGetNumMapEntries)();
  947.         const AFX_PARSEMAP_ENTRY* pCurrent = pParseMap->lpEntries;
  948.  
  949.         for (iEntry = 0; iEntry < cEntries && pRet == NULL; iEntry++, pCurrent++)
  950.         {
  951. #ifdef _DEBUG
  952.             // make sure not 2 parameter maps in a row
  953.  
  954.             if (pCurrent->pfn == NULL && (iEntry+1 < cEntries))
  955.                 ISAPIASSERT(pCurrent[1].pfn != NULL);
  956. #endif
  957.  
  958.             // skip parameter maps
  959.  
  960.             if (pCurrent->pfn == NULL)
  961.                 continue;
  962.  
  963.             pszFnName = pCurrent->pszFnName;
  964.  
  965.             // if the caller wants the default command, find that--
  966.             // if the caller wants something specific, perform a compare
  967.             // otherwise, see if we recursed to look up the default command
  968.  
  969.             if (pCmdDefault == NULL)
  970.             {
  971.                 if (pszMethod == NULL && pCurrent->pszArgs == NULL)
  972.                     pRet = pCurrent;
  973.                 else if (pszMethod != NULL && pCurrent->pszArgs != NULL
  974.                     && lstrcmpi(pszFnName, pszMethod) == 0)
  975.                     pRet = pCurrent;
  976.             }
  977.             else if (pCurrent->pfn == pCmdDefault && pCurrent->pszArgs != NULL)
  978.                 pRet = pCurrent;
  979.  
  980.             if (pRet != NULL)
  981.             {
  982.                 // if we need the default, recurse to find it by name
  983.                 if (pszMethod == NULL && pCmdDefault == NULL)
  984.                     return LookUp(NULL, pMap, pParams, pCurrent->pfn);
  985.  
  986.                 // found it!  see if there are parameters
  987.  
  988.                 if (iEntry+1 >= cEntries || pCurrent[1].pfn != NULL)
  989.                 {
  990.                     pParams = NULL;
  991.                     pMap = NULL;
  992.                 }
  993.                 else
  994.                 {
  995.                     pParams = (AFX_PARSEMAP_ENTRY*) &(pCurrent[1]);
  996.                     pMap = pParseMap;
  997.                 }
  998.             }
  999.         }
  1000.  
  1001. #ifdef _AFXDLL
  1002.         pBaseMap = (*pParseMap->pfnGetBaseMap)();
  1003. #else
  1004.         pBaseMap = pParseMap->pBaseMap;
  1005. #endif
  1006.  
  1007.         // catch simple mistake of BEGIN_PARSE_MAP(CMyClass, CMyClass)
  1008.         ISAPIASSERT(pBaseMap != pParseMap);
  1009.         pParseMap = pBaseMap;
  1010.     }
  1011.  
  1012.     // no matching entry ?
  1013.     if (pRet == NULL)
  1014.         ISAPITRACE1("Warning: no handler for command '%s'\n", pszMethod);
  1015.     return pRet;
  1016. }
  1017.  
  1018. UINT PASCAL CHttpServer::GetStackSize(const BYTE* pbParams)
  1019. {
  1020.     // size of arguments on stack when pushed by value
  1021.     static const UINT rgnByValue[] =
  1022.     {
  1023.         sizeof(_STACK_INT),         // ITS_I2
  1024.         sizeof(_STACK_LONG),        // ITS_I4
  1025.         sizeof(_STACK_FLOAT),       // ITS_R4
  1026.         sizeof(_STACK_DOUBLE),      // ITS_R8
  1027.         sizeof(LPCTSTR),            // ITS_PSTR
  1028.         0,                          // ITS_EMPTY
  1029.         sizeof(LPCTSTR)+sizeof(_STACK_INT),     // ITS_RAW
  1030.     };
  1031.     // sizeof 'this' pointer
  1032.     UINT nCount = sizeof(CHttpServer*);
  1033. #ifdef _ALIGN_STACK
  1034.     nCount = (nCount + (_ALIGN_STACK-1)) & ~(_ALIGN_STACK-1);
  1035. #endif
  1036.  
  1037.     // count arguments
  1038.     ISAPIASSERT(pbParams != NULL);
  1039.     while (*pbParams != 0 && *pbParams != IT_EMPTY)
  1040.     {
  1041.         // align if necessary
  1042.         // get and add appropriate byte count
  1043.  
  1044. #ifdef _ALIGN_DOUBLES
  1045.         // align doubles on 8 byte for some platforms
  1046.         if (*pbParams == IT_R8)
  1047.             nCount = (nCount + _ALIGN_DOUBLES-1) & ~(_ALIGN_DOUBLES-1);
  1048. #endif
  1049.  
  1050.         // *pbParams must fit in the rgnByValue array
  1051.         ISAPIASSERT(*pbParams >= 1 && *pbParams <= sizeof(rgnByValue)/sizeof(UINT));
  1052.         nCount += rgnByValue[*pbParams-1];
  1053.  
  1054. #ifdef _ALIGN_STACK
  1055.         nCount = (nCount + (_ALIGN_STACK-1)) & ~(_ALIGN_STACK-1);
  1056. #endif
  1057.         ++pbParams;
  1058.     }
  1059. #if defined(_ALIGN_DOUBLES) && defined(_SHADOW_DOUBLES)
  1060.     // align doubles on 8 byte for some platforms
  1061.     nCount = (nCount + _ALIGN_DOUBLES-1) & ~(_ALIGN_DOUBLES-1);
  1062. #endif
  1063.     return nCount;
  1064. }
  1065.  
  1066.  
  1067. // indirect call helper (see OLECALL.CPP for implementation)
  1068.  
  1069. extern "C" DWORD AFXISAPI
  1070. _AfxParseCall(AFX_PISAPICMD pfn, void* pArgs, UINT nSizeArgs);
  1071.  
  1072. // invoke standard method given IDispatch parameters/return value, etc.
  1073.  
  1074. int CHttpServer::CallMemberFunc(CHttpServerContext* pCtxt,
  1075.     const AFX_PARSEMAP_ENTRY* pEntry,
  1076.     AFX_PARSEMAP_ENTRY* pParams, LPTSTR pszParams)
  1077. {
  1078.     ISAPIASSERT(NULL != pEntry);
  1079.     AFX_PISAPICMD pFunc = pEntry->pfn;
  1080.     ISAPIASSERT(NULL != pFunc);
  1081.     int nRet = callOK;
  1082.  
  1083.     // get default function and parameters
  1084.     BYTE bNoParams = 0;
  1085.  
  1086.     ::EnterCriticalSection(m_pCritSec);
  1087.     const BYTE* pbParams = (const BYTE*)pEntry->pszArgs;
  1088.     if (NULL == pbParams)
  1089.         pbParams = &bNoParams;
  1090.     UINT nParams = lstrlenA((LPCSTR)pbParams);
  1091.  
  1092.     AFX_PARSEMAP_ENTRY_PARAMS *pDefaultParams = NULL;
  1093.     if (pParams != NULL)
  1094.     {
  1095.         if (pParams->pszFnName == NULL)
  1096.             nRet = ParseDefaultParams(pParams, nParams, pDefaultParams, pbParams);
  1097.         else
  1098.             pDefaultParams = (AFX_PARSEMAP_ENTRY_PARAMS*) pParams->pszFnName;
  1099.     }
  1100.     ::LeaveCriticalSection(m_pCritSec);
  1101.  
  1102.     if (nRet == callOK)
  1103.     {
  1104.         // get default function and return value information
  1105.         AFX_PISAPICMD pfn = pEntry->pfn;
  1106.  
  1107.         // determine size of arguments and allocate stack space
  1108.         // include space for our context pointer
  1109.         UINT nSizeArgs = GetStackSize(pbParams) + sizeof(_STACK_PTR);
  1110.         ISAPIASSERT(nSizeArgs != 0);
  1111.  
  1112.         if (nSizeArgs < _STACK_MIN)
  1113.             nSizeArgs = _STACK_MIN;
  1114.         BYTE* pStack = (BYTE*)_alloca(nSizeArgs + _SCRATCH_SIZE);
  1115.         if (pStack == NULL)
  1116.         {
  1117.             ISAPITRACE0("Error: stack overflow in CHttpServer::CallMemberFunc()!\n");
  1118.             return callNoStackSpace;
  1119.         }
  1120.  
  1121.         if (pDefaultParams != NULL)
  1122. #ifndef _SHADOW_DOUBLES
  1123.             nRet = PushDefaultStackArgs(pStack, pCtxt, pbParams, pszParams,
  1124.                 pDefaultParams);
  1125. #else
  1126.             nRet = PushDefaultStackArgs(pStack, pCtxt, pbParams, pszParams,
  1127.                 pDefaultParams, nSizeArgs);
  1128. #endif
  1129.         else
  1130. #ifndef _SHADOW_DOUBLES
  1131.             nRet = PushStackArgs(pStack, pCtxt, pbParams, pszParams);
  1132. #else
  1133.             nRet = PushStackArgs(pStack, pCtxt, pbParams, pszParams, nSizeArgs);
  1134. #endif
  1135.         pStack += _STACK_OFFSET;
  1136.  
  1137.         if (nRet == 0)
  1138.         {
  1139.             DWORD (AFXISAPI *pfnInet)(AFX_PISAPICMD, void*, UINT) =
  1140.                 &_AfxParseCall;
  1141.             pfnInet(pfn, pStack, nSizeArgs);
  1142.         }
  1143.     }
  1144.  
  1145.     return nRet;
  1146. }
  1147.  
  1148. int CHttpServer::CountParams(LPCTSTR pszCommandLine, int& nCount)
  1149. {
  1150.     BOOL bInQuote = FALSE;
  1151.     BOOL bInSpace = TRUE;
  1152.     LPCTSTR pszWalker = pszCommandLine;
  1153.     int nRetCode = callOK;
  1154.  
  1155.     nCount = 0;
  1156.     if (pszCommandLine != NULL)
  1157.     {
  1158.         while (*pszWalker != '\0')
  1159.         {
  1160.             if (bInSpace)
  1161.             {
  1162.                 // this is invalid syntax
  1163.                 ISAPIASSERT(*pszWalker != '\'');
  1164.                 if (*pszWalker == '\'')
  1165.                 {
  1166.                     nRetCode = callMissingQuote;
  1167.                     break;
  1168.                 }
  1169.  
  1170.                 if (!_istspace(*pszWalker))
  1171.                 {
  1172.                     nCount++;
  1173.                     bInSpace = FALSE;
  1174.                 }
  1175.             }
  1176.             else
  1177.             {
  1178.                 if (*pszWalker == '\'')
  1179.                     bInQuote = !bInQuote;
  1180.                 else if (!bInQuote &&
  1181.                     (_istspace(*pszWalker) || *pszWalker == m_cTokenDelimiter))
  1182.                     bInSpace = TRUE;
  1183.             }
  1184.  
  1185.             pszWalker++;
  1186.         }
  1187.  
  1188.         // can't have only whitespace
  1189.         if (nCount == 0 && bInSpace)
  1190.         {
  1191.             nRetCode = callMissingParams;
  1192.             ISAPIASSERT(nCount > 0 || !bInSpace);
  1193.         }
  1194.         // unclosed quoted string?
  1195.         else if (bInQuote)
  1196.         {
  1197.             nRetCode = callMissingQuote;
  1198.             ISAPIASSERT(!bInQuote);
  1199.         }
  1200.     }
  1201.  
  1202.     return nRetCode;
  1203. }
  1204.  
  1205. int CHttpServer::ParseDefaultParams(AFX_PARSEMAP_ENTRY* pParams,
  1206.     int nParams, AFX_PARSEMAP_ENTRY_PARAMS*& pBlock, const BYTE* pbParams)
  1207. {
  1208.     int nRet = callOK;
  1209.  
  1210.     LPSTR pszWalker;
  1211.     BOOL bInQuote;
  1212.     BOOL bInSpace;
  1213.     BOOL bInEquals;
  1214.     BOOL bMandatory = TRUE;
  1215.  
  1216.     // only if we haven't done it already
  1217.  
  1218.     if (pParams->pszFnName == NULL)
  1219.     {
  1220.         AFX_PARSEMAP_ENTRY_PARAMS* pParBlock = NULL;
  1221.         ISAPIASSERT(pParams->pszParamInfo == NULL);
  1222.  
  1223.         // can't have empty param string
  1224.         ISAPIASSERT(*pParams->pszArgs != '\0');
  1225.  
  1226.         if (*pParams->pszArgs != '\0')
  1227.         {
  1228.             pParBlock = new AFX_PARSEMAP_ENTRY_PARAMS;
  1229.             pBlock = pParBlock;
  1230.             memset(pParBlock, 0, sizeof(AFX_PARSEMAP_ENTRY_PARAMS));
  1231.             pParams->pszFnName = (LPTSTR) pParBlock;
  1232.  
  1233.             // start by finding how many parameters we have
  1234.             nRet = CountParams(pParams->pszArgs, pParBlock->nParams);
  1235.             if (nRet == callOK)
  1236.                 pParams->pszParamInfo = _tcsdup(pParams->pszArgs);
  1237.         }
  1238.  
  1239.         if (pParBlock == NULL || nRet != callOK)
  1240.         {
  1241.             if (nRet == callOK)
  1242.                 nRet = callMissingParams;
  1243.             goto CleanUp;
  1244.         }
  1245.  
  1246.         // wrong number of parameters?
  1247.         if (nParams != pParBlock->nParams)
  1248.         {
  1249.             nRet = callBadParamCount;
  1250.             ISAPIASSERT(nParams == pParBlock->nParams);
  1251.         }
  1252.         // it's a winner!
  1253.         else
  1254.         {
  1255.             pParBlock->ppszInfo =
  1256.                 new LPTSTR[pParBlock->nParams *2];
  1257.             pParBlock->ppszDefaults =
  1258.                 new BYTE[pParBlock->nParams * sizeof(double)];
  1259.             pParBlock->ppszValues =
  1260.                 new BYTE[pParBlock->nParams * sizeof(double)];
  1261.  
  1262.             bInQuote = FALSE;
  1263.             bInSpace = TRUE;
  1264.             bInEquals = FALSE;
  1265.             int nStorage = 0;
  1266.  
  1267.             for (pszWalker = pParams->pszParamInfo;
  1268.                 *pszWalker != '\0'; pszWalker++)
  1269.             {
  1270.                 if (bInSpace)
  1271.                 {
  1272.                     // this is invalid syntax
  1273.                     ISAPIASSERT(*pszWalker != '\'' && *pszWalker != '=');
  1274.                     if (*pszWalker == '\'' || *pszWalker == '=')
  1275.                     {
  1276.                         nRet = callMissingQuote;
  1277.                         break;
  1278.                     }
  1279.  
  1280.                     if (!_istspace(*pszWalker))
  1281.                     {
  1282.                         pParBlock->ppszInfo[nStorage++] = pszWalker;
  1283.                         bInSpace = FALSE;
  1284.                     }
  1285.                 }
  1286.                 else
  1287.                 {
  1288.                     if (bInEquals)
  1289.                     {
  1290.                         if (_istspace(*pszWalker) && bInQuote)
  1291.                             continue;
  1292.  
  1293.                         if (*pszWalker == '\'' || _istspace(*pszWalker))
  1294.                         {
  1295.                             if (bInQuote || _istspace(*pszWalker))
  1296.                             {
  1297.                                 *pszWalker = '\0';
  1298.                                 bInEquals = FALSE;
  1299.                                 bInSpace = TRUE;
  1300.                                 bInQuote = FALSE;
  1301.                             }
  1302.                             else
  1303.                             {
  1304.                                 pParBlock->ppszInfo[nStorage-1]++;
  1305.                                 bInQuote = TRUE;
  1306.                             }
  1307.                         }
  1308.                     }
  1309.                     else
  1310.                     {
  1311.                         // parameter with no default
  1312.                         if (_istspace(*pszWalker))
  1313.                         {
  1314.                             // can't have required param after optional params
  1315.                             ISAPIASSERT(bMandatory);
  1316.                             if (!bMandatory)
  1317.                             {
  1318.                                 nRet = callBadParam;
  1319.                                 goto CleanUp;
  1320.                             }
  1321.  
  1322.                             *pszWalker = '\0';
  1323.                             bInSpace = TRUE;
  1324.                             pParBlock->ppszInfo[nStorage++] = NULL;
  1325.                         }
  1326.                         // end of parameter name with default
  1327.                         else if (*pszWalker == '=')
  1328.                         {
  1329.                             bMandatory = FALSE;
  1330.                             bInEquals = TRUE;
  1331.                             *pszWalker = '\0';
  1332.                             pParBlock->ppszInfo[nStorage++] = pszWalker+1;
  1333.                         }
  1334.                         // bad syntax--quote in param name
  1335.                         else if (*pszWalker == '\'')
  1336.                         {
  1337.                             ISAPIASSERT(*pszWalker != '\'');
  1338.                             nRet = callMissingQuote;
  1339.                             break;
  1340.                         }
  1341.                     }
  1342.                 }
  1343.             }
  1344.  
  1345.             // handle case of no default for final param
  1346.             if (nStorage & 1)
  1347.                 pParBlock->ppszInfo[nStorage] = NULL;
  1348.  
  1349.             int nIndex;
  1350.             for (nIndex = 0; nIndex < pParBlock->nParams; nIndex++)
  1351.             {
  1352.                 if (pParBlock->ppszInfo[2*nIndex+1] != NULL)
  1353. #ifndef _SHADOW_DOUBLES
  1354.                     StoreStackParameter(
  1355.                         &pParBlock->ppszDefaults[sizeof(double) * nIndex],
  1356.                         pbParams[nIndex], pParBlock->ppszInfo[2*nIndex+1]);
  1357. #else
  1358.                     StoreStackParameter(
  1359.                         &pParBlock->ppszDefaults[sizeof(double) * nIndex],
  1360.                         pbParams[nIndex], pParBlock->ppszInfo[2*nIndex+1],
  1361.                         0, FALSE);
  1362. #endif
  1363.                 else
  1364.                     pParBlock->nRequired++;
  1365.             }
  1366.         }
  1367.     }
  1368.  
  1369. CleanUp:
  1370.     return nRet;
  1371. }
  1372.  
  1373.  
  1374. // push arguments on stack appropriate for C++ call (compiler dependent)
  1375. #ifndef _SHADOW_DOUBLES
  1376. int CHttpServer::PushDefaultStackArgs(BYTE* pStack,
  1377.     CHttpServerContext* pCtxt,
  1378.     const BYTE* pbParams, LPTSTR pszParams,
  1379.     AFX_PARSEMAP_ENTRY_PARAMS* pDefParams)
  1380. #else
  1381. int CHttpServer::PushDefaultStackArgs(BYTE* pStack,
  1382.     CHttpServerContext* pCtxt,
  1383.     const BYTE* pbParams, LPTSTR pszParams,
  1384.     AFX_PARSEMAP_ENTRY_PARAMS* pDefParams, int nSizeArgs)
  1385. #endif
  1386. {
  1387.     ISAPIASSERT(pStack != NULL);
  1388.  
  1389.     LPTSTR pszCurParam = NULL;
  1390.     int nExplicit = 0;
  1391.     LPBYTE pFlags;
  1392.     int nPushedExplicit = 0;
  1393.     int nRetVal = callOK;
  1394.     LPTSTR pszLineEnd;
  1395.  
  1396.     // keep a list of flags to know what's pushed and what's not
  1397.  
  1398.     pFlags = new BYTE[pDefParams->nParams];
  1399.     memset(pFlags, 0, pDefParams->nParams);
  1400.  
  1401.     // C++ member functions use the __thiscall convention, where parameters
  1402.     //  are pushed last to first.  Assuming the stack grows down, this means
  1403.     //  that the first parameter is at the lowest memory address in the
  1404.     //  stack frame and the last parameter is at the highest address.
  1405.  
  1406.     // push the 'this' pointer
  1407.  
  1408.     *(_STACK_PTR*)pStack = (_STACK_PTR)this;
  1409.     pStack += sizeof(_STACK_PTR);
  1410.  
  1411.     // push our context pointer
  1412.  
  1413.     *(_STACK_PTR*)pStack = (_STACK_PTR)pCtxt;
  1414.     pStack += sizeof(_STACK_PTR);
  1415.  
  1416.     // copy the default argument list to the usable argument list
  1417.     memcpy(pDefParams->ppszValues, pDefParams->ppszDefaults,
  1418.         sizeof(double) * pDefParams->nParams);
  1419.  
  1420.     // push the arguments, if explicitly supplied
  1421.  
  1422.     if (pszParams != NULL)
  1423.     {
  1424.         pszLineEnd = pszParams + lstrlen(pszParams);
  1425.  
  1426.         TCHAR szTokens[2];
  1427.         szTokens[0] = m_cTokenDelimiter;
  1428.         szTokens[1] = '\0';
  1429.  
  1430.         ISAPIASSERT(pbParams != NULL);
  1431.         for (const BYTE* pb = pbParams; *pb != '\0'; ++pb)
  1432.         {
  1433.             if (*pb == IT_EMPTY)
  1434.             {
  1435.                 // can't have ITS_EMPTY with other types!
  1436.                 ISAPIASSERT(pb == pbParams);
  1437.                 break;
  1438.             }
  1439.  
  1440.             if (pszParams == NULL)
  1441.                 break;
  1442.             pszCurParam = _tcstok(pszParams, szTokens);
  1443.             if (pszCurParam == NULL)
  1444.                 break;
  1445.  
  1446.             // does this param have a name?
  1447.             LPTSTR pszValue = _tcschr(pszCurParam, '=');
  1448.             if (pszValue != NULL)
  1449.             {
  1450.                 *pszValue++ = '\0';
  1451.  
  1452.                 // find the parameter in our param block
  1453.  
  1454.                 int nIndex;
  1455.                 BOOL bFound = FALSE;
  1456.                 for (nIndex = 0; nIndex < pDefParams->nParams; nIndex++)
  1457.                 {
  1458.                     if (lstrcmpi(pDefParams->ppszInfo[nIndex*2], pszCurParam) == 0)
  1459.                     {
  1460.                         bFound = TRUE;
  1461.                         break;
  1462.                     }
  1463.                 }
  1464.  
  1465.                 // something we don't recognize?
  1466.                 if (!bFound)
  1467.                 {
  1468.                     nRetVal = callBadParam;
  1469.                     goto CleanUp;
  1470.                 }
  1471.  
  1472.                 pszParams = pszValue + lstrlen(pszValue);
  1473.                 if (pszParams != pszLineEnd)
  1474.                     pszParams++;
  1475.  
  1476.                 // if this parameter has a default and there's
  1477.                 // no value for the parameter after the equal sign,
  1478.                 // let the default value prevail
  1479.  
  1480.                 if (*pszValue != '\0' ||
  1481.                     pDefParams->ppszInfo[2*nIndex+1] == NULL)
  1482.                 {
  1483. #ifndef _SHADOW_DOUBLES
  1484.                     StoreStackParameter(
  1485.                         &(pDefParams->ppszValues[nIndex*sizeof(double)]),
  1486.                         pbParams[nIndex], pszValue);
  1487. #else
  1488.                     StoreStackParameter(
  1489.                         &(pDefParams->ppszValues[nIndex*sizeof(double)]),
  1490.                         pbParams[nIndex], pszValue, 0, FALSE);
  1491. #endif
  1492.  
  1493.                     // if this has no default, it counts as explicit, too
  1494.                     if (pDefParams->ppszInfo[2*nIndex+1] == NULL)
  1495.                         nExplicit++;
  1496.                 }
  1497.  
  1498.                 // if we've already pushed this parameter, or if we've
  1499.                 // already pushed this many explicit params, make an error
  1500.                 if (pFlags[nIndex] != 0 || nIndex < nPushedExplicit)
  1501.                 {
  1502.                     nRetVal = callBadParam;
  1503.                     goto CleanUp;
  1504.                 }
  1505.                 pFlags[nIndex] = 1;
  1506.             }
  1507.             else
  1508.             {
  1509.                 // not allowed to have optional params before required params
  1510.                 if (nExplicit != 0)
  1511.                 {
  1512.                     nRetVal = callBadParam;
  1513.                     goto CleanUp;
  1514.                 }
  1515.  
  1516.                 pszParams += lstrlen(pszCurParam);
  1517.                 if (pszParams != pszLineEnd)
  1518.                     pszParams++;
  1519.  
  1520. #ifndef _SHADOW_DOUBLES
  1521.                 pStack = StoreStackParameter(pStack, *pb, pszCurParam);
  1522. #else
  1523.                 pStack = StoreStackParameter(pStack, *pb, pszCurParam,
  1524.                     nSizeArgs, TRUE);
  1525. #endif
  1526.  
  1527.                 if (pFlags[nPushedExplicit] != 0)
  1528.                 {
  1529.                     nRetVal = callBadParam;
  1530.                     goto CleanUp;
  1531.                 }
  1532.                 pFlags[nPushedExplicit] = 1;
  1533.                 nPushedExplicit++;
  1534.             }
  1535.         }
  1536.  
  1537.         // any unused parameters?
  1538.  
  1539.         LPTSTR pszMoreParams;
  1540.         pszMoreParams = _tcschr(pszParams, m_cTokenDelimiter);
  1541.  
  1542.         if (*pb == '\0' && (pszMoreParams != NULL || *pszParams != '\0'))
  1543.         {
  1544.             nRetVal = callBadParamCount;
  1545.             goto CleanUp;
  1546.         }
  1547.     }
  1548.  
  1549.     // were any arguments without defaults missed?
  1550.     if (nPushedExplicit + nExplicit < pDefParams->nRequired)
  1551.     {
  1552.         nRetVal = callBadParamCount;
  1553.     }
  1554.     else if (nPushedExplicit < pDefParams->nParams)
  1555.     {
  1556.         int nIndex;
  1557.         for (nIndex = nPushedExplicit; nIndex < pDefParams->nParams; nIndex++)
  1558.         {
  1559. #ifndef _SHADOW_DOUBLES
  1560.             pStack = StoreRawStackParameter(pStack,
  1561.                 pbParams[nIndex],
  1562.                 &(pDefParams->ppszValues[nIndex*sizeof(double)]));
  1563. #else
  1564.             pStack = StoreRawStackParameter(pStack,
  1565.                 pbParams[nIndex],
  1566.                 &(pDefParams->ppszValues[nIndex*sizeof(double)]), 0);
  1567. #endif
  1568.         }
  1569.     }
  1570.  
  1571. CleanUp:
  1572.     if (pFlags != NULL)
  1573.         delete [] pFlags;
  1574.     return nRetVal;
  1575. }
  1576.  
  1577.  
  1578. // push arguments on stack appropriate for C++ call (compiler dependent)
  1579. #ifndef _SHADOW_DOUBLES
  1580. int CHttpServer::PushStackArgs(BYTE* pStack, CHttpServerContext* pCtxt,
  1581.     const BYTE* pbParams, LPTSTR pszParams)
  1582. #else
  1583. int CHttpServer::PushStackArgs(BYTE* pStack, CHttpServerContext* pCtxt,
  1584.     const BYTE* pbParams, LPTSTR pszParams, UINT nSizeArgs)
  1585. #endif
  1586. {
  1587.     LPTSTR pszCurParam = NULL;
  1588.     ISAPIASSERT(pStack != NULL);
  1589.  
  1590.     // C++ member functions use the __thiscall convention, where parameters
  1591.     //  are pushed last to first.  Assuming the stack grows down, this means
  1592.     //  that the first parameter is at the lowest memory address in the
  1593.     //  stack frame and the last parameter is at the highest address.
  1594.  
  1595.     // push the 'this' pointer
  1596.  
  1597.     *(_STACK_PTR*)pStack = (_STACK_PTR)this;
  1598.     pStack += sizeof(_STACK_PTR);
  1599.  
  1600.     // push our context pointer
  1601.  
  1602.     *(_STACK_PTR*)pStack = (_STACK_PTR)pCtxt;
  1603.     pStack += sizeof(_STACK_PTR);
  1604.  
  1605.     // push the arguments (first to last, low address to high address)
  1606.  
  1607.     TCHAR szTokens[2];
  1608.     szTokens[0] = m_cTokenDelimiter;
  1609.     szTokens[1] = '\0';
  1610.     const BYTE* pb;
  1611.     int nRetCode = callOK;
  1612.  
  1613.     if (pszParams != NULL && *pbParams == IT_EMPTY)
  1614.         nRetCode = callBadParamCount;
  1615.     else if (pszParams != NULL)
  1616.     {
  1617.         LPTSTR pszLineEnd;
  1618.         pszLineEnd = pszParams + lstrlen(pszParams);
  1619.  
  1620.         ISAPIASSERT(pbParams != NULL);
  1621.         for (pb = pbParams; *pb != '\0'; ++pb)
  1622.         {
  1623.             if (*pb == IT_EMPTY)
  1624.             {
  1625.                 // can't have ITS_EMPTY with other types!
  1626.                 ISAPIASSERT(pb == pbParams);
  1627.                 if (pb != pbParams)
  1628.                     nRetCode = callBadParam;
  1629.                 break;
  1630.             }
  1631.  
  1632.             if (*pb == IT_RAW)
  1633.             {
  1634.                 // can't have ITS_RAW with other types!
  1635.                 ISAPIASSERT(pb == pbParams);
  1636.                 if (pb != pbParams)
  1637.                     nRetCode = callBadParam;
  1638.                 else
  1639.                 {
  1640.                     BYTE* pbParam = (BYTE*) &pszParams;
  1641. #ifndef _SHADOW_DOUBLES
  1642.                     pStack = StoreRawStackParameter(pStack, IT_PSTR, pbParam);
  1643.                     pStack = StoreRawStackParameter(pStack, IT_I4,
  1644.                         (BYTE*) &(pCtxt->m_dwBytesReceived));
  1645. #else
  1646.                     pStack = StoreRawStackParameter(pStack, IT_PSTR, pbParam);
  1647.                     pStack = StoreRawStackParameter(pStack, IT_I4,
  1648.                         (BYTE*) &(pCtxt->m_dwBytesReceived));
  1649. #endif
  1650.                 }
  1651.                 return nRetCode;
  1652.             }
  1653.  
  1654.             if (pszParams == NULL)
  1655.                 break;
  1656.             pszCurParam = _tcstok(pszParams, szTokens);
  1657.             if (pszCurParam == NULL)
  1658.                 break;
  1659.  
  1660.             pszParams = pszCurParam + lstrlen(pszCurParam);
  1661.             if (pszParams != pszLineEnd)
  1662.                 pszParams++;
  1663.  
  1664. #ifndef _SHADOW_DOUBLES
  1665.             pStack = StoreStackParameter(pStack, *pb, pszCurParam);
  1666. #else
  1667.             pStack = StoreStackParameter(pStack, *pb, pszCurParam, nSizeArgs, TRUE);
  1668. #endif
  1669.         }
  1670.  
  1671.         // check that all source arguments were consumed
  1672.  
  1673.         if (nRetCode == callOK)
  1674.         {
  1675.             LPTSTR pszMoreParams;
  1676.             pszMoreParams = _tcschr(pszParams, m_cTokenDelimiter);
  1677.  
  1678.             if (*pb != '\0' && pszMoreParams == NULL)
  1679.                 nRetCode = callBadParamCount;
  1680.             else if (*pb == '\0' && (pszMoreParams != NULL || *pszParams != '\0'))
  1681.                 nRetCode = callBadParamCount;
  1682.         }
  1683.     }
  1684.     else
  1685.     {
  1686.         if (*pbParams != IT_EMPTY)
  1687.             nRetCode = callBadParamCount;
  1688.     }
  1689.  
  1690.     return nRetCode;
  1691. }
  1692.  
  1693. #ifndef _SHADOW_DOUBLES
  1694. BYTE* CHttpServer::StoreRawStackParameter(BYTE* pStack, BYTE nType,
  1695.     BYTE* pRawParam)
  1696. #else
  1697. BYTE* CHttpServer::StoreRawStackParameter(BYTE* pStack, BYTE nType,
  1698.     BYTE* pRawParam, int nSizeArgs)
  1699. #endif
  1700. {
  1701.     ISAPIASSERT(pStack != NULL);
  1702.     ISAPIASSERT(pRawParam != NULL);
  1703.  
  1704. #ifdef _SHADOW_DOUBLES
  1705.     double* pDoubleShadow = (double*)(pStack + nSizeArgs);
  1706.     double* pDoubleShadowMax = pDoubleShadow + _SHADOW_DOUBLES;
  1707. #endif
  1708.  
  1709.     // push parameter value on the stack
  1710.     switch (nType)
  1711.     {
  1712.     // by value parameters
  1713.     case IT_I2:
  1714.         *(_STACK_INT*)pStack = *((WORD*) pRawParam);
  1715.         pStack += sizeof(_STACK_INT);   // 'short' is passed as 'int'
  1716.         break;
  1717.     case IT_I4:
  1718.         *(_STACK_LONG*)pStack = *((DWORD*) pRawParam);
  1719.         pStack += sizeof(_STACK_LONG);
  1720.         break;
  1721.     case IT_R4:
  1722.         *(_STACK_FLOAT*)pStack = *((_STACK_FLOAT*) pRawParam);
  1723.         pStack += sizeof(_STACK_FLOAT);
  1724. #ifdef _SHADOW_DOUBLES
  1725.         if (pDoubleShadow < pDoubleShadowMax)
  1726.             *pDoubleShadow++ = *((_STACK_DOUBLE*) pRawParam);
  1727. #endif
  1728.         break;
  1729.     case IT_R8:
  1730. #ifdef _ALIGN_DOUBLES
  1731.         // align doubles on 8 byte for some platforms
  1732.         pStack = (BYTE*)(((DWORD)pStack + _ALIGN_DOUBLES-1) &
  1733.             ~(_ALIGN_DOUBLES-1));
  1734. #endif
  1735.         *(_STACK_DOUBLE*)pStack = *((_STACK_DOUBLE*) pRawParam);
  1736.         pStack += sizeof(_STACK_DOUBLE);
  1737. #ifdef _SHADOW_DOUBLES
  1738.         if (pDoubleShadow < pDoubleShadowMax)
  1739.             *pDoubleShadow++ = *((_STACK_DOUBLE*) pRawParam);
  1740. #endif
  1741.         break;
  1742.     case IT_PSTR:
  1743.         *(_STACK_PTR*)pStack = *((_STACK_PTR*) pRawParam);
  1744.         pStack += sizeof(_STACK_PTR);
  1745.         break;
  1746.     default:
  1747.         ISAPIASSERT(FALSE);
  1748.     }
  1749.  
  1750. #ifdef _ALIGN_STACK
  1751.     // align stack as appropriate for next parameter
  1752.     pStack = (BYTE*)(((DWORD)pStack + (_ALIGN_STACK-1)) &
  1753.         ~(_ALIGN_STACK-1));
  1754.     ISAPIASSERT(((DWORD)pStack & (_ALIGN_STACK-1)) == 0);
  1755. #endif
  1756.  
  1757.     return pStack;
  1758. }
  1759.  
  1760. #ifndef _SHADOW_DOUBLES
  1761. BYTE* CHttpServer::StoreStackParameter(BYTE* pStack, BYTE nType,
  1762.     LPTSTR pszCurParam)
  1763. #else
  1764. BYTE* CHttpServer::StoreStackParameter(BYTE* pStack, BYTE nType,
  1765.     LPTSTR pszCurParam, UINT nSizeArgs, BOOL bDoShadow)
  1766. #endif
  1767. {
  1768.     ISAPIASSERT(pStack != NULL);
  1769.     ISAPIASSERT(pszCurParam != NULL);
  1770.  
  1771. #ifdef _SHADOW_DOUBLES
  1772.     double* pDoubleShadow = (double*)(pStack + nSizeArgs);
  1773.     double* pDoubleShadowMax = pDoubleShadow + _SHADOW_DOUBLES;
  1774. #endif
  1775.  
  1776.     // push parameter value on the stack
  1777.     switch (nType)
  1778.     {
  1779.     // by value parameters
  1780.     case IT_I2:
  1781.         *(_STACK_INT*)pStack = (WORD) _ttoi(pszCurParam);
  1782.         pStack += sizeof(_STACK_INT);   // 'short' is passed as 'int'
  1783.         break;
  1784.     case IT_I4:
  1785.         *(_STACK_LONG*)pStack = (DWORD) _ttol(pszCurParam);
  1786.         pStack += sizeof(_STACK_LONG);
  1787.         break;
  1788.     case IT_R4:
  1789.         *(_STACK_FLOAT*)pStack = (_STACK_FLOAT) atof(pszCurParam);
  1790.         pStack += sizeof(_STACK_FLOAT);
  1791. #ifdef _SHADOW_DOUBLES
  1792.         if (bDoShadow && pDoubleShadow < pDoubleShadowMax)
  1793.             *pDoubleShadow++ = (double) atof(pszCurParam);
  1794. #endif
  1795.         break;
  1796.     case IT_R8:
  1797. #ifdef _ALIGN_DOUBLES
  1798.         // align doubles on 8 byte for some platforms
  1799.         pStack = (BYTE*)(((DWORD)pStack + _ALIGN_DOUBLES-1) &
  1800.             ~(_ALIGN_DOUBLES-1));
  1801. #endif
  1802.         *(_STACK_DOUBLE*)pStack = (_STACK_DOUBLE) atof(pszCurParam);
  1803.         pStack += sizeof(_STACK_DOUBLE);
  1804. #ifdef _SHADOW_DOUBLES
  1805.         if (bDoShadow && pDoubleShadow < pDoubleShadowMax)
  1806.             *pDoubleShadow++ = atof(pszCurParam);
  1807. #endif
  1808.         break;
  1809.     case IT_PSTR:
  1810.         *(_STACK_PTR*)pStack = (_STACK_PTR) PreprocessString(pszCurParam);
  1811.         pStack += sizeof(_STACK_PTR);
  1812.         break;
  1813.     default:
  1814.         ISAPIASSERT(FALSE);
  1815.     }
  1816.  
  1817. #ifdef _ALIGN_STACK
  1818.     // align stack as appropriate for next parameter
  1819.     pStack = (BYTE*)(((DWORD)pStack + (_ALIGN_STACK-1)) &
  1820.         ~(_ALIGN_STACK-1));
  1821.     ISAPIASSERT(((DWORD)pStack & (_ALIGN_STACK-1)) == 0);
  1822. #endif
  1823.  
  1824.     return pStack;
  1825. }
  1826.  
  1827. LPVOID CHttpServer::PreprocessString(LPTSTR psz)
  1828. {
  1829.     LPTSTR pszSource = psz;
  1830.     LPTSTR pszDest = psz;
  1831.  
  1832.     static const TCHAR szHex[] = _T("0123456789ABCDEF");
  1833.  
  1834.     // unescape special characters
  1835.  
  1836.     while (*pszSource != '\0')
  1837.     {
  1838.         if (*pszSource == '+')
  1839.             *pszDest++ = ' ';
  1840.         else if (*pszSource == '%')
  1841.         {
  1842.             TCHAR nValue = '?';
  1843.             LPCTSTR pszLow;
  1844.             LPCTSTR pszHigh;
  1845.             pszSource++;
  1846.  
  1847.             *pszSource = (TCHAR) _totupper(*pszSource);
  1848.             pszHigh = _tcschr(szHex, *pszSource);
  1849.  
  1850.             if (pszHigh != NULL)
  1851.             {
  1852.                 pszSource++;
  1853.                 *pszSource = (TCHAR) _totupper(*pszSource);
  1854.                 pszLow = _tcschr(szHex, *pszSource);
  1855.                 if (pszLow != NULL)
  1856.                 {
  1857.                     nValue = (TCHAR) (((pszHigh - szHex) << 4) +
  1858.                                     (pszLow - szHex));
  1859.                 }
  1860.  
  1861.             }
  1862.  
  1863.             *pszDest++ = nValue;
  1864.         }
  1865.         else
  1866.             *pszDest++ = *pszSource;
  1867.         pszSource++;
  1868.     }
  1869.     *pszDest = '\0';
  1870.     return (LPVOID) psz;
  1871. }
  1872.  
  1873. ///////////////////////////////////////////////////////////////////////
  1874. // in-memory HTML Stream
  1875.  
  1876. CHtmlStream::CHtmlStream(UINT nGrowBytes /* = 4096 */)
  1877. {
  1878.     ISAPIASSERT(nGrowBytes <= UINT_MAX && nGrowBytes >= 0);
  1879.  
  1880.     m_nGrowBytes = nGrowBytes;
  1881.     m_nPosition = 0;
  1882.     m_nBufferSize = 0;
  1883.     m_lpBuffer = NULL;
  1884.     m_bAutoDelete = TRUE;
  1885.     m_nStreamSize = 0;
  1886. }
  1887.  
  1888. CHtmlStream::CHtmlStream(BYTE* lpBuffer, UINT nBufferSize,
  1889.     UINT nGrowBytes /* = 0 */)
  1890. {
  1891.     ISAPIASSERT(nGrowBytes <= UINT_MAX && nGrowBytes >= 0);
  1892.  
  1893.     m_nGrowBytes = nGrowBytes;
  1894.     m_nPosition = 0;
  1895.     m_nBufferSize = nBufferSize;
  1896.  
  1897.     // if the caller provides a grow-by size, use it;
  1898.     // otherwise, incrememt by the initial buffer size
  1899.     m_nStreamSize = nGrowBytes == 0 ? nBufferSize : 0;
  1900.  
  1901.     m_lpBuffer = lpBuffer;
  1902.     m_bAutoDelete = FALSE;
  1903. }
  1904.  
  1905. void CHtmlStream::Attach(BYTE* lpBuffer, UINT nBufferSize, UINT nGrowBytes)
  1906. {
  1907.     ISAPIASSERT(m_lpBuffer == NULL);
  1908.  
  1909.     m_nGrowBytes = nGrowBytes;
  1910.     m_nPosition = 0;
  1911.     m_nBufferSize = nBufferSize;
  1912.  
  1913.     // if the caller provides a grow-by size, use it;
  1914.     // otherwise, incrememt by the initial buffer size
  1915.     m_nStreamSize = nGrowBytes == 0 ? nBufferSize : 0;
  1916.  
  1917.     m_lpBuffer = lpBuffer;
  1918.     m_bAutoDelete = FALSE;
  1919. }
  1920.  
  1921. BYTE* CHtmlStream::Detach()
  1922. {
  1923.     BYTE* lpBuffer = NULL;
  1924.     if (m_lpBuffer != NULL)
  1925.     {
  1926.         TCHAR chZero = 0;
  1927.         Write(&chZero, sizeof(TCHAR));
  1928.         lpBuffer = m_lpBuffer;
  1929.         m_lpBuffer = NULL;
  1930.     }
  1931.     m_nBufferSize = 0;
  1932.     m_nStreamSize = 0;
  1933.     m_nPosition = 0;
  1934.  
  1935.     return lpBuffer;
  1936. }
  1937.  
  1938. CHtmlStream::~CHtmlStream()
  1939. {
  1940.     // Close should have already been called, but we check anyway
  1941.     if (m_lpBuffer)
  1942.         Close();
  1943.     ISAPIASSERT(NULL == m_lpBuffer);
  1944.  
  1945.     m_nGrowBytes = 0;
  1946.     m_nPosition = 0;
  1947.     m_nStreamSize = 0;
  1948.     m_nBufferSize = 0;
  1949. }
  1950.  
  1951. BYTE* CHtmlStream::Alloc(DWORD nBytes)
  1952. {
  1953.     return (BYTE*)malloc((UINT)nBytes);
  1954. }
  1955.  
  1956. BYTE* CHtmlStream::Realloc(BYTE* lpMem, DWORD nBytes)
  1957. {
  1958.     return (BYTE*)realloc(lpMem, (UINT)nBytes);
  1959. }
  1960.  
  1961. #pragma intrinsic(memcpy)
  1962. BYTE* CHtmlStream::Memcpy(BYTE* lpMemTarget, const BYTE* lpMemSource,
  1963.     UINT nBytes)
  1964. {
  1965.     ISAPIASSERT(lpMemTarget != NULL);
  1966.     ISAPIASSERT(lpMemSource != NULL);
  1967.  
  1968.     return (BYTE*)memcpy(lpMemTarget, lpMemSource, nBytes);
  1969. }
  1970. #pragma function(memcpy)
  1971.  
  1972. void CHtmlStream::Free(BYTE* lpMem)
  1973. {
  1974.     ISAPIASSERT(lpMem != NULL);
  1975.     free(lpMem);
  1976. }
  1977.  
  1978. void CHtmlStream::GrowStream(DWORD dwNewLen)
  1979. {
  1980.     if (dwNewLen > m_nBufferSize)
  1981.     {
  1982.         // grow the buffer
  1983.         DWORD dwNewBufferSize = (DWORD)m_nBufferSize;
  1984.  
  1985.         // watch out for buffers which cannot be grown!
  1986.         ISAPIASSERT(m_nGrowBytes > 0);
  1987.         if (m_nGrowBytes == 0)
  1988.             throw;
  1989.  
  1990.         // determine new buffer size
  1991.         while (dwNewBufferSize < dwNewLen)
  1992.             dwNewBufferSize += m_nGrowBytes;
  1993.  
  1994.         // allocate new buffer
  1995.         BYTE* lpNew;
  1996.         if (m_lpBuffer == NULL)
  1997.             lpNew = Alloc(dwNewBufferSize);
  1998.         else
  1999.             lpNew = Realloc(m_lpBuffer, dwNewBufferSize);
  2000.  
  2001.         if (lpNew == NULL)
  2002.             throw;
  2003.  
  2004.         m_lpBuffer = lpNew;
  2005.         m_nBufferSize = dwNewBufferSize;
  2006.     }
  2007. }
  2008.  
  2009. CHtmlStream& CHtmlStream::operator<<(LPCTSTR psz)
  2010. {
  2011.     Write(psz, lstrlen(psz));
  2012.     return *this;
  2013. }
  2014.  
  2015. CHtmlStream& CHtmlStream::operator<<(short int w)
  2016. {
  2017.     TCHAR sz[16];
  2018.     int nLen = wsprintf(sz, szDecimalFormat, w);
  2019.     Write(sz, nLen);
  2020.     return *this;
  2021. }
  2022.  
  2023. CHtmlStream& CHtmlStream::operator<<(long int dw)
  2024. {
  2025.     TCHAR sz[16];
  2026.     int nLen = wsprintf(sz, szDecimalFormat, dw);
  2027.     Write(sz, nLen);
  2028.     return *this;
  2029. }
  2030.  
  2031. CHtmlStream& CHtmlStream::operator<<(float f)
  2032. {
  2033.     TCHAR sz[64];
  2034.     int nLen = _stprintf(sz, szFloatFormat, f);
  2035.     Write(sz, nLen);
  2036.     return *this;
  2037. }
  2038.  
  2039. CHtmlStream& CHtmlStream::operator<<(double d)
  2040. {
  2041.     TCHAR sz[64];
  2042.     int nLen = _stprintf(sz, szFloatFormat, d);
  2043.     Write(sz, nLen);
  2044.     return *this;
  2045. }
  2046.  
  2047. CHtmlStream& CHtmlStream::operator<<(const CHtmlStream& stream)
  2048. {
  2049.     DWORD dwSourceLen = stream.GetStreamSize();
  2050.     Write(stream.m_lpBuffer, dwSourceLen);
  2051.     return *this;
  2052. }
  2053.  
  2054. void CHtmlStream::Close()
  2055. {
  2056.     m_nGrowBytes = 0;
  2057.     m_nPosition = 0;
  2058.     m_nBufferSize = 0;
  2059.     m_nStreamSize = 0;
  2060.     if (m_lpBuffer && m_bAutoDelete)
  2061.         Free(m_lpBuffer);
  2062.     m_lpBuffer = NULL;
  2063. }
  2064.  
  2065. void CHtmlStream::Abort()
  2066. {
  2067.     Close();
  2068. }
  2069.  
  2070. void CHtmlStream::Write(const void* lpBuf, UINT nCount)
  2071. {
  2072.     if (nCount == 0)
  2073.         return;
  2074.     ISAPIASSERT(lpBuf != NULL);
  2075.  
  2076.     if (m_nPosition + nCount > m_nBufferSize)
  2077.         GrowStream(m_nPosition + nCount);
  2078.  
  2079.     ISAPIASSERT(m_nPosition + nCount <= m_nBufferSize);
  2080.  
  2081.     Memcpy((BYTE*)m_lpBuffer + m_nPosition, (BYTE*)lpBuf, nCount);
  2082.  
  2083.     m_nPosition += nCount;
  2084.  
  2085.     if (m_nPosition > m_nStreamSize)
  2086.         m_nStreamSize = m_nPosition;
  2087. }
  2088.  
  2089. void CHtmlStream::Reset()
  2090. {
  2091.     m_nPosition = 0;
  2092.     m_nStreamSize = 0;
  2093. }
  2094.  
  2095. void CHtmlStream::InitStream()
  2096. {
  2097.     // subclass can override for interesting applications
  2098. }
  2099.  
  2100. ///////////////////////////////////////////////////////////////////////
  2101. // HTTP Filter entry points
  2102.  
  2103. extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
  2104.     DWORD dwNotificationType, LPVOID pvNotification)
  2105. {
  2106. #ifdef _AFXDLL
  2107.     AFX_MANAGE_STATE(AfxGetStaticModuleState());
  2108. #endif
  2109.  
  2110.     DWORD dwRet;
  2111.  
  2112.     ISAPIASSERT(pFilter != NULL);
  2113.     if (pFilter == NULL)
  2114.         dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
  2115.     else
  2116.         dwRet = pFilter->HttpFilterProc(pfc,
  2117.             dwNotificationType, pvNotification);
  2118.  
  2119.     return dwRet;
  2120. }
  2121.  
  2122. extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
  2123. {
  2124. #ifdef _AFXDLL
  2125.     AFX_MANAGE_STATE(AfxGetStaticModuleState());
  2126. #endif
  2127.  
  2128.     BOOL bRet;
  2129.  
  2130.     ISAPIASSERT(pFilter != NULL);
  2131.     if (pFilter == NULL)
  2132.         bRet = FALSE;
  2133.     else
  2134.         bRet = pFilter->GetFilterVersion(pVer);
  2135.  
  2136.     return bRet;
  2137. }
  2138.  
  2139.  
  2140. ///////////////////////////////////////////////////////////////////////
  2141. // CHttpFilter implementation
  2142.  
  2143. CHttpFilter::CHttpFilter()
  2144. {
  2145.     ISAPIASSERT(pFilter == NULL);
  2146.     pFilter = this;
  2147. }
  2148.  
  2149. CHttpFilter::~CHttpFilter()
  2150. {
  2151.     pFilter = NULL;
  2152. }
  2153.  
  2154. BOOL CHttpFilter::GetFilterVersion(PHTTP_FILTER_VERSION pVer)
  2155. {
  2156.     pVer->dwFlags = SF_NOTIFY_ORDER_DEFAULT;
  2157.     pVer->dwFilterVersion = HTTP_FILTER_REVISION;
  2158.     pVer->lpszFilterDesc[0] = '\0';
  2159.     return TRUE;
  2160. }
  2161.  
  2162. DWORD CHttpFilter::HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
  2163.     DWORD dwNotificationType, LPVOID pvNotification)
  2164. {
  2165.     DWORD dwRet = SF_STATUS_REQ_NEXT_NOTIFICATION;
  2166.     CHttpFilterContext callCtxt(pfc);
  2167.  
  2168.     switch (dwNotificationType)
  2169.     {
  2170.     case SF_NOTIFY_READ_RAW_DATA:
  2171.         dwRet = OnReadRawData(&callCtxt, (PHTTP_FILTER_RAW_DATA) pvNotification);
  2172.         break;
  2173.  
  2174.     case SF_NOTIFY_PREPROC_HEADERS:
  2175.         dwRet = OnPreprocHeaders(&callCtxt,
  2176.             (PHTTP_FILTER_PREPROC_HEADERS) pvNotification);
  2177.         break;
  2178.  
  2179.     case SF_NOTIFY_AUTHENTICATION:
  2180.         dwRet = OnAuthentication(&callCtxt,
  2181.             (PHTTP_FILTER_AUTHENT) pvNotification);
  2182.         break;
  2183.  
  2184.     case SF_NOTIFY_URL_MAP:
  2185.         dwRet = OnUrlMap(&callCtxt, (PHTTP_FILTER_URL_MAP) pvNotification);
  2186.         break;
  2187.  
  2188.     case SF_NOTIFY_SEND_RAW_DATA:
  2189.         dwRet = OnSendRawData(&callCtxt, (PHTTP_FILTER_RAW_DATA) pvNotification);
  2190.         break;
  2191.  
  2192.     case SF_NOTIFY_LOG:
  2193.         dwRet = OnLog(&callCtxt, (PHTTP_FILTER_LOG) pvNotification);
  2194.         break;
  2195.  
  2196.     case SF_NOTIFY_END_OF_NET_SESSION:
  2197.         dwRet = OnEndOfNetSession(&callCtxt);
  2198.         break;
  2199.  
  2200.     case SF_NOTIFY_END_OF_REQUEST:
  2201.         dwRet = OnEndOfRequest(&callCtxt);
  2202.         break;
  2203.  
  2204.     default:
  2205.         ISAPITRACE1("Warning: unrecognized HTTP filter notification code %d\n", dwNotificationType);
  2206.         break;
  2207.     }
  2208.  
  2209.     return dwRet;
  2210. }
  2211.  
  2212. // The user will override these.  Here, the functions have no
  2213. // formal parameters to avoid warnings.
  2214.  
  2215. DWORD CHttpFilter::OnReadRawData(CHttpFilterContext*, PHTTP_FILTER_RAW_DATA)
  2216. {
  2217.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2218. }
  2219.  
  2220. DWORD CHttpFilter::OnPreprocHeaders(CHttpFilterContext*, PHTTP_FILTER_PREPROC_HEADERS)
  2221. {
  2222.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2223. }
  2224.  
  2225. DWORD CHttpFilter::OnAuthentication(CHttpFilterContext*, PHTTP_FILTER_AUTHENT)
  2226. {
  2227.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2228. }
  2229.  
  2230. DWORD CHttpFilter::OnUrlMap(CHttpFilterContext*, PHTTP_FILTER_URL_MAP)
  2231. {
  2232.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2233. }
  2234.  
  2235. DWORD CHttpFilter::OnSendRawData(CHttpFilterContext*, PHTTP_FILTER_RAW_DATA)
  2236. {
  2237.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2238. }
  2239.  
  2240. DWORD CHttpFilter::OnLog(CHttpFilterContext*, PHTTP_FILTER_LOG)
  2241. {
  2242.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2243. }
  2244.  
  2245. DWORD CHttpFilter::OnEndOfNetSession(CHttpFilterContext*)
  2246. {
  2247.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2248. }
  2249.  
  2250. DWORD CHttpFilter::OnEndOfRequest(CHttpFilterContext*)
  2251. {
  2252.     return SF_STATUS_REQ_NEXT_NOTIFICATION;
  2253. }
  2254.  
  2255.  
  2256. ///////////////////////////////////////////////////////////////////////
  2257. // tracing helper function for linking without MFC
  2258.  
  2259. #ifndef _AFX
  2260. #ifdef _DEBUG
  2261.  
  2262. void AFXISAPI_CDECL AfxISAPITrace(LPCTSTR lpszFormat, ...)
  2263. {
  2264.     va_list args;
  2265.     va_start(args, lpszFormat);
  2266.  
  2267.     // if the trace has been set to go to a window and the user
  2268.     // presses RETRY, we will break to the debugger here
  2269.  
  2270.     if (_CrtDbgReport(_CRT_WARN, NULL, 0, NULL, lpszFormat, args) == 1)
  2271.         _CrtDbgBreak();
  2272.  
  2273.     va_end(args);
  2274. }
  2275.  
  2276. #endif
  2277. #endif
  2278.  
  2279. ///////////////////////////////////////////////////////////////////////
  2280. // handle inline functions
  2281.  
  2282. #ifndef _AFX_ENABLE_INLINES
  2283.  
  2284. static const char _szAfxWinInl[] = "isapi.inl";
  2285. #undef THIS_FILE
  2286. #define THIS_FILE _szAfxWinInl
  2287. #define _AFXISAPI_INLINE
  2288. #include "afxisapi.inl"
  2289.  
  2290. #endif //!_AFX_ENABLE_INLINES
  2291.