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