Notification Type (notificationType) | Explanations | Data Structure (pvNotification) |
SF_NOTIFY_AUTHENTICATION | The server is authenticating an incoming user. This happens once per session. | HTTP_FILTER_AUTHENT |
SF_NOTIFY_END_OF_REQUEST | The server just finished processing the request. Note that the session to the client may still be open because of the HTTP Keep-Alive connection option. This notification is mainly for cleanup of any request-specific data used by your filter. . | Not used |
SF_NOTIFY_READ_RAW_DATA | The server is reading raw data from the underlying socket. By handling this notification, a filter can access the raw data being sent by the client. This notification may be posted more than once for each request. | HTTP_FILTER_RAW_DATA |
SF_NOTIFY_PREPROC_HEADERS | The server is just about to process the request's headers. Server variables will become available at the next notification for this request. | HTTP_FILTER_ PREPROC_HEADERS |
SF_NOTIFY_URL_MAP | The server is mapping the URL address to its physical path. Your filter can specify a different physical path for this request at this notification | HTTP_FILTER_URL_MAP |
SF_NOTIFY_SEND_RAW_DATA | The server is sending data back to the client on the underlying socket. This may occur more than once for each request. The filter may change outgoing data by handling this notification. | HTTP_FILTER_RAW_DATA |
SF_NOTIFY_LOG | The server is writing an entry to the IIS log LOG The filter may alter this information by handling this notification.. | HTTP_FILTER |
SF_NOTIFY_END_OF_ NET_SESSION | The session between the client and the server was closed. This notification is mainly provided to clean up any session-specific data. | Not used |
SF_NOTIFY_ACCESS_DENIED | The server couldn't access the requested document, because access was denied. A "401 Access Denied" response status is about to be sent to the client.. | HTTP_FILTER_ACCESS_DENIED |
SF_NOTIFY_SEND_RESPONSE | The server is about to send HTTP response headers to the client. The filter may alter them by handling this notification. | HTTP_FILTER_SEND_RESPONSE |
Figure 2 HttpFilterProc Return Values
Value | Description |
SF_STATUS_REQ_FINISHED | The request is finished. The server needs to close the session with the client. |
SF_STATUS_REQ_FINISHED_KEEP_CONN | Just like SF_STATUS_REQ_FINISHED, except that if the Keep-Alive option was negotiated the session should stay open. |
SF_STATUS_REQ_NEXT_NOTIFICATION | If more than one filter is installed, the next filter in the chain should be called. For example, if a filter handles raw reads or writes (to do custom data encoding, for instance), then after changing the raw data the next filter should be called (to check certain custom headers, for instance). |
SF_STATUS_REQ_HANDLED_NOTIFICATION | The notification was handled by this filter. Other notifications should not be called for the same request. |
SF_STATUS_REQ_ERROR | An error occurred in the filter. The filter should obtain an error code (usually via GetLastError) and pass it to the client. The server will abort the HTTP session if HttpFilterProc returns this value. |
SF_STATUS_REQ_READ_NEXT | The filter is an opaque stream filter, reading large amounts of data from the server. This value indicates to the server that the filter did not read all of the data. The server should post another read notification to the filter so the filter can continue reading data. |
Figure 3 HttpFilterProc
DWORD WINAPI HttpFilterProc (PHTTP_FILTER_CONTEXT pFC,
DWORD dwNotificationType,
LPVOID pvNotification)
{
HTTP_FILTER_URL_MAP *pUrlMap;
CHAR szTemp [256], *lpszUserAgent, *lpszServer;
DWORD dwSize = 0;
if (dwNotificationType == SF_NOTIFY_URL_MAP)
{
pUrlMap = (HTTP_FILTER_URL_MAP *) pvNotification;
if (strstr (strlwr ((CHAR *)pUrlMap->pszURL), "ieonly") )
{
// This call should fail w/error ERROR_INSUFFICIENT_BUFFER
// you should check, for other errors.
pFC->GetServerVariable (pFC,"HTTP_USER_AGENT",
lpszUserAgent,&dwSize);
lpszUserAgent = (CHAR *) pFC->AllocMem (pFC,dwSize,0);
// request is to IEONLY directory, check user agent
if (!pFC->GetServerVariable (pFC,"HTTP_USER_AGENT",
lpszUserAgent,&dwSize))
{
pFC->ServerSupportFunction (pFC,
SF_REQ_SEND_RESPONSE_HEADER,
(PVOID) "200 OK", 0,0);
wsprintf (szTemp, "GetServerVariable failed: %d",
GetLastError());
dwSize = lstrlen (szTemp);
pFC -> WriteClient (pFC, szTemp, &dwSize, 0);
return SF_STATUS_REQ_FINISHED;
}
if (!strstr (strlwr (lpszUserAgent), "msie 4") )
{
// not an Internet Explorer 4.0 browser, redirect client
// Get the name of the server. But first determine size
// of the buffer.
dwSize = 0;
pFC->GetServerVariable (pFC,"SERVER_NAME",
lpszServer,&dwSize);
lpszServer = (CHAR *) pFC->AllocMem (pFC,dwSize,0);
if (!pFC->GetServerVariable (pFC,"SERVER_NAME",
lpszServer,&dwSize))
{
// report error check here
return SF_STATUS_REQ_FINISHED;
}
wsprintf (szTemp, "Location: http://%s/%s\r\n\r\n",
lpszServer, FILENAME);
pFC->ServerSupportFunction (pFC,SF_REQ_SEND_RESPONSE_HEADER,
(PVOID) "302 Redirect",
(DWORD) szTemp,0);
return SF_STATUS_REQ_FINISHED;
}
}
} // if notification handler
return SF_STATUS_REQ_NEXT_NOTIFICATION;
} // end HttpFilterProc
Figure 4 Adding ISAPI Extension to SmartRedir
// declare critical section object and user agent substring
// as file scope data
CRITICAL_SECTION g_CritSect;
CHAR szUserAgentId[256];
BOOL WINAPI GetFilterVersion(. . .)
{
// critical section should be initialized first.
// It can be done either in GetFilterVersion or in DllMain()
// when it is called for PROCESS_ATTACH
InitializeCriticalSection (&g_CritSect);
// set the default user agent
strcpy (szUserAgentId, "msie 4");
•
•
•
}
DWORD WINAPI HttpFilterProc(. . .)
{
•
•
•
// implement code that searches for the user agent substring
EnterCriticalSection (&g_CritSect);
if (!strstr (strlwr (lpszUserAgent), szUserAgentId) )
{
•
•
•
}
LeaveCriticalSection (&g_CritSect);
•
•
•
}
DWORD WINAPI HttpExtensionProc(. . .)
{
•
•
•
// implement code that changes the szUserAgentId based on the
// parameters passed to our extension
EnterCriticalSection (&g_CritSect);
Change_User_Agent (szUserAgentId);
LeaveCriticalSection (&g_CritSect);
•
•
•
}
DWORD WINAPI TerminateFilter (. . .)
{
// critical section must be deleted. It can be done here
// or in DllMain, when it is called for the PROCESS_DETACH
DeleteCriticalSection (&g_CritSect);
•
•
•
}
Figure 5 SSIDemo
#include <windows.h>
#include <httpext.h>
#include <stdio.h>
#include <stdarg.h>
#pragma optimize("", off)
#define REGKEY "System\\CurrentControlSet\\Services\\W3SVC\\SSI"
#define ISWHITE( ch ) ((ch) && ((ch) == ' ' || (ch) == '\t' || \
(ch) == '\n' || (ch) == '\r'))
CHAR cVarChar = '@', *lpDataDict = NULL; // Even though it is a global, it can
// only be read from multiple threads.
DWORD GetValueFromTag (CHAR *szVal, CHAR *szTag, EXTENSION_CONTROL_BLOCK *pECB);
#ifdef _DEBUG
void DebugOut (CHAR *lpszFmt, ...)
{
CHAR szBuff[1024];
va_list vargs;
va_start (vargs,lpszFmt);
vsprintf (szBuff, lpszFmt, vargs);
va_end (vargs);
OutputDebugString (szBuff);
return;
}
#else
void DebugOut (CHAR *lpszFmt, ...)
{
return;
}
#endif
BOOL WINAPI DllMain (HANDLE hInst,
ULONG ul_reason_for_call,
LPVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_DETACH)
{
DebugOut ("DLL_PROCESS_DETACH for instance: 0x%X\n", hInst);
LocalFree (lpDataDict);
}
return TRUE;
}
/****************************************************************************
*
* FUNCTION: GetExtensionVersion
*
* PURPOSE: Standart procedure which needs to be exported from the DLL.
*
* PARAMETRS:
* pVer - pointer to version information structure.
*
* RETURN: BOOL
*
* COMMENTS: Will be called by the server.
*
****************************************************************************/
BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVer)
{
// We will open a dictinary file here and will read its
// content into the global memory.
//
// If for any reason we can't do it, set the global pointer to 0;
HANDLE hFileDict;
DWORD dwSizeDict, dwError, dwRead;
int i = 0;
HKEY hKey;
CHAR szDictFile [256], temp[2];
pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
lstrcpyn(pVer->lpszExtensionDesc,"HTTP SSI Application",
HSE_MAX_EXT_DLL_NAME_LEN );
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,REGKEY,0,KEY_READ,&hKey))
{
// We don't care if we can't read reg
dwRead = sizeof (temp);
if (RegQueryValueEx (hKey, "VariableCharacter", NULL, NULL, (LPBYTE) temp,
&dwRead) == ERROR_SUCCESS)
cVarChar = temp[0];
dwRead = sizeof (szDictFile);
if (RegQueryValueEx (hKey, "DictFile", NULL, NULL, (LPBYTE) szDictFile,
&dwRead) != ERROR_SUCCESS)
{
DebugOut ("Can't get reg value DictFile, error: %d\n",
GetLastError());
return TRUE;
}
}
else
{
DebugOut ("Could not read registry.\n, No data read, vachar is @\n");
return TRUE;
}
RegCloseKey (hKey);
Again:
dwError = GetLastError();
hFileDict = CreateFile(szDictFile, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, (HANDLE) NULL);
if ( (hFileDict == INVALID_HANDLE_VALUE) &&
(dwError == ERROR_SHARING_VIOLATION))
{
DebugOut ("File: %s is locked (sharing). Still trying...\n", szDictFile);
Sleep (100);
i++;
if (i>3)
goto Again;
else
{
lpDataDict = NULL;
return TRUE;
}
}
else if ( (hFileDict == INVALID_HANDLE_VALUE) &&
(dwError != ERROR_SHARING_VIOLATION))
{
DebugOut ("Error opening: %s Error: %d\n", szDictFile);
lpDataDict = NULL;
return TRUE;
}
dwSizeDict = GetFileSize (hFileDict, NULL);
lpDataDict = (CHAR *) LocalAlloc (LPTR, dwSizeDict + 1 );
i=0;
AgainRead:
if (!ReadFile (hFileDict, lpDataDict, dwSizeDict, &dwRead, NULL))
{
if ( (dwError = GetLastError()) == ERROR_LOCK_VIOLATION)
{
DebugOut ("File: %s is locked (lock). Still trying...\n", szDictFile);
Sleep (100);
if (i>3)
goto AgainRead;
else
{
lpDataDict = NULL;
return TRUE;
}
}
else
{
DebugOut ("Error opening: %s Error: %d\n",szDictFile,dwError);
lpDataDict = NULL;
return TRUE;
}
}
CloseHandle (hFileDict);
lpDataDict [dwRead] = '\0';
DebugOut ("Dictionary file %s is read into memory\n", szDictFile);
return TRUE;
} // GetExtensionVersion()
/****************************************************************************
*
* FUNCTION: HttpExtensionProc
*
* PURPOSE: Standart procedure which needs to be exported from the DLL.
*
* PARAMETRS:
* pECB - pointer to version extension control block.
*
* RETURN: DWORD, status code.
*
* COMMENTS: Will be called by the server.
*
****************************************************************************/
DWORD WINAPI HttpExtensionProc (EXTENSION_CONTROL_BLOCK *pECB)
{
HANDLE hFile;
DWORD dwLen=0, dwError, dwSize, dwRead;
int i = 0;
CHAR *lpData = NULL, *lpIncStart, *lpIncEnd, *lpDataForDelete;
CHAR szTagBuffer[256], szValueBuffer [256];
CHAR szBuff [256];
DebugOut ("Opening file: %s for HTML\n", pECB->lpszPathTranslated);
Again:
hFile = CreateFile(pECB->lpszPathTranslated, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, (HANDLE) NULL);
dwError = GetLastError();
if ( (hFile == INVALID_HANDLE_VALUE) && (dwError == ERROR_SHARING_VIOLATION))
{
DebugOut ("File: %s is locked (sharing). Still trying...\n",
pECB->lpszPathTranslated);
Sleep (100);
i++;
if (i>3)
goto Again;
else
return HSE_STATUS_ERROR;
}
else if ( (hFile == INVALID_HANDLE_VALUE) &&
(dwError != ERROR_SHARING_VIOLATION))
{
DebugOut ("Error opening: %s Error: %d\n",
pECB->lpszPathTranslated,dwError);
return HSE_STATUS_ERROR;
}
dwSize = GetFileSize (hFile, NULL);
lpData = (CHAR *) LocalAlloc (LPTR, dwSize + 1 );
i=0;
AgainRead:
if (!ReadFile (hFile, lpData, dwSize, &dwRead, NULL))
{
if ( (dwError = GetLastError()) == ERROR_LOCK_VIOLATION)
{
DebugOut ("File: %s is locked (lock). Still trying...\n",
pECB->lpszPathTranslated);
Sleep (100);
if (i>3)
goto AgainRead;
else
return HSE_STATUS_ERROR;
}
else
{
DebugOut ("Error opening: %s Error: %d\n",
pECB->lpszPathTranslated,dwError);
return HSE_STATUS_ERROR;
}
}
CloseHandle (hFile);
lpData[dwRead] = '\0';
// we will loose lpData as we parse the file.
// preserve its value for buffer deletion.
lpDataForDelete = lpData;
wsprintf ( szBuff, "Content-Type: text/html\r\n\r\n");
dwLen = lstrlen ( szBuff );
pECB -> ServerSupportFunction ( pECB -> ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
"200 OK",
&dwLen,
(LPDWORD) szBuff );
// this is a <!-- #TAG --> sample
while ( (lpIncStart = strstr (lpData, "<!--")) != NULL )
{
// print stuff before <!--
dwSize = (lpIncStart - lpData) / sizeof (CHAR);
pECB -> WriteClient (pECB -> ConnID, lpData, &dwSize, 0);
// find end of tag -->
if ( (lpIncEnd = strstr (lpData, "-->")) == NULL )
{
// we did not find end of tag bail out
DebugOut ("No --> found. Bailing out\n");
dwSize = lstrlen (lpData);
pECB -> WriteClient (pECB -> ConnID, lpData, &dwSize, 0);
return HSE_STATUS_SUCCESS;
}
// move lpData to the beginning of the tag
lpData = lpIncStart + lstrlen ("<!--");
while ( ISWHITE( *lpData ) )
lpData++;
// start coping the tag buffer
i = 0;
while ( ( !ISWHITE (*lpData)) && (lpData != lpIncEnd) )
szTagBuffer[i++] = *lpData++;
szTagBuffer[i]='\0';
DebugOut ("Tag found: %s\n", szTagBuffer);
lpData= lpIncEnd + lstrlen ("-->");
// output proper value here
if (szTagBuffer[0] == '#')
{
// it was a tag, get rid of the #
CHAR *p = szTagBuffer + sizeof (CHAR);
DebugOut ("Looking up: %s\n", p);
dwSize = GetValueFromTag (szValueBuffer, p, pECB);
pECB -> WriteClient (pECB -> ConnID, szValueBuffer, &dwSize, 0);
}
else
{
// it was a comment, just print it
DebugOut ("Comment found (not a tag)\n");
dwSize = (lpData - lpIncStart) / sizeof (CHAR);
pECB -> WriteClient (pECB -> ConnID, lpIncStart, &dwSize, 0);
}
}
dwSize = lstrlen (lpData);
pECB -> WriteClient (pECB -> ConnID, lpData, &dwSize, 0);
LocalFree (lpDataForDelete);
return HSE_STATUS_SUCCESS;
}
DWORD GetValueFromTag (CHAR *szValue, CHAR *szTag, EXTENSION_CONTROL_BLOCK *pECB)
{
// we are assuming that our dictionary file is small enough to fit
// into memory. This is a datafile format:
// TaG2 = This_is_tag2
// Tags proceeded with @ are the server variables.
// Tags can't have spaces.
DWORD dwLen = 256, dwError, dwTag = lstrlen (szTag);
CHAR * pch = lpDataDict;
CHAR *p;
int i;
if ( *szTag == cVarChar )
{
p=szTag + sizeof (CHAR);
// This is a server variable
if ( !pECB -> GetServerVariable (pECB -> ConnID, // Connection id
p, // Variable
szValue, // Buffer
&dwLen)) // Length
{
if ( (dwError= GetLastError ()) == ERROR_INVALID_INDEX )
wsprintf ( szValue, "<br><i>Variable %s does not exist. Please notify Webmaster about this.</i><br>", p);
else
wsprintf ( szValue, "<br><i>Error %d occured while getting %s variable</i><br>", dwError, p);
}
}
else
{
p=szTag;
// this is a real tag. Do nasty job of looking for it.
if ( !pch )
{
// Our buffer is empty
wsprintf ( szValue, "<br><i>Can't find match for %s</i><br>", p);
return lstrlen (szValue);
}
while (*pch)
{
while ( ISWHITE( *pch ) )
pch++;
if ( toupper( *pch ) == toupper( *szTag ) &&
!strnicmp( szTag, pch, dwTag ) &&
( pch [dwTag] == ' ' || pch [dwTag] == '=') )
{
pch += dwTag + 1;
goto Found;
}
pch = strchr( pch+1, '\n' );
}
Found:
while ( ISWHITE( *pch ) || *pch == '=')
pch++;
i = 0;
while ( ( *pch != '\n' ) && (i < 256) )
szValue [i++] = *pch++;
szValue[i] = '\0';
}
return lstrlen (szValue);
}
Figure 6 GetExtensionVersion
BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVer)
{
HANDLE hFileDict;
DWORD dwSizeDict, dwError, dwRead;
int i = 0;
HKEY hKey;
CHAR szDictFile [256], temp[2];
pVer->dwExtensionVersion = MAKELONG (HSE_VERSION_MINOR,
HSE_VERSION_MAJOR);
lstrcpyn(pVer->lpszExtensionDesc,"HTTP SSI Application",
HSE_MAX_EXT_DLL_NAME_LEN );
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
REGKEY,0,KEY_READ,&hKey))
{
// We don't care if we can't read registry
dwRead = sizeof (temp);
if (RegQueryValueEx (hKey, "VariableCharacter",
NULL, NULL, (LPBYTE) temp, &dwRead) == ERROR_SUCCESS)
cVarChar = temp[0];
dwRead = sizeof (szDictFile);
if (RegQueryValueEx (hKey, "DictFile", NULL, NULL,
(LPBYTE) szDictFile, &dwRead) != ERROR_SUCCESS)
{
DebugOut ("Can't get reg value DictFile, error: %d\n",
GetLastError());
return TRUE;
}
}
else
{
DebugOut ("Could not read registry.\n, "
"No data read, vachar is @\n");
return TRUE;
}
RegCloseKey (hKey);
Again:
dwError = GetLastError();
hFileDict = CreateFile(szDictFile, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
(HANDLE) NULL);
if ( (hFileDict == INVALID_HANDLE_VALUE) &&
(dwError == ERROR_SHARING_VIOLATION))
{
DebugOut ("File: %s is locked (sharing). Still trying...\n",
szDictFile);
Sleep (100);
i++;
if (i>3) // if file is locked, try 3 times
goto Again;
else
{
lpDataDict = NULL;
return TRUE;
}
}
else if ( (hFileDict == INVALID_HANDLE_VALUE) &&
(dwError != ERROR_SHARING_VIOLATION))
{
DebugOut ("Error opening: %s Error: %d\n", szDictFile);
lpDataDict = NULL;
return TRUE;
}
dwSizeDict = GetFileSize (hFileDict, NULL);
lpDataDict = (CHAR *) LocalAlloc (LPTR, dwSizeDict + 1 );
i=0;
AgainRead:
if (!ReadFile (hFileDict, lpDataDict, dwSizeDict, &dwRead, NULL))
{
if ( (dwError = GetLastError()) == ERROR_LOCK_VIOLATION)
{
DebugOut ("File: %s is locked (lock). Still trying...\n",
szDictFile);
Sleep (100);
if (i>3)
goto AgainRead;
else
{
lpDataDict = NULL;
return TRUE;
}
}
else
{
DebugOut ("Error opening: %s Error: %d\n",szDictFile,dwError);
lpDataDict = NULL;
return TRUE;
}
}
CloseHandle (hFileDict);
lpDataDict [dwRead] = '\0';
DebugOut ("Dictionary file %s is read into memory\n", szDictFile);
return TRUE;
} // GetExtensionVersion()
Figure 7 Tag Substitution Code
while ( (lpIncStart = strstr (lpData, "<!--")) != NULL )
{
// print stuff before <!--
dwSize = (lpIncStart - lpData) / sizeof (CHAR);
pECB -> WriteClient (pECB -> ConnID, lpData, &dwSize, 0);
// find end of tag -->
if ( (lpIncEnd = strstr (lpData, "-->")) == NULL )
{
// we did not find end of tag bail out
DebugOut ("No --> found. Bailing out\n");
dwSize = lstrlen (lpData);
pECB -> WriteClient (pECB -> ConnID, lpData, &dwSize, 0);
return HSE_STATUS_SUCCESS;
}
// move lpData to the beggining of the tag
lpData = lpIncStart + lstrlen ("<!--");
while ( ISWHITE( *lpData ) )
lpData++;
// start coping the tag buffer
i = 0;
while ( ( !ISWHITE (*lpData)) && (lpData != lpIncEnd) )
szTagBuffer[i++] = *lpData++;
szTagBuffer[i]='\0';
DebugOut ("Tag found: %s\n", szTagBuffer);
lpData= lpIncEnd + lstrlen ("-->");
// output proper value here
if (szTagBuffer[0] == '#')
{
// it was a tag, get rid of the #
CHAR *p = szTagBuffer + sizeof (CHAR);
DebugOut ("Looking up: %s\n", p);
dwSize = GetValueFromTag (szValueBuffer, p, pECB);
pECB -> WriteClient (pECB -> ConnID, szValueBuffer,
&dwSize, 0);
}
else
{
// it was a comment, just print it
DebugOut ("Found comment, not a tag\n");
dwSize = (lpData - lpIncStart) / sizeof (CHAR);
pECB -> WriteClient (pECB -> ConnID, lpIncStart, &dwSize, 0);
}
} // end while
dwSize = lstrlen (lpData);
pECB -> WriteClient (pECB -> ConnID, lpData, &dwSize, 0);
Figure 8 GetValueFromTag
DWORD GetValueFromTag (CHAR *szValue, CHAR *szTag,
EXTENSION_CONTROL_BLOCK *pECB)
{
DWORD dwLen = 256, dwError, dwTag = lstrlen (szTag);
CHAR * pch = lpDataDict;
CHAR *p;
int i;
if ( *szTag == cVarChar ) // check if tag starts with the
// special character, indicating server
// variable.
{
p=szTag + sizeof (CHAR);
// This is a server variable
if ( !pECB -> GetServerVariable (pECB -> ConnID, // Connection id
p, // Variable
szValue, // Buffer
&dwLen)) // Length
{
if ( (dwError= GetLastError ()) == ERROR_INVALID_INDEX )
wsprintf ( szValue, "<br><i>Variable %s does"
" not exist. Please notify Webmaster about"
" this.</i><br>",
p);
else
wsprintf ( szValue, "<br><i>Error %d occured while"
" getting %s variable</i><br>", dwError, p);
}
}
else
{
p=szTag;
// this is a real tag. Do nasty job of looking for it.
if ( !pch )
{
// Our buffer is empty
wsprintf ( szValue, "<br><i>Can't find match for %s</i><br>",
p);
return lstrlen (szValue);
}
while (*pch)
{
while ( ISWHITE( *pch ) )
pch++;
if ( toupper( *pch ) == toupper( *szTag ) &&
!strnicmp( szTag, pch, dwTag ) &&
( pch [dwTag] == ' ' || pch [dwTag] == '=') )
{
pch += dwTag + 1;
goto Found;
}
pch = strchr( pch+1, '\n' );
}
Found:
while ( ISWHITE( *pch ) || *pch == '=')
pch++;
i = 0;
while ( ( *pch != '\n' ) && (i < 256) )
szValue [i++] = *pch++;
szValue[i] = '\0';
}
return lstrlen (szValue);
}
Figure 9 HELLO.BPI Template File
<HTML>
<!-- Leon Braginski and Matt Powell (c) -->
<BODY>
<h2><center> BPI Custom Script Processor Test </h2></center><br>
My name is <b><!-- #FName --> <!-- #LName --></b><br>
I live in <b><!--#City-->, <!-- #State --> <!-- #Country --> </b> <hr>
<h2><center> Server Variables </h2></center><br>
Your browser is:<b> <!-- #@HTTP_USER_AGENT --> </b><br>
Your IP address: <b> <!-- #@REMOTE_ADDR --> </b><br>
IIS Server software: <b> <!-- #@SERVER_SOFTWARE --> </b><br>
</BODY>
</HTML>