Figure 2 Server Notifications and Corresponding CHttpFilter Members and their Parameters
Notification | Meaning | CHttpFilter Member | Structure |
SF_NOTIFY_PREPROC_HEADERS | The server is about to begin processing infor mation provided in the HTTP headers of the client request. | OnPreprocHeaders | HTTP_FILTER_PREPROC_HEADERS |
SF_NOTIFY_SEND_RAW_DATA | The server is about to send data to the client. | OnSendRawData | HTTP_FILTER_RAW_DATA |
SF_NOTIFY_READ_RAW_DATA | The server has received data from the client and is about to process it. | OnReadRawData | HTTP_FILTER_RAW_DATA |
SF_NOTIFY_AUTHENTICATION | The server is authenti cating a user. | OnAuthentication | HTTP_FILTER_AUTHENT |
SF_NOTIFY_URL_MAP | The server is mapping a URL from the client's request for a logical URL to a physical path on the server. | OnUrlMap | HTTP_FILTER_URL_MAP |
SF_NOTIFY_LOG | The server is terminating the connection to the client. | OnLog | HTTP_FILTER_LOG |
SF_NOTIFY_END_OF_NET_SESSION | The server is writing data to the log about the current session. | OnEndOfNetSession | (none) |
Figure 3 ISAPI Filter Context
//////////////////
// Filter context structuure
// The server calls your HttpFilterProc with a pointer to one of these.
//
struct HTTP_FILTER_CONTEXT {
DWORD cbSize; // size in bytes
DWORD Revision; // struct revision level
PVOID ServerContext; // server-private info
DWORD ulReserved; // reserved for server: do not use!
BOOL fIsSecurePort; // port is authenticated ?
PVOID pFilterContext; // (your) filter-private info
// Server callbacks
BOOL (WINAPI * GetServerVariable) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszVariableName,
LPVOID lpvBuffer,
LPDWORD lpdwSize);
BOOL (WINAPI * AddResponseHeaders) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPSTR lpszHeaders,
DWORD dwReserved);
BOOL (WINAPI * WriteClient) (
struct _HTTP_FILTER_CONTEXT * pfc,
LPVOID Buffer,
LPDWORD lpdwBytes,
DWORD dwReserved);
VOID * (WINAPI * AllocMem) (
struct _HTTP_FILTER_CONTEXT * pfc,
DWORD cbSize,
DWORD dwReserved);
BOOL (WINAPI * ServerSupportFunction) (
struct _HTTP_FILTER_CONTEXT * pfc,
enum SF_REQ_TYPE sfReq,
PVOID pData,
DWORD ul1,
DWORD ul2);
};
////////////////
// MFC wrapper for HTTP_FILTER_CONTEXT
//
class CHttpFilter {
public:
CHttpFilterContext(PHTTP_FILTER_CONTEXT pfc);
~CHttpFilterContext() { }
BOOL GetServerVariable(LPTSTR lpszVariableName, LPVOID lpvBuffer,
LPDWORD lpdwSize);
BOOL AddResponseHeaders(LPTSTR lpszHeaders, DWORD dwReserved = 0);
BOOL WriteClient(LPVOID lpvBuffer, LPDWORD lpdwBytes,
DWORD dwReserved = 0);
LPVOID AllocMem(DWORD cbSize, DWORD dwReserved = 0);
BOOL ServerSupportFunction(enum SF_REQ_TYPE sfReq,
LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType);
// pointer to the actual HTTP_FILTER_CONTEXT
PHTTP_FILTER_CONTEXT const m_pFC;
};
Figure 4 MFC Filer Class
//////////////////
// MFC class for ISAPI filters
// To implement a filter, you override the appropriate virtual functions.
//
// You should instantiate one of these in your filter module,
// just like "theApp".
//
class CHttpFilter {
public:
CHttpFilter();
~CHttpFilter();
// Virtual analogs for DLL entry points
virtual DWORD HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
DWORD dwNotificationType, LPVOID pvNotification);
virtual BOOL GetFilterVersion(PHTTP_FILTER_VERSION pVer);
// Specific virtual notifcation handlers
virtual DWORD OnReadRawData(CHttpFilterContext* pfc,
PHTTP_FILTER_RAW_DATA pRawData);
virtual DWORD OnPreprocHeaders(CHttpFilterContext* pfc,
PHTTP_FILTER_PREPROC_HEADERS pHeaders);
virtual DWORD OnAuthentication(CHttpFilterContext* pfc,
PHTTP_FILTER_AUTHENT pAuthent);
virtual DWORD OnUrlMap(CHttpFilterContext* pfc,
PHTTP_FILTER_URL_MAP pUrlMap);
virtual DWORD OnSendRawData(CHttpFilterContext* pfc,
PHTTP_FILTER_RAW_DATA pRawData);
virtual DWORD OnLog(CHttpFilterContext* pfc, PHTTP_FILTER_LOG pLog);
virtual DWORD OnEndOfNetSession(CHttpFilterContext* pfc);
};
Figure 5 OLDBROW
OLDBROW.RC
//Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
"\r\n"
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
"#include ""OldBrow.rc2"" // non-MS Visual C++ edited resources\r\n"
"#include ""afxres.rc"" // Standard components\r\n"
"#include ""afxisapi.rc"" // Internet Support resources\r\n"
"#endif"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Microsoft\0"
VALUE"FileDescription","OldBrowInternetServerExtensionModule\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "OLDBROW\0"
VALUE "LegalCopyright", "Copyright © 1996 Microsoft\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "OLDBROW.DLL\0"
VALUE "ProductName", "OldBrow Internet Server Extension\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE DISCARDABLE
BEGIN
IDS_FILTER "OldBrow Filter"
END
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#include "OldBrow.rc2" // non-Microsoft Visual C++ edited resources
#include "afxres.rc" // Standard components
#include "afxisapi.rc" // Internet Support resources
#endif
#endif // not APSTUDIO_INVOKED
OLDBROW.RC2
//
// OldBrow.RC2 - resources Microsoft Visual C++ does not edit directly
//
#ifdef APSTUDIO_INVOKED
#error this file is not editable by Microsoft Visual C++
#endif //APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
// Add manually edited resources here...
/////////////////////////////////////////////////////////////////////////////
OLDBROW.H
// OLDBROW.H - Implementation file for your Internet Server
// OldBrow Filter
class COldBrowFilter : public CHttpFilter
{
public:
COldBrowFilter();
~COldBrowFilter();
BOOL GetFilterVersion(PHTTP_FILTER_VERSION pVer);
DWORD OnUrlMap(CHttpFilterContext* pCtxt,
PHTTP_FILTER_URL_MAP pMapInfo);
// TODO: Add your own overrides here
};
OLDBROW.CPP
// OLDBROW.CPP - Implementation file for your Internet Server
// OldBrow Filter
#include <afx.h>
#include <afxwin.h>
#include <afxisapi.h>
#include "resource.h"
#include "OldBrow.h"
// NOTE!
// You will (probably) need to change this #define to point at the
// right file as installed on your system. You'll get "Not found"
// errors from your browser if it isn't working.
#define TOO_OLD "C:\\INETSRV\\WWWROOT\\OLDBROW.HTM"
///////////////////////////////////////////////////////////////////////
// The one and only COldBrowFilter object
COldBrowFilter theFilter;
///////////////////////////////////////////////////////////////////////
// COldBrowFilter implementation
COldBrowFilter::COldBrowFilter()
{
}
COldBrowFilter::~COldBrowFilter()
{
}
BOOL COldBrowFilter::GetFilterVersion(PHTTP_FILTER_VERSION pVer)
{
// Call default implementation for initialization
CHttpFilter::GetFilterVersion(pVer);
// Clear the flags set by base class
pVer->dwFlags &= ~SF_NOTIFY_ORDER_MASK;
// Set the flags we are interested in
pVer->dwFlags |= SF_NOTIFY_ORDER_LOW | SF_NOTIFY_SECURE_PORT |
SF_NOTIFY_NONSECURE_PORT | SF_NOTIFY_URL_MAP;
// Load description string
TCHAR sz[SF_MAX_FILTER_DESC_LEN+1];
ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),
IDS_FILTER, sz, SF_MAX_FILTER_DESC_LEN));
_tcscpy(pVer->lpszFilterDesc, sz);
return TRUE;
}
DWORD COldBrowFilter::OnUrlMap(CHttpFilterContext* pCtxt,
PHTTP_FILTER_URL_MAP pMapInfo)
{
char szUserAgent[1024];
DWORD dwSize = sizeof(szUserAgent);
if (pCtxt->GetServerVariable("HTTP_USER_AGENT", szUserAgent, &dwSize))
{
// Old versions of Microsoft Internet Explorer say "Microsoft
// Internet Explorer" in their User-Agent: header. We'll not
// allow access from those browsers because the user should
// upgrade to Microsoft Internet Explorer 2.0, which uses
// the abbreviation "MSIE" instead.
szUserAgent[dwSize] = '\0';
if (strncmp(szUserAgent,"Microsoft Internet Explorer", 25) == 0)
strncpy(pMapInfo->pszPhysicalPath, TOO_OLD, pMapInfo->cbPathBuff-1);
}
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
///////////////////////////////////////////////////////////////////////
// If your extension will not use MFC, you'll need this code to make
// sure the extension objects can find the resource handle for the
// module. If you convert your extension to not be dependent on MFC,
// remove the comments around the following AfxGetResourceHandle()
// and DllMain() functions, as well as the g_hInstance global.
/****
static HINSTANCE g_hInstance;
HINSTANCE AFXISAPI AfxGetResourceHandle()
{
return g_hInstance;
}
BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason,
LPVOID lpReserved)
{
if (ulReason == DLL_PROCESS_ATTACH)
{
g_hInstance = hInst;
}
return TRUE;
}
****/
Figure 7 Basic Structures for Server Apps
////////////////
// Version info structure for GetExtensionVersion
//
struct HSE_VERSION_INFO {
DWORD dwExtensionVersion;
CHAR lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN];
};
////////////////
// The server calls your HttpExtensionProc with one of these
//
struct EXTENSION_CONTROL_BLOCK {
DWORD cbSize; // size of this struct.
DWORD dwVersion; // version info of this spec
HCONN ConnID; // Context number not to be modified!
DWORD dwHttpStatusCode; // HTTP Status code
// log info specific to this Extension DLL
CHAR lpszLogData[HSE_LOG_BUFFER_LEN];
LPSTR lpszMethod; // REQUEST_METHOD
LPSTR lpszQueryString; // QUERY_STRING
LPSTR lpszPathInfo; // PATH_INFO
LPSTR lpszPathTranslated; // PATH_TRANSLATED
DWORD cbTotalBytes; // Total bytes indicated from client
DWORD cbAvailable; // Available number of bytes
LPBYTE lpbData; // pointer to cbAvailable bytes
LPSTR lpszContentType; // Content type of client data
BOOL (WINAPI * GetServerVariable) (
HCONN hConn,
LPSTR lpszVariableName,
LPVOID lpvBuffer,
LPDWORD lpdwSize );
BOOL (WINAPI * WriteClient) (
HCONN ConnID,
LPVOID Buffer,
LPDWORD lpdwBytes,
DWORD dwReserved );
BOOL (WINAPI * ReadClient) (
HCONN ConnID,
LPVOID lpvBuffer,
LPDWORD lpdwSize );
BOOL (WINAPI * ServerSupportFunction) (
HCONN hConn,
DWORD dwHSERRequest,
LPVOID lpvBuffer,
LPDWORD lpdwSize,
LPDWORD lpdwDataType );
};
Figure 8 MFC Classes for Server Apps
///////////////////////////////////////////////////////////////////////
// Internet Information Server Extension Support
//
class CHttpServer {
public:
CHttpServer(TCHAR cDelimiter = '&');
~CHttpServer();
enum errors {
callOK = 0, // everything is fine
callParamRequired, // a required parameter was missing
callBadParamCount, // there were too many or too few parameters
callBadCommand, // the command name was not found
callNoStackSpace, // no stack space was available
callNoStream, // no CHtmlStream was available
callMissingQuote, // a parameter had a bad format
callMissingParams, // no parameters were available
callBadParam, // a paremeter had a bad format (ie, only one quote)
};
// overridables
virtual int CallFunction(CHttpServerContext* pCtxt,
LPTSTR pszQuery, LPTSTR pszCommand);
virtual BOOL OnParseError(CHttpServerContext* pCtxt, int nCause);
// operations
virtual void EndContent(CHttpServerContext* pCtxt) const;
virtual void StartContent(CHttpServerContext* pCtxt) const;
virtual void WriteTitle(CHttpServerContext* pCtxt) const;
virtual LPCTSTR GetTitle() const;
void AddHeader(CHttpServerContext* pCtxt, LPCTSTR pszString) const;
virtual DWORD HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB);
virtual BOOL GetExtensionVersion(HSE_VERSION_INFO *pVer);
virtual CHtmlStream* ConstructStream();
virtual BOOL InitInstance(CHttpServerContext* pCtxt);
// implementation
protected:
UINT PASCAL GetStackSize(const BYTE* pbParams);
int CallMemberFunc(CHttpServerContext* pCtxt,
const AFX_PARSEMAP_ENTRY* pEntry,
AFX_PARSEMAP_ENTRY* pParams, LPTSTR szParams);
LPTSTR GetQuery(CHttpServerContext* pCtxt,
LPTSTR lpszQuery, DWORD cbQuery);
const AFX_PARSEMAP_ENTRY* LookUp(LPCTSTR szMethod,
const AFX_PARSEMAP*& pMap, AFX_PARSEMAP_ENTRY*& pParams,
AFX_PISAPICMD pCmdDefault = NULL);
int CountParams(LPCTSTR pszCommandLine, int& nCount);
int ParseDefaultParams(AFX_PARSEMAP_ENTRY* pParams,
int nParams, AFX_PARSEMAP_ENTRY_PARAMS*& pBlock,
const BYTE* pbTypes);
LPVOID PreprocessString(LPTSTR psz);
void BuildStatusCode(LPTSTR szResponse, DWORD dwCode);
#if defined(_PPC_) || defined(_MPPC_)
int PushDefaultStackArgs(BYTE* pStack,
CHttpServerContext* pCtxt, const BYTE* pbParams,
LPTSTR lpszParams, AFX_PARSEMAP_ENTRY_PARAMS* pDefParams,
int nSizeArgs);
int PushStackArgs(BYTE* pStack, CHttpServerContext* pCtxt,
const BYTE* pbParams, LPTSTR lpszParams, UINT nSizeArgs);
BYTE* StoreStackParameter(BYTE* pStack, BYTE nType,
LPTSTR pszCurParam, UINT nSizeArgs, BOOL bDoShadow);
BYTE* StoreRawStackParameter(BYTE* pStack, BYTE nType,
BYTE* pRawParam, int nSizeArgs);
#else
int PushDefaultStackArgs(BYTE* pStack,
CHttpServerContext* pCtxt, const BYTE* pbParams,
LPTSTR lpszParams, AFX_PARSEMAP_ENTRY_PARAMS* pDefParams);
int PushStackArgs(BYTE* pStack, CHttpServerContext* pCtxt,
const BYTE* pbParams, LPTSTR lpszParams);
BYTE* StoreStackParameter(BYTE* pStack, BYTE nType, LPTSTR pszParam);
BYTE* StoreRawStackParameter(BYTE* pStack, BYTE nType, BYTE* pRawParam);
#endif
LPCRITICAL_SECTION m_pCritSec;
const TCHAR m_cTokenDelimiter; // can't EVER change
DECLARE_PARSE_MAP()
};
//////////////////
// MFC wrapper for server context (EXTENSION_CONTROL_BLOCK)
//
class CHttpServerContext {
public:
CHttpServerContext(EXTENSION_CONTROL_BLOCK* pECB);
virtual ~CHttpServerContext();
// Operations
BOOL GetServerVariable(LPTSTR lpszVariableName,
LPVOID lpvBuffer, LPDWORD lpdwSize);
BOOL WriteClient(LPVOID lpvBuffer,LPDWORD lpdwBytes,DWORD dwReserved = 0);
BOOL ReadClient(LPVOID lpvBuffer, LPDWORD lpdwSize);
BOOL ServerSupportFunction(DWORD dwHSERRequest,
LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType);
CHttpServerContext& operator<<(LPCTSTR psz);
CHttpServerContext& operator<<(long int dw);
CHttpServerContext& operator<<(short int w);
CHttpServerContext& operator<<(CHtmlStream& stream);
CHttpServerContext& operator<<(double d);
CHttpServerContext& operator<<(float f);
void Reset();
// Attributes
EXTENSION_CONTROL_BLOCK* const m_pECB;
CHtmlStream* m_pStream;
DWORD m_dwEndOfHeaders;
};
Figure 9 MFC Arg Types
ITS_EMPTY | no parameters |
ITS_I2 | short |
ITS_I4 | long |
ITS_R4 | float |
ITS_R8 | double |
ITS_PSTR | LPCTSTR |
Figure 10 MFCTALK
MFCTALK.RC
//Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
"\r\n"
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
"#include ""MFCTalk.rc2"" // non-MS Visual C++ edited resources\r\n"
"#include ""afxres.rc"" // Standard components\r\n"
"#include ""afxisapi.rc"" // Internet Support resources\r\n"
"#endif"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Microsoft\0"
VALUE"FileDescription","MFCTalkInternetServerExtensionModule\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "MFCTALK\0"
VALUE "LegalCopyright", "Copyright © 1996 Microsoft\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "MFCTALK.DLL\0"
VALUE "ProductName", "MFCTalk Internet Server Extension\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE DISCARDABLE
BEGIN
IDS_SERVER "MFCTalk Extension"
END
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#include "MFCTalk.rc2" // non-Microsoft Visual C++ edited resources
#include "afxres.rc" // Standard components
#include "afxisapi.rc" // Internet Support resources
#endif
#endif // not APSTUDIO_INVOKED
MFCTALK.RC2
//
// MFCTalk.RC2 - resources Microsoft Visual C++ does not edit directly
//
#ifdef APSTUDIO_INVOKED
#error this file is not editable by Microsoft Visual C++
#endif //APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
// Add manually edited resources here...
/////////////////////////////////////////////////////////////////////////////
MFCTALK.H
// MFCTALK.H - Implementation file for your Internet Server
// MFCTalk Extension
class CMFCTalkExtension : public CHttpServer
{
public:
CMFCTalkExtension();
~CMFCTalkExtension();
BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer);
protected:
CCriticalSection m_ListCritical;
CStringList m_ListContent;
protected:
void GetCharacter();
void WriteChatForm(CHttpServerContext* pCtxt);
void WriteRoomContent(CHttpServerContext* pCtxt, int nTimes);
void AddComment(LPCTSTR pstrName, LPCTSTR pstrSays);
public:
void StartContent(CHttpServerContext* pCtxt);
void Default(CHttpServerContext* pCtxt, int nTimes);
void Comment(CHttpServerContext* pCtxt, LPCTSTR pstr);
void Refresh(CHttpServerContext* pCtxt, int nTimes);
DECLARE_PARSE_MAP()
};
MFCTALK.CPP
// MFCTALK.CPP - Implementation file for your Internet Server
// MFCTalk Extension
#include <afx.h>
#include <afxwin.h>
#include <afxisapi.h>
#include <afxmt.h> // for locking
#include <winsock.h> // for address translation
#include "resource.h"
#include "MFCTalk.h"
#pragma comment(lib, "wsock32.lib") // for winsock
#define MAX_LIST_SIZE 20
///////////////////////////////////////////////////////////////////////
// command-parsing map
BEGIN_PARSE_MAP(CMFCTalkExtension, CHttpServer)
ON_PARSE_COMMAND(Default, CMFCTalkExtension, ITS_I4)
ON_PARSE_COMMAND_PARAMS("Times=0")
DEFAULT_PARSE_COMMAND(Default, CMFCTalkExtension)
ON_PARSE_COMMAND(Refresh, CMFCTalkExtension, ITS_I4)
ON_PARSE_COMMAND(Comment, CMFCTalkExtension, ITS_PSTR)
ON_PARSE_COMMAND_PARAMS("Says")
END_PARSE_MAP(CMFCTalkExtension)
///////////////////////////////////////////////////////////////////////
// The one and only CMFCTalkExtension object
CMFCTalkExtension theExtension;
///////////////////////////////////////////////////////////////////////
// CMFCTalkExtension implementation
CMFCTalkExtension::CMFCTalkExtension()
{
}
CMFCTalkExtension::~CMFCTalkExtension()
{
m_ListContent.RemoveAll();
}
BOOL CMFCTalkExtension::GetExtensionVersion(HSE_VERSION_INFO* pVer)
{
// Call default implementation for initialization
CHttpServer::GetExtensionVersion(pVer);
// Load description string
TCHAR sz[HSE_MAX_EXT_DLL_NAME_LEN+1];
ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),
IDS_SERVER, sz, HSE_MAX_EXT_DLL_NAME_LEN));
_tcscpy(pVer->lpszExtensionDesc, sz);
srand(1234); // how random!
return TRUE;
}
///////////////////////////////////////////////////////////////////////
// CMFCTalkExtension command handlers
void CMFCTalkExtension::GetCharacter()
{
// make a person who isn't here say something funny
int nPerson = rand() % 7;
LPTSTR strPerson;
LPTSTR strSays;
switch (nPerson)
{
case 0:
strPerson = "Marilyn M.";
strSays = "Hello, Sailor!";
break;
case 1:
strPerson = "Richard N.";
strSays = "People have gotta know if their president is a crook.";
break;
case 2:
strPerson = "Bill";
strSays = "Does anybody know what we closed at?";
break;
case 3:
strPerson = "Mike B.";
strSays = "I'm the man!";
break;
case 4:
strPerson = "John K.";
strSays = "What can I do for my country?";
break;
case 5:
strPerson = "Charles B.";
strSays="Somedaysyou'reSuperman,andsomedaysyou'reClarkKent.";
break;
case 6:
strPerson = "Flea";
strSays = "You've got to play music like you really mean it.";
break;
}
AddComment(strPerson, strSays);
return;
}
void CMFCTalkExtension::WriteChatForm(CHttpServerContext* pCtxt)
{
// dump HTML to make the form
*pCtxt << "<h2>Welcome to MFCTalk!</h2><hr>";
*pCtxt << "<FORM ACTION=\"MFCTalk.dll?Comment\" \"METHOD=\"POST\">";
*pCtxt << "Type something you'd like to share:<p>";
*pCtxt << "<INPUT TYPE=\"text\" NAME=\"Says\" SIZE=60><p>";
*pCtxt << "<INPUT TYPE=\"submit\" VALUE=\"Send\">";
*pCtxt << "</FORM><FORM ACTION=\"MFCTalk.dll?Refresh\"
\"METHOD=\"POST\">";
*pCtxt << "<INPUT TYPE=\"submit\" VALUE=\"Refresh\">";
*pCtxt << "Show Message Times<INPUT TYPE=\"checkbox\" NAME=\"Times\"
CHECKED=\"1\">";
*pCtxt << "</FORM><hr>";
}
void CMFCTalkExtension::WriteRoomContent(CHttpServerContext* pCtxt, int nTimes)
{
// through the list backwards (because that's time order)
m_ListCritical.Lock();
POSITION pos;
pos = m_ListContent.GetTailPosition();
// print something nice if there's no list
if (pos == NULL)
*pCtxt << "<i>Nobody has said anything yet.</i><p>";
else
{
CString strThisOne;
while (pos != NULL)
{
LPCTSTR strThisOne = m_ListContent.GetPrev(pos);
if (nTimes == 0)
{
strThisOne = _tcschr(strThisOne, ' ');
if (strThisOne == NULL)
continue;
strThisOne++;
}
*pCtxt << strThisOne;
}
}
m_ListCritical.Unlock();
}
void CMFCTalkExtension::Default(CHttpServerContext* pCtxt, int nTimes)
{
// by default, show the form and show the content
StartContent(pCtxt);
WriteTitle(pCtxt);
WriteChatForm(pCtxt);
WriteRoomContent(pCtxt, nTimes);
EndContent(pCtxt);
}
void CMFCTalkExtension::AddComment(LPCTSTR pstrName, LPCTSTR pstrSays)
{
// someone said something... add it to the list
m_ListCritical.Lock();
CString str;
CTime timing = CTime::GetCurrentTime();
str = timing.Format("%H:%M:%S ");
str += "<b>";
str += pstrName;
str += "</b>:";
str += pstrSays;
str += "<p>";
m_ListContent.AddTail(str);
// is the list too big?
if (m_ListContent.GetCount() > MAX_LIST_SIZE)
m_ListContent.RemoveHead();
m_ListCritical.Unlock();
}
void CMFCTalkExtension::Refresh(CHttpServerContext* pCtxt, int nTimes)
{
// if we're refreshing, just have a character say something
// and dump the form and the list again
if (nTimes)
GetCharacter();
Default(pCtxt, nTimes);
}
void CMFCTalkExtension::StartContent(CHttpServerContext* pCtxt)
{
// remember to say taht the content always expires!
AddHeader(pCtxt, "Pragma: no-cache\r\n");
CHttpServer::StartContent(pCtxt);
}
void CMFCTalkExtension::Comment(CHttpServerContext* pCtxt, LPCTSTR pstr)
{
StartContent(pCtxt);
WriteTitle(pCtxt);
// l-trim what they said
while (_istspace(*pstr))
pstr++;
if (*pstr != NULL)
{
TCHAR sz[80];
DWORD dwSize = 80;
// query the user's IP address
if (!pCtxt->GetServerVariable("REMOTE_HOST", sz, &dwSize))
_tcscpy(sz, "<i>unknown</i>");
else
{
// convert to binary address
char nAddr[4];
nAddr[0] = nAddr[1] = nAddr[2] = nAddr[3] = 0;
int nIndex = 0;
char *pstr = sz;
while (*pstr != '\0' && nIndex < 4)
{
if (*pstr == '.')
nIndex++;
else
{
nAddr[nIndex] *= 10;
nAddr[nIndex] += (*pstr - '0');
}
pstr++;
}
// ask WinSock for host name
HOSTENT* pResult;
pResult = gethostbyaddr((const char*) &nAddr, 4,PF_INET);
if (pResult == NULL)
_tcscat(sz, " <i>(unresolved)</i>");
else
_tcscpy(sz, pResult->h_name);
}
// finally, add it!
AddComment(sz, pstr);
}
// rewrite everything
WriteChatForm(pCtxt);
WriteRoomContent(pCtxt, 1);
EndContent(pCtxt);
}
///////////////////////////////////////////////////////////////////////
// If your extension will not use MFC, you'll need this code to make
// sure the extension objects can find the resource handle for the
// module. If you convert your extension to not be dependent on MFC,
// remove the comments around the following AfxGetResourceHandle()
// and DllMain() functions, as well as the g_hInstance global.
/****
static HINSTANCE g_hInstance;
HINSTANCE AFXISAPI AfxGetResourceHandle()
{
return g_hInstance;
}
BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason,
LPVOID lpReserved)
{
if (ulReason == DLL_PROCESS_ATTACH)
{
g_hInstance = hInst;
}
return TRUE;
}
****/
Figure A MFC Classes for ISAPI
New MFC Class | Description |
CHttpServer | Creates an Internet server extension |
CHttpFilter | Creates an Internet server filter to screen messages sent to and from an Internet server |
CHttpServerContext | CHttpServer uses this to handle concurrent multiple requests |
CHttpFilterContext | CHttpFilter uses this to handle concurrent multiple requests |
CHtmlStream | Used by CHttpServer to send an HTML stream back to the client |
Figure B Sampling of Third-party Controls that Ship with Visual C++
OLE Control | Company | Description |
Drag-it/OCX | Kelro Software | Lets your users draw a model of their data using symbols you provide on a customer toolbox. |
InterAct | ProtoView Development Corp. | InterAct is a versatile programming object that allows users to create a wide variety of diagrams. |
LEADTOOLS OCX | LEAD Technologies Inc. | Comprehensive support for raster images with 150+ functions in seven imaging categories. |
Mh3dGauge | MicroHelp, Inc. | Creates user-defined gauges with a choice of linear (filled) or needle styles. |
MList2 | Desaware, Inc. | List box control with support for bitmaps, icons, multiple colors, and more. |
Non-Rectangle Arrow Button | ASP Corp. | Provides a three-dimensional arrow and buttons of other shapes. |
PICS Date Edit | ProtoView Development Corp. | The PICS Date Edit control allows date values to be entered and formatted. It provides a drop down calendar to choose dates from and a selection of display styles. |
Sax Basic Engine | Sax Software Corp. | Allows the addition of macro language support to an application. |
SmartHelp | Blue Sky Software | Simply drag the Help button into an application's dialog boxes and screens and visually select any Help file. |
VideoPlay/OCX | Media Architects, Inc. | An easy way to integrate AVI, Quicktime, or MPEG video playback without sacrificing critical functionality. |
Visual 3Space Browser Control | Template Graphics Software | A control that lets you integrate 3D and VRML into your apps. |
Visual Voice for TAPI Solo | Stylus Innovation, Inc. | Supports the building of telephony applications such as touch-tone data access and voice mail. |
VSFLEX | VideoSoft | FlexArray is similar to a spreadsheet, but allows total flexibility to manage tables containing strings and pictures. FlexString enables addition of regular-expression text matching into an application. |
Figure C New MFC Sample Programs
ACDUAL | Shows how to add dual-interface support to an MFC-based OLE Automation server |
BINDSCRB | Shows how to write an OLE document object for use with the Binder tool in Microsoft Office 95 applications |
DAOTABLE | Shows how to use MFC Data Access Object classes to create databases, tables, queries, fields, and indexes |
DLGTEMPL | Shows how to dynamically create and use a dialog template |
HTTPSVR | Shows how to use MFC and the Windows Sockets classes to implement a simple HTTP server |
MFCUCASE | Shows how to use MFC classes to create Internet filter DLLs |
ODBCINFO | Shows how to determine ODBC driver capabilities at run time by opening a selected ODBC data source and displaying information using property pages |
ROWLIST | Shows how to implement full-row selection in the report mode of the CListView control class |
WWWQUOTE | Shows how to write a World Wide Web application for retrieving information (in this case, stock quotes) |