A working Windows CGI Program

/*---------------------------------------------
	WINCGI.CPP
	(C) 1994 PHD Computer Consultants Ltd
	27 October 1994: Chris Cant
  ---------------------------------------------*/

#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <ctype.h>

/////////////////////////////////////////////////////////////////////////////

typedef char __huge *HPSTR;

/////////////////////////////////////////////////////////////////////////////

void LogError( char*, LPSTR);
void HandleComments( int);
void HandleOrder( int);
void HandleUnrecognised( int);
void EnumerateKeys( char* INIfile, int hOF);

/////////////////////////////////////////////////////////////////////////////

int DoTinyMessageLoop( HANDLE, HANDLE, int);

/////////////////////////////////////////////////////////////////////////////

const short MAXARG = 10;
char* argv[MAXARG];
int argc = 0;
char* pszCmdLine = NULL;

int GetArgs( LPSTR lpszCmdLine)
{
	int len = _fstrlen( lpszCmdLine);
	char* pszCmdLine = new char[len+1];
	_fstrcpy( pszCmdLine, lpszCmdLine);
    
	argc = 1;
	argv[0] = NULL;

	char* pCmdLine = pszCmdLine;
	while( *pCmdLine!='\0')
	{
		char ch;
		if( argc==MAXARG) break;
		argv[argc++] = pCmdLine;
		do ch=*++pCmdLine;
		while( ch!=' ' && ch!='\0');
		if( ch=='\0') break;
		*pCmdLine++ = '\0';
		do ch=*pCmdLine++;
		while( ch==' ' && ch!='\0');
		pCmdLine--;
	}
	return argc;
}

/////////////////////////////////////////////////////////////////////////////
//	WinMain called with parameters <cgi-data-file> <content-file> <output-file> 
<url-args>

static char* FormComments	= "/Comments";
static char* FormOrder		= "/Order";

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
	/////////////////////////////////////////////////////////////////////////
	// Get arguments

	GetArgs( lpszCmdLine);
	if( argc<4 || argc>5)
	{
		LogError( "Server calling with wrong number of parameters", lpszCmdLine);
		return DoTinyMessageLoop( hInstance, hPrevInstance, nCmdShow);
	}
	
	/////////////////////////////////////////////////////////////////////////
	// Open output file

	char OutputFile[80];
	strncpy( OutputFile, argv[3], 79);
	OutputFile[79] = '\0';
	int hOutputFile = _open( OutputFile, _O_TEXT|_O_WRONLY|_O_CREAT|_O_TRUNC, _S_IREAD|_S_IWRITE);
	if( hOutputFile==-1)
	{
		LogError( "Could create output file", lpszCmdLine);
		return DoTinyMessageLoop( hInstance, hPrevInstance, nCmdShow);
	}

	/////////////////////////////////////////////////////////////////////////
	// Get logical path: see what to do

	char Buf[80];
	GetPrivateProfileString( "CGI", "Logical path", "", Buf, sizeof( Buf), argv[1]);
		
	if( strnicmp( Buf, FormComments, strlen( FormComments))==0)
		HandleComments( hOutputFile);
	else if( strnicmp( Buf, FormOrder, strlen( FormOrder))==0)
		HandleOrder( hOutputFile);
	else
		HandleUnrecognised( hOutputFile);

	/////////////////////////////////////////////////////////////////////////
	
	return DoTinyMessageLoop( hInstance, hPrevInstance, nCmdShow);
}

/////////////////////////////////////////////////////////////////////////////
// DoTinyMessageLoop: Necessary to allow httpd to sync with us

HANDLE hInst;
long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG);

int DoTinyMessageLoop( HANDLE hInstance, HANDLE hPrevInstance, int nCmdShow)
{
	hInst = hInstance;

	static char szAppName[] = "wincgi";
	
	if (!hPrevInstance) 
	{
		WNDCLASS wndclass;
		wndclass.style         = CS_HREDRAW | CS_VREDRAW;
		wndclass.lpfnWndProc   = WndProc;
		wndclass.cbClsExtra    = 0;
		wndclass.cbWndExtra    = 0;
		wndclass.hInstance     = hInstance;
		wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION);
		wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
		wndclass.hbrBackground = GetStockObject (WHITE_BRUSH);
		wndclass.lpszMenuName  = NULL;
		wndclass.lpszClassName = szAppName;
			
		RegisterClass (&wndclass);
	}
	
	HWND hwnd = CreateWindow (szAppName, szAppName,
			WS_OVERLAPPEDWINDOW,
			CW_USEDEFAULT, CW_USEDEFAULT,
			CW_USEDEFAULT, CW_USEDEFAULT,
			NULL, NULL, hInstance, NULL);
	
//	ShowWindow (hwnd, nCmdShow);
//	UpdateWindow (hwnd);
	
	MSG msg;
	while (GetMessage (&msg, NULL, 0, 0))
	{
		TranslateMessage (&msg);
		DispatchMessage (&msg);
	}
	return msg.wParam;
}

/////////////////////////////////////////////////////////////////////////////

#define ID_TIMER 1

long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
	static UINT timer = 0;

	switch (message)
	{
	case WM_CREATE:
		// Set timer for 50ms
		SetTimer( hwnd, ID_TIMER, 50, (TIMERPROC)NULL);
		return 0;
		
	case WM_TIMER:
		// Timer gone off: die
		KillTimer( hwnd, ID_TIMER);
		PostMessage( hwnd, WM_CLOSE, NULL, NULL);
		return 0;

	case WM_DESTROY:
		PostQuitMessage (0);
		return 0;
	}
	
	return DefWindowProc (hwnd, message, wParam, lParam);
}

/////////////////////////////////////////////////////////////////////////////
// LogError:  Log an error message, together with given command line

void LogError( char* ErrorMsg, LPSTR lpszCmdLine)
{
//	PlaySound( "C:\\WINDOWS\\DING.WAV");
	int hLogFile = _open( "FAXBACK.LOG", _O_WRONLY|_O_TEXT|_O_APPEND|_O_CREAT, _S_IREAD|_S_IWRITE);
	if( hLogFile==-1) return;

	char HeaderBuf[50];
	time_t now;
	time( &now);
	wsprintf( HeaderBuf, "--- WINCGI error on %s", (LPSTR)ctime( &now));
	_write( hLogFile, HeaderBuf, strlen( HeaderBuf));

	int len = strlen( ErrorMsg) + _fstrlen( lpszCmdLine);
	char* MsgBuf = new char[strlen( ErrorMsg)+_fstrlen( lpszCmdLine)+50];
	wsprintf( MsgBuf, "Error:        %s\nCommand line: %s\n\n", (LPSTR)ErrorMsg, lpszCmdLine);
	_write( hLogFile, MsgBuf, strlen( MsgBuf));
	_close( hLogFile);
	delete MsgBuf;
}

/////////////////////////////////////////////////////////////////////////////
// DecodeValue:  Decode a huge value string:  + to space, %XX to char code

void DecodeValue( HPSTR hpValue, long& length)
{
	HPSTR hpDest = hpValue;
	HPSTR hpSrc = hpValue;
		
	for (long srcCountUp=0; srcCountUp<length; srcCountUp++)
	{
		char ch = *hpSrc++;
		if( ch=='+')
		{
			*hpDest++ = ' ';
			continue;
		}
		if( ch=='%')
		{
			ch = *hpSrc++;
			char ch1 = toupper(ch);
			ch = *hpSrc++;
			char ch2 = toupper(ch);
			if( isxdigit( ch1) && isxdigit( ch2))
			{
				int dig1 = (ch1<='9') ? ch1-'0' : ch1-'A'+10;
				int dig2 = (ch2<='9') ? ch2-'0' : ch2-'A'+10;
				*hpDest++ = (dig1<<4)+dig2;
				length -= 2;
				continue;
			}
			hpSrc -= 3;
			ch = *hpSrc++;
		}
		*hpDest++ = ch;
	}
}

/////////////////////////////////////////////////////////////////////////////
// ReadBuf:  Read a file into a huge buffer, 256 bytes at a time

const short ReadBlockSize = 256;

void ReadBuf( int hF, HPSTR hpValue, long len)
{
	long txd = 0L;
	char block[ReadBlockSize];

	while( txd<len)
	{
		int totx = ReadBlockSize;
		if( txd+(long)totx > len)
			totx = (int)(len-txd);

		_read( hF, block, totx);
		_fstrncpy( hpValue+txd, block, totx);
		txd += totx;
	}
}

/////////////////////////////////////////////////////////////////////////////
// GetFormValue:  Get a value for a key, from one of the CGI data file sections.
//		   Return a huge pointer.

HPSTR GetFormValue( char* INIfile, char* ContFile, char* KeyName, long& count)
{
	char INIline[300];

	// Is key in LITERAL section?
	count = GetPrivateProfileString( "Form Literal", KeyName, "\x1A", INIline, 300, INIfile);
	if( *INIline!='\x1A')
	{
		// Copy INI string into value string
		short len = strlen( INIline);
		HPSTR hpValue = new __huge char[len+1];
		_fstrncpy( hpValue, INIline, len+1);
		return hpValue;
	}
	
	// Is key in EXTERNAL section?
	GetPrivateProfileString( "Form External", KeyName, "\x1A", INIline, 300, INIfile);
	if( *INIline!='\x1A')
	{
		char* pLen = strchr( INIline, ' ');
		if( pLen)
		{
			*pLen++ = '\0';
			int hExt = _open( INIline, _O_RDONLY|_O_BINARY);
			if( hExt!=-1)
			{
				// Read external file into value string
				unsigned int len = atoi( pLen);
				HPSTR hpValue = new __huge char[len+1];
				ReadBuf( hExt, hpValue, len);
				hpValue[len] = '\0';
				_close( hExt);
				count = len;
				return hpValue;
			}
		}
	}

	// Is key in HUGE section?
	GetPrivateProfileString( "Form Huge", KeyName, "\x1A", INIline, 300, INIfile);
	if( *INIline!='\x1A')
	{
		char *pLen = strchr( INIline, ' ');
		if( pLen)
		{
			// Open raw content file
			*pLen++ = '\0';
			long offset = atol( INIline);
			long len = atol( pLen);
			int hExt = _open( ContFile, _O_RDONLY|_O_BINARY);
			if( hExt!=-1)
			{
				// Read content file part into value string
				_lseek( hExt, offset, SEEK_SET);
				HPSTR hpValue = new __huge char[len+1];
				ReadBuf( hExt, hpValue, len);
				hpValue[len] = '\0';
				_close( hExt);
				// Decode from raw form
				DecodeValue( hpValue, len);
				count = len;
				return hpValue;
			}
		}
	}
	
	count = 0;
	return NULL;
}

/////////////////////////////////////////////////////////////////////////////
// TidyMultiLine:  Tidy a TEXTAREA multi-line string so that \r chars removed.

void TidyMultiLine( HPSTR hpValue, long& length)
{
	char __huge *hpDest = hpValue;
	char __huge *hpSrc = hpValue;
	long skipped = 0L;
			
	for(long srcIndex=0; srcIndex<length; srcIndex++)
	{
		char ch = *hpSrc++;
		if( ch=='\r')
			skipped++;
		else
			*hpDest++ = ch;
	}
	length -= skipped;
}

/////////////////////////////////////////////////////////////////////////////
// HandleComments:  Just act as if ignored for just now

void HandleComments( int hOF)
{
	HandleUnrecognised( hOF);
}

/////////////////////////////////////////////////////////////////////////////
// HandleOrder:  Check order details, build reply output and store order

void StoreOrder( HPSTR, HPSTR, HPSTR, HPSTR, HPSTR, HPSTR);

void HandleOrder( int hOF)
{
	char* pCF = "Content-type: text/html\n\n<HTML><HEAD>";
	int CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);

	pCF = "<TITLE>WINCGI Order</TITLE></HEAD>\n<BODY><H1>WINCGI Order</H1><P>\n";
	CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);
		
	long ProdLen, NameLen, FaxLen, CardLen, ContactLen, AddrLen;
	HPSTR ProductCode = GetFormValue( argv[1], argv[2], "ProductCode", ProdLen);
	HPSTR Name = GetFormValue( argv[1], argv[2], "Name", NameLen);
	HPSTR Fax = GetFormValue( argv[1], argv[2], "Fax", FaxLen);
	HPSTR Card = GetFormValue( argv[1], argv[2], "Card", CardLen);
	HPSTR Contact = GetFormValue( argv[1], argv[2], "Contact", ContactLen);
	HPSTR Address = GetFormValue( argv[1], argv[2], "Address", AddrLen);

	if( NameLen==0L || CardLen==0L || AddrLen==0L)
	{
		pCF = "<H2>Sorry, you must enter a name, a card number and an address</H2>\n";
		CFlen = strlen( pCF);
		_write( hOF, pCF, CFlen);
	}
	else
	{
		TidyMultiLine( Address, AddrLen);
		Address[AddrLen] = '\0';

		StoreOrder( ProductCode, Name, Fax, Card, Contact, Address);

		char* Msg = new char[ProdLen+NameLen+CardLen+ContactLen+AddrLen+50];
		wsprintf( Msg, "<H2>Thanks for your order <B>%s</B></H2>\n", Name);
		CFlen = strlen( Msg);
		_write( hOF, Msg, CFlen);
		
		wsprintf( Msg, "You have ordered %s<P>\n", ProductCode);
		CFlen = strlen( Msg);
		_write( hOF, Msg, CFlen);

		if( ContactLen>0L)
		{
			wsprintf( Msg, "Your contact phone/email is <I>%s</I><P>\n", Contact);
			CFlen = strlen( Msg);
			_write( hOF, Msg, CFlen);
		}

		wsprintf( Msg, "<HR>\n<H2>Billing Details</H2>\nName: \
<B>%s</B><P>\nCard: <B>%s</B><P>\nAddress: <B>%s</B><P>\n", Name, Card, Address);
		CFlen = strlen( Msg);
		_write( hOF, Msg, CFlen);
		
		if( FaxLen>0)
		{
			wsprintf( Msg, "<HR><H2>Sending confirmation fax to <I>%s</I>.</H2>\n", Fax);
			CFlen = strlen( Msg);
			_write( hOF, Msg, CFlen);
		}
		delete Msg;
	}

	pCF = "<HR><A HREF=/phd//>Return to order menu</A>\n</BODY></HTML>\n";
	CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);
		
	_close( hOF);

	if( ProductCode) delete ProductCode;
	if( Name) delete Name;
	if( Fax) delete Fax;
	if( Card) delete Card;
	if( Contact) delete Contact;
	if( Address) delete Address;
}

/////////////////////////////////////////////////////////////////////////////
// StoreOrder:  Store away the received order

void StoreOrder( HPSTR ProductCode, HPSTR Name, HPSTR Fax, HPSTR Card, HPSTR Contact, HPSTR Address)
{
	// Store in a file somewhere
}

/////////////////////////////////////////////////////////////////////////////
// HandleUnrecognised: Handle an unrecognised form request:
//			 Echo the keys as HTML to user

void HandleUnrecognised( int hOF)
{
	char* pCF = "Content-type: text/html\n\n<HTML><HEAD><TITLE>Unrecognised \
request</TITLE></HEAD>\n<BODY><H1>Unrecognised request</H1><P>\n";
	int CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);
		
	EnumerateKeys( argv[1], hOF);

	pCF = "<HR><A HREF=/phd/>Return to main menu</A>\n</BODY></HTML>\n";
	CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);
		
	_close( hOF);
}

/////////////////////////////////////////////////////////////////////////////
// EnumerateKeys:  List the Query String and Logical Path , together
//			all the keys from each Form section as HTML

enum LOCATION { LITERAL, EXTERNAL, HUGE, };

void EnumerateFormKeys( char* INIfile, int hOF, LOCATION);

void EnumerateKeys( char* INIfile, int hOF)
{	
	/////////////////////////////////////////////////////////////////////////

	char* pCF = "Query String=";
	int CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);
	
	char Query[80];
	GetPrivateProfileString( "CGI", "Query String", "", Query, sizeof( Query), INIfile);
	CFlen = strlen( Query);
	_write( hOF, Query, CFlen);
		
	pCF = "<P>\nLogical path=";
	CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);
	
	GetPrivateProfileString( "CGI", "Logical path", "", Query, sizeof( Query), INIfile);
	CFlen = strlen( Query);
	_write( hOF, Query, CFlen);

	pCF = "<P><P>\n";
	CFlen = strlen( pCF);
	_write( hOF, pCF, CFlen);
	
	/////////////////////////////////////////////////////////////////////////

	EnumerateFormKeys( INIfile, hOF, LITERAL);
	EnumerateFormKeys( INIfile, hOF, EXTERNAL);
	EnumerateFormKeys( INIfile, hOF, HUGE);
}	

/////////////////////////////////////////////////////////////////////////////
// EnumerateKeys:  Enumerate the keys in a form section as HTML

void EnumerateFormKeys( char* INIfile, int hOF, LOCATION location)
{
	// Allocate a buffer for the entries
	HLOCAL hBuf = LocalAlloc(LMEM_MOVEABLE, 1024);
	char* pszBuf = (char*)LocalLock( hBuf);
	
	// Retrieve all the entries in the one of the three sections
	char* SectionName;
	char* pHeader;
	switch( location)
	{
	case LITERAL:
		SectionName = "Form Literal";
		pHeader = "<H2>LITERAL</H2>\n";
		GetPrivateProfileString( SectionName, NULL, "", pszBuf, 1024, INIfile);
		break;
	case EXTERNAL:
		SectionName = "Form External";
		pHeader = "<H2>EXTERNAL</H2>\n";
		GetPrivateProfileString( SectionName, NULL, "", pszBuf, 1024, INIfile);
		break;
	case HUGE:
		SectionName = "Form Huge";
		pHeader = "<H2>HUGE</H2>\n";
		GetPrivateProfileString( SectionName, NULL, "", pszBuf, 1024, INIfile);
	}
	_write( hOF, pHeader, strlen( pHeader));
	
	// Retrieve the string for each entry, until
	// reaching the double null character.
	for (char* pszKey = pszBuf; *pszKey != '\0'; pszKey += strlen(pszKey) + 1)
	{
		// Retrieve the value for each entry in the buffer
		char szVal[80];
		GetPrivateProfileString( SectionName, pszKey, "not found", szVal, sizeof(szVal), INIfile);

		// Write each key=value string as HTML
		char szMsg[80];
		if( strlen( pszKey)+strlen(szVal) < 70)
		{
			wsprintf( szMsg, "%s = %s<P>\n", (LPSTR)pszKey, (LPSTR)szVal);
			_write( hOF, szMsg, strlen( szMsg));
		}
		else
		{
			_write( hOF, pszKey, strlen( pszKey));
			_write( hOF, "<P>\n", 4);
		}

		// For external keys, write the external value string as HTML
		if( location==EXTERNAL)
		{
			char *pLen = strchr( szVal, ' ');
			if( pLen)
			{
				*pLen++ = '\0';
				int hExt = _open( szVal, _O_RDONLY|_O_BINARY);
				if( hExt!=-1)
				{
					unsigned int len = atoi( pLen);
					char* pBuf = new char[len+1];
					_read( hExt, pBuf, len);
					_close( hExt);
					_write( hOF, pBuf, len);
					_write( hOF, "<P>\n", 4);
					delete pBuf;
				}
			}
		}
	}

	LocalUnlock( hBuf);
	LocalFree( hBuf);
}

/////////////////////////////////////////////////////////////////////////////

Return to Web Forms