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)