home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / ISAPI.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-03  |  49.2 KB  |  2,070 lines

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