/////////////////////////////////////////////////////////////////////////////
// counter.cpp : Defines the initialization routines for the DLL.
//
// Written by Jeff Miller
// of Microsoft Product Support Services, Languages Developer Support
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
// This ISAPI DLL can be called in three ways:
//
// will return an x-bitmap image containing the current system time
//
// will return an x-bitmap image containing the accumulated
// count for the identifier /mydir/mypage.htm
//
// will return an x-bitmap image containing the accumulated
// count for the page which called the DLL.
#include
#include
#include
#include "resource.h"
#include "counter.h"
#include "charset.h"
// This program sends back xbitmap images rather than HTML.
static const TCHAR szContentType[] = _T("Content-Type: image/x-xbitmap\r\n");
// The following defines the location and filename for the log file.
// This should be placed in a location that can be read and written to.
static const TCHAR szLogFile[] = _T("C:\\counter.log");
// The following two headers will tell the client never to cache
// this information, as it is dynamic.
static const TCHAR szExpires[] = _T("Expires: Thu, 01 Jan 1995 01:00:00 GMT\r\n");
static const TCHAR szNoCache[] = _T("Pragma: no-cache\r\n");
///////////////////////////////////////////////////////////////////////
// command-parsing map
BEGIN_PARSE_MAP(CCounterExtension, CHttpServer)
ON_PARSE_COMMAND(Clock, CCounterExtension, ITS_EMPTY)
ON_PARSE_COMMAND(Default, CCounterExtension, ITS_EMPTY)
DEFAULT_PARSE_COMMAND(Default, CCounterExtension)
END_PARSE_MAP(CCounterExtension)
///////////////////////////////////////////////////////////////////////
// The one and only CCounterExtension object
CCounterExtension theExtension;
///////////////////////////////////////////////////////////////////////
// CCounterExtension implementation
CCounterExtension::CCounterExtension()
{
}
CCounterExtension::~CCounterExtension()
{
}
BOOL CCounterExtension::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);
return TRUE;
}
///////////////////////////////////////////////////////////////////////
// CCounterExtension command handlers
void CCounterExtension::Default(CHttpServerContext* pCtxt)
{
// We don't call StartContent() or WriteTitle() here due to the
// fact that those will send back tags appropriate only to
// HTML pages. We will be sending back an image.
// Don't allow these pages to be cached
AddHeader(pCtxt, szExpires);
AddHeader(pCtxt, szNoCache);
TCHAR pstrBuffer[1024];
// Check to see if a path was given after the DLL's name,
// such as
// If so, use the given path. Otherwise, use the URL of
// the page which called this DLL.
if (_tcsclen(pCtxt->m_pECB->lpszPathInfo) != 0)
{
_tcscpy(pstrBuffer, pCtxt->m_pECB->lpszPathInfo);
}
else
{
// HTTP_REFERER contains the full URL of the page which
// called this DLL.
DWORD dwSize = 1024;
pCtxt->GetServerVariable(_T("HTTP_REFERER"), pstrBuffer, &dwSize);
}
// Call member function to see how many times the requested
// page has been accessed and update that count.
// GetPageCount will return -1 if there was a problem reading the
// counter log file.
CString szPath(pstrBuffer);
int nCount = GetPageCount(szPath);
if (nCount != -1)
{
// call member function to output an xbitmap image
// containing the digits
CString szCount;
szCount.Format(_T("%d"), nCount);
OutputXBM(pCtxt, szCount);
}
// EndContent() is only appropriate for HTML files, so we
// don't call it here.
}
void CCounterExtension::Clock(CHttpServerContext* pCtxt)
{
// Clock
// Called when DLL is accessed using the format
//
// We don't call StartContent() or WriteTitle() here due to the
// fact that those will send back tags appropriate only to
// HTML pages. We will be sending back an image.
// Don't allow these pages to be cached
AddHeader(pCtxt, szExpires);
AddHeader(pCtxt, szNoCache);
// Get the current system time, and put it into a form
// which our function will accept.
CTime time = CTime::GetCurrentTime();
CString szTime = time.Format(_T("%H%%%M"));
// call member function to output an xbitmap image
// containing the digits
OutputXBM(pCtxt, szTime);
// EndContent() is only appropriate for HTML files, so we
// don't call it here.
}
void CCounterExtension::OutputXBM(CHttpServerContext* pCtxt, CString& szDigits)
{
// Function to take in a string containing the digits 0..9 and the character
// ':' and output an xbitmap image of those digits to the stream
// Start by writing the proper content type to the client
AddHeader(pCtxt, szContentType);
// NOTE: this code as is will only work properly with image data
// that has a width = 8
int nFinalWidth = char_width * szDigits.GetLength();
int nFinalHeight = char_height;
// write out the XBM header. We cast to long int because there is
// no CHttpServerContext << operator overload that accepts an
// integer.
*pCtxt << _T("#define counter_width ") << (long int)nFinalWidth << _T("\r\n");
*pCtxt << _T("#define counter_height ") << (long int)nFinalHeight << _T("\r\n");
*pCtxt << _T("static unsigned char counter_bits[] = {\r\n");
// Now for each horizontal line of output, get the bitmap for each
// character for that line and output it.
for (int nLine=0; nLine= __TEXT('0') && szDigits[nChar] <= __TEXT('9'))
nDigitOffset = szDigits[nChar] - __TEXT('0');
else
// colon is in index 10 in the bitmap array
nDigitOffset = 10;
CString szHex;
szHex.Format(_T("0x%02X, "), char_bits[nDigitOffset][nLine]);
*pCtxt << szHex;
}
}
*pCtxt << _T("};\r\n");
}
int CCounterExtension::GetPageCount(CString& szPage)
{
// Given a unique page identifier (szPage), check our "database"
// to see how many times this page has been accessed, then
// return that count. If this function fails, -1 will be returned.
CFile file;
CFileException e;
int nOpenAttempts = 0;
// make sure only one thread operates on the log file at a time
while (TRUE)
{
if (file.Open(szLogFile,
CFile::modeRead | CFile::shareExclusive
| CFile::modeCreate | CFile::modeNoTruncate, &e))
{
// we opened the file, so continue onwards
break;
}
else
{
// we couldn't open the file...figure out why
if (e.m_cause == CFileException::sharingViolation)
{
// sharing violation
// another thread must have the file open, so wait and retry
::Sleep(100);
// increase the attempt counter
nOpenAttempts++;
if (nOpenAttempts == 30)
{
// too many retries. something's amiss
CString szErrText;
szErrText.LoadString(IDS_RETRYERR);
TRACE0(szErrText);
// return error code
return -1;
}
}
else
{
// not a sharing violation error
// so it's probably serious
TCHAR szCause[255];
e.GetErrorMessage(szCause, 255);
CString szErrText;
szErrText.LoadString(IDS_OPENERR);
TRACE1(szErrText, szCause);
// return error code
return -1;
}
}
}
// we've now successfully opened the log file so let's read it!
CArchive archive(&file, CArchive::load);
// Serialize in the data from the file
try
{
m_Paths.Serialize(archive);
}
catch (CArchiveException* e)
{
// if we get endOfFile, it probably means that the counter
// log file doesn't exist yet, so it's not a fatal error
if (e->m_cause != CArchiveException::endOfFile)
{
TCHAR szCause[255];
e->GetErrorMessage(szCause, 255);
CString szErrText;
szErrText.LoadString(IDS_SERIALIZEERR);
TRACE1(szErrText, szCause);
m_Paths.RemoveAll();
archive.Close();
file.Close();
e->Delete();
return -1;
}
e->Delete();
}
archive.Close();
file.Close();
// Try to find the page identifier in the log
int nVal = 0;
CString szCount;
if (m_Paths.Lookup(szPage, szCount))
nVal = atoi(szCount);
// Increment the count.
nVal++;
// Set the new value.
szCount.Format(_T("%d"), nVal);
m_Paths.SetAt(szPage, szCount);
// Write the updated log
if ( !file.Open(szLogFile,
CFile::modeWrite | CFile::shareExclusive
| CFile::modeCreate, &e) )
{
TCHAR szCause[255];
e.GetErrorMessage(szCause, 255);
CString szErrText;
szErrText.LoadString(IDS_OPENERR);
TRACE1(szErrText, szCause);
// return error code
return -1;
}
CArchive archiveStore(&file, CArchive::store);
try
{
m_Paths.Serialize(archiveStore);
}
catch (CArchiveException* e)
{
TCHAR szCause[255];
e->GetErrorMessage(szCause, 255);
CString szErrText;
szErrText.LoadString(IDS_SERIALIZEERR);
TRACE1(szErrText, szCause);
archiveStore.Close();
file.Close();
e->Delete();
// return error code
return -1;
}
archiveStore.Close();
file.Close();
// Return the count we received
return nVal;
}