/*++
File: cgiwrap.c
Demonstrates an executable which can be used to load an ISAPI DLL like
a CGI script.
--*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <httpext.h>
#include <stdio.h>
#include <stdlib.h>
//
// These are things that go out in the Response Header
//
#define HTTP_VER "HTTP/1.0"
#define SERVER_VERSION "Http-Srv-Beta2/1.0"
//
// Simple wrappers for the heap APIS
//
#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
#define xfree(s) HeapFree(GetProcessHeap(), 0, (s))
//
// The mandatory exports from the ISAPI DLL
//
typedef BOOL(WINAPI * VersionProc) (HSE_VERSION_INFO *);
typedef DWORD(*HttpExtProc) (EXTENSION_CONTROL_BLOCK *);
//
// Prototypes of the functions this sample implements
//
BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *);
BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD);
BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD);
BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD);
BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD);
char *MakeDateStr(VOID);
char *GetEnv(char *);
//
// In the startup of this program, we look at our executable name and
// replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load.
// This means that the executable need only be given the same "name" as
// the DLL to load. There is no recompilation required.
//
int __cdecl
main(int argc, char **argv)
{
HINSTANCE hDll;
VersionProc GetExtensionVersion;
HttpExtProc HttpExtensionProc;
HSE_VERSION_INFO version_info;
EXTENSION_CONTROL_BLOCK ECB;
DWORD rc;
char szModuleFileName[256], *c;
if (!GetModuleFileName(NULL, szModuleFileName, 256)) {
fprintf(stderr, "cannot get ModuleFileName %d\n", GetLastError());
return -1;
}
rc = strlen(szModuleFileName);
c = szModuleFileName + rc - 4; // Go back to the last "."
c[1] = 'D';
c[2] = 'L';
c[3] = 'L';
hDll = LoadLibrary(szModuleFileName); // Load our DLL
if (!hDll) {
fprintf(stderr, "Error: Failure to load %s.dll (%d)\n",
argv[0], GetLastError());
return -1;
}
//
// Find the exported functions
//
GetExtensionVersion = (VersionProc) GetProcAddress(hDll,
"GetExtensionVersion");
if (!GetExtensionVersion) {
fprintf(stderr, "Can't Get Extension Version %d\n", GetLastError());
return -1;
}
HttpExtensionProc = (HttpExtProc) GetProcAddress(hDll,
"HttpExtensionProc");
if (!HttpExtensionProc) {
fprintf(stderr, "Can't Get Extension proc %d\n", GetLastError());
return -1;
}
//
// This should really check if the version information matches what
// we expect.
//
__try {
if (!GetExtensionVersion(&version_info)) {
fprintf(stderr, "Fatal: GetExtensionVersion failed\n");
return -1;
}
}
__except(1) {
return -1;
}
//
// Fill the ECB with the necessary information
//
if (!FillExtensionControlBlock(&ECB)) {
fprintf(stderr, "Fill Ext Block Failed\n");
return -1;
}
//
// Call the DLL
//
__try {
rc = HttpExtensionProc(&ECB);
}
__except(1) {
return -1;
}
//
// We should really free memory (e.g., from GetEnv), but we'll be dead
// soon enough
//
if (rc == HSE_STATUS_PENDING)
//
// We will exit in ServerSupportFunction
//
Sleep(INFINITE);
return 0;
}
//
// GetServerVariable() is how the DLL calls the main program to figure out
// the environment variables it needs. This is a required function.
//
BOOL WINAPI
GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
LPVOID lpBuffer, LPDWORD lpdwSize)
{
DWORD rc;
//
// We don't really have an HCONN, so we assume a value of 0 (which is
// passed
// to the DLL in the ECB by HttpExtensionProc().
// Hence the check for a non-zero HCONN
if (hConn) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
rc = GetEnvironmentVariable(lpszVariableName, lpBuffer, *lpdwSize);
if (!rc) {
//
// return of 0 indicates the variable was not found
//
SetLastError(ERROR_NO_DATA);
return FALSE;
}
if (rc > *lpdwSize) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
//
// GetEnvironmentVariable does not count the NUL character
//
*lpdwSize = rc + 1;
return TRUE;
}
//
// Again, we don't have an HCONN, so we simply wrap ReadClient() to
// ReadFile on stdin. The semantics of the two functions are the same
//
BOOL WINAPI
ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize)
{
return ReadFile(GetStdHandle(STD_INPUT_HANDLE), lpBuffer, (*lpdwSize),
lpdwSize, NULL);
}
//
// ditto for WriteClient()
//
BOOL WINAPI
WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
DWORD dwReserved)
{
return WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), lpBuffer, *lpdwSize,
lpdwSize, NULL);
}
//
// This is a special callback function used by the DLL for certain extra
// functionality. Look at the API help for details.
//
BOOL WINAPI
ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType)
{
char *lpszRespBuf;
char *temp;
DWORD dwBytes;
BOOL bRet;
switch (dwHSERequest) {
case (HSE_REQ_SEND_RESPONSE_HEADER):
lpszRespBuf = xmalloc(*lpdwSize + 80); // accomodate our header
if (!lpszRespBuf)
return FALSE;
wsprintf(lpszRespBuf, "%s %s %s %s\r\n%s",
HTTP_VER,
lpvBuffer ? lpvBuffer : "200 Ok", // Default response is 200 Ok
temp = MakeDateStr(), // Create a time string
SERVER_VERSION,
//
// If this exists, it is a pointer to a data buffer to be sent.
//
lpdwDataType ? (char *) lpdwDataType : NULL);
xfree(temp);
dwBytes = strlen(lpszRespBuf);
bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
xfree(lpszRespBuf);
break;
case (HSE_REQ_DONE_WITH_SESSION):
//
// A real server would do cleanup here
//
ExitProcess(0);
break;
case (HSE_REQ_SEND_URL_REDIRECT_RESP):
//
// This sends a redirect (temporary) to the client.
// The header construction is similar to RESPONSE_HEADER above.
//
lpszRespBuf = xmalloc(*lpdwSize + 80);
if (!lpszRespBuf)
return FALSE;
wsprintf(lpszRespBuf, "%s %s %s\r\n",
HTTP_VER,
"302 Moved Temporarily",
(lpdwSize > 0) ? lpvBuffer : 0);
dwBytes = strlen(lpszRespBuf);
bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
xfree(lpszRespBuf);
break;
default:
return FALSE;
break;
}
return bRet;
}
//
// Makes a string of the date and time from GetSystemTime().
// This is in UTC, as required by the HTTP spec.`
//
char *
MakeDateStr(void)
{
SYSTEMTIME systime;
char *szDate = xmalloc(64);
char *DaysofWeek[] =
{"Sun", "Mon", "Tue", "Wed", "Thurs", "Fri", "Sat"};
char *Months[] =
{"NULL", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
GetSystemTime(&systime);
wsprintf(szDate, "%s, %d %s %d %d:%d.%d",
DaysofWeek[systime.wDayOfWeek],
systime.wDay,
Months[systime.wMonth],
systime.wYear,
systime.wHour, systime.wMinute,
systime.wSecond);
return szDate;
}
//
// Fill the ECB up
//
BOOL WINAPI
FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK * ECB)
{
char *temp;
ECB->cbSize = sizeof (EXTENSION_CONTROL_BLOCK);
ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
ECB->ConnID = 0;
//
// Pointers to the functions the DLL will call.
//
ECB->GetServerVariable = GetServerVariable;
ECB->ReadClient = ReadClient;
ECB->WriteClient = WriteClient;
ECB->ServerSupportFunction = ServerSupportFunction;
//
// Fill in the standard CGI environment variables
//
ECB->lpszMethod = GetEnv("REQUEST_METHOD");
ECB->lpszQueryString = GetEnv("QUERY_STRING");
ECB->lpszPathInfo = GetEnv("PATH_INFO");
ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
ECB->cbTotalBytes = ((temp = GetEnv("CONTENT_LENGTH")) ?
(atoi(temp)) : 0);
ECB->cbAvailable = 0;
ECB->lpbData = "";
ECB->lpszContentType = GetEnv("CONTENT_TYPE");
return TRUE;
}
//
// Works like _getenv(), but uses win32 functions instead.
//
char *
GetEnv(LPSTR lpszEnvVar)
{
char *var, dummy;
DWORD dwLen;
if (!lpszEnvVar)
return "";
dwLen = GetEnvironmentVariable(lpszEnvVar, &dummy, 1);
if (dwLen == 0)
return "";
var = xmalloc(dwLen);
if (!var)
return "";
(void) GetEnvironmentVariable(lpszEnvVar, var, dwLen);
return var;
}