CGIWRAP.C
/*++ 
 
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; 
}