Contributor: ANN LYNNWORTH
unit Cgi;
{ cgi.pas
Author: Ann Lynnworth
Copyright (c) 1995-1996, Ann Lynnworth. All Rights Reserved.
Thanks to Fred Thompson for adding getSmallMultiField().
Thanks to Dagur Georgsson for testing and debugging the
internationalization of getSmallField.
This program may be freely used and modified by anyone. It would be
considerate to keep at least my name and copyright notice intact.
It is distributed with a "don't laugh at my code" disclaimer.
This was my first attempt at writing a Delphi component back
in June '95. If I change it now, hundreds of web-applications
will break. So I'm leaving the data structures alone.
What would I do different? For starters, I wouldn't use pstrings
on the published properties!
URLs of note:
http://super.sonic.net/ann/delphi/cgicomp/ -- home of this component
http://www.href.com/ -- home of my company, HREF Tools Corp., with newer cgi tools
http://website.ora.com/ -- home of WebSite 32-bit server
http://www.borland.com/ -- you remember Borland; they made Delphi for us
}
{ Technical support -- sorry, there isn't any. This is a FREE component.
Here are the 3 things I usually tell people to get them started:
1. download the free demo project from http://super.sonic.net/ann/delphi/cgicomp/code.html
2. If you're following the directions in cgihelp.hlp, make sure you also
connect the form create method to the form's onCreate event handler.
That's easy to overlook and of course your app won't work.
3. To test, you need to run the .exe from a browser using an http command
in the form: http://127.0.0.1/cgi-win/demo1.exe
That IP references your local drive. You can use any other IP
or domain name.
You will not be able to test or debug your web-application within Delphi.
These components do not work as-is with Netscape server, at least
not with Netscape's implementation of win-cgi as of 2/23/96.
They only work with WebSite server from O'Reilly & Associates.
}
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes,
forms, iniFiles, Dialogs;
type
NTWebServerType = (WebSite); {only one choice; I meant to have more}
type
TWebServer = class(TComponent);
type
TCGIEnvData = class(TComponent)
private
{ Private declarations -- custom for this component }
fServerType : NTWebServerType;
fServerComponent : TWebServer;
fStdOut : integer;
fAddress : string;
{ for use with WebSite only }
finiFilename : string;
{CGI section of web site INI file}
fCGICGIVersion : string;
fCGIRequestProtocol : string;
fCGIRequestMethod : string; { 'GET' or 'POST' -- should be POST }
fCGIExecutablePath : string;
fCGILogicalPath : string;
fCGIPhysicalPath : string;
fCGIQueryString : string;
fCGIContentType : string;
fCGIContentLength : longInt;
fCGIServerSoftware : string;
fCGIServerName : string;
fCGIServerPort : string;
fCGIServerAdmin : string;
fCGIReferer : string;
fCGIFrom : string;
fCGIRemoteHost : string;
fCGIRemoteAddress : string;
fCGIAuthenticatedUsername : string;
fCGIAuthenticatedPassword : string;
fCGIAuthenticationMethod : string;
fCGIAuthenticationRealm : string;
fSystemGMTOffset : double;
fSystemOutputFile : string;
fSystemContentFile : string;
fSystemDebugMode : string;
{Custom Private Procedures & Functions }
procedure getCGIItem( p : pString; key : string; okEmpty : boolean );
{ CGI }
function getCGICGIVersion : pstring;
function getCGIRequestProtocol : pstring;
function getCGIRequestMethod : pstring;
function getCGIExecutablePath : pstring;
function getCGILogicalPath : pstring;
function getCGIPhysicalPath : pString;
function getCGIQueryString : pString;
function getCGIContentType : pString;
function getCGIContentLength : longInt;
function getCGIServerSoftware : pstring;
function getCGIServerName : pstring;
function getCGIServerPort : pString;
function getCGIServerAdmin : pString;
function getCGIReferer : pString;
function getCGIFrom : pString;
function getCGIRemoteHost : pString;
function getCGIRemoteAddress : pString;
function getCGIAuthenticatedUsername : pString;
function getCGIAuthenticatedPassword : pString;
function getCGIAuthenticationMethod : pString;
function getCGIAuthenticationRealm : pString;
{ system }
{function getSystemGMTOffset: pstring;}
function getSystemOutputFile : pstring;
function getSystemDebugMode : pstring;
function getSystemContentFile : pstring;
protected
{ Protected declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
public
{ Public declarations }
{ misc }
{ WebSite only }
procedure setIniFilename( value : string );
{CGI}
{ Regarding all these pstrings. I didn't know better. I was trying to save
255 bytes x this many properties. "Don't laugh at my code." Or laugh away,
just do it in private. }
property CGICGIVersion : pstring read getCGICGIVersion stored false;
property CGIRequestProtocol : pstring read getCGIRequestProtocol stored false;
property CGIRequestMethod : pstring read getCGIRequestMethod stored false;
property CGIExecutablePath : pstring read getCGIExecutablePath stored false;
property CGILogicalPath : pstring read getCGILogicalPath stored false;
property CGIPhysicalPath : pstring read getCGIPhysicalPath stored false;
property CGIQueryString : pString read getCGIQueryString stored false;
property CGIContentType : pString read getCGIContentType stored false;
property CGIContentLength : longInt read getCGIContentLength stored false;
property CGIServerSoftware : pString read getCGIServerSoftware stored false;
property CGIServerPort : pString read getCGIServerPort stored false;
property CGIServerName : pString read getCGIServerName stored false;
property CGIServerAdmin : pstring read getCGIServerAdmin stored false;
property CGIReferer : pString read getCGIReferer stored false;
property CGIFrom : pString read getCGIFrom stored false;
property CGIRemoteHost : pString read getCGIRemoteHost stored false;
property CGIRemoteAddress : pString read getCGIRemoteAddress stored false;
property CGIAuthenticatedUsername : pString read getCGIAuthenticatedUsername stored false;
property CGIAuthenticatedPassword : pString read getCGIAuthenticatedPassword stored false;
property CGIAuthenticationMethod : pString read getCGIAuthenticationMethod stored false;
property CGIAuthenticationRealm : pString read getCGIAuthenticationRealm stored false;
{System}
property SystemGMToffset : double read fSystemGMToffset stored false;
property SystemOutputFile : pstring read getSystemOutputFile stored false;
property SystemContentFile : pstring read getSystemContentFile stored false;
property SystemDebugMode : pstring read getSystemDebugMode stored false;
published
{ Published declarations }
{ set this to your address, e.g. ann@href.com }
property address : string read fAddress write fAddress;
{ ServerTypes WebSite and httpd16 are functionally equivalent. }
{ The whole issue of ServerType is silly. }
{ Property is left in for compatibility only. 1-Jan-96 }
property ServerType : NTWebServerType read fServerType write fServerType;
function swapChar( s : string; fromChar : char; toChar : char ) : string;
{ set this to paramstr(1) at the beginning of your program }
property webSiteIniFilename : string read finiFilename write setIniFilename;
{ ***************************** }
{ Use the LOCATION: URL feature to "bounce" a user to a URL }
procedure bounceToLocation( goHere : string );
{ set application.onException to TCGIEnvData1.cgiErrorHandler as soon as you can in your app! }
procedure cgiErrorHandler( sender : TObject; e : Exception ) ;
{ call this at the end of your program }
procedure closeStdout;
{ This opens the stdout file based on filename created by WebSite;
if you forget this line, the first send command will take care of it
for you automatically. }
function createStdout : boolean;
{ get data from a named External field {size between 255 and 64K chars
and put it into a PChar. If you're basically working with input from
a TextArea on a form, see getTextArea below. It will be much more
convenient. }
function getExternalField( key : string; var externFilename : string; dest : PChar ) : boolean;
{ get everything in a section ('Form Literal' or 'Form External'). Refer to
readSectionValues in Delphi Help. This is the same thing -- it just automatically
goes to the correct INI file for you. }
function getSectionValues( sectionName : string; strings : TStringList ) : boolean;
{ get data from an HTML form based on field name ("key") }
function getSmallField( key : string ) : string;
{***********************************************************************}
{*** getSmallMultiField - added Dec. 17, 1995 - Fred Thompson **********}
{***********************************************************************}
{ get Multiple data from an HTML form based on field name ("key") }
{ Return value contains all the values selected. }
function getSmallMultiField( key : string ) : Tstringlist;
{***********************************************************************}
{ TextAreas are tricky. If the user only enters one line of text, that
text is stored as a "small field" in the [Form Literal] section. This
function hides that complexity and lets you simply work with a string
list (which might only have one string in it). }
function getTextArea( key : string; dest : TStringList ) : boolean;
{ send a line of code to stdout (including required CR/LF) }
function send( s : string ) : boolean ;
function sendString( s : string; appendNewline : boolean ) : boolean;
{ send contents of Address property }
function sendAddress : boolean;
function sendAuthRequest : boolean;
{ send wallpaper background command (HTML 3.0) -- no color control yet }
function sendBackground( imageFilename : string ) : boolean;
{ send a string to stdout, as a comment. This is used internally to
alert you to warnings/errors. }
procedure sendComment( s : string );
{ send header, e.g. H1, H2, etc. }
function sendHdr( hdrLevel : char; hdrText : string ) : boolean;
{ send horizonal ruler line command }
function sendHR : boolean;
{ send A HREF command including optional image and optional (netscape) attributes
such as align=left or hspace=5 }
function sendHREF( imageFilename : string; imageAttrib : string;
visiblePhrase : string; linkedURL : string ) : boolean;
{ send a simple IMG SRC phase. attrib can be hspace=5 or align=left }
function sendIMG( imageFilename : string; imageAttrib : string ) : boolean;
procedure sendMailto( emailAddress : string );
{ do nothing; copied from Bob Denny's cgi.bas. Bob Denny is the author of
Win-Httpd and WebSite server. He has my endless gratitude for answering
my endless questions in May '95. }
procedure sendNoOp;
{ send HTTP/1.0 200 OK etc. }
function sendPrologue : boolean;
{ send TITLE phrase }
function sendTitle( title : string ) : boolean;
{ convert Delphi date/time to GMT for use in HTML header }
function webDate (dt : TDateTime ) : string ;
end;
{***************************************************************}
{***************************************************************}
type
TWebsite = class(TWebServer)
private
fServerType : NTWebServerType;
fCGI : TCGIEnvData;
fIniFile : TIniFile;
{ custom }
procedure CGIData( p : pString; key : string; okEmpty : boolean );
procedure initData;
function readWebSiteCGIString( key : string; okEmpty : boolean ) : string;
function getExternalField( key : string; var externFilename : string; dest : PChar ) : boolean;
function getTextArea( key : string; dest : TStringList ) : boolean;
public
{ Public declarations }
property INIFile: TIniFile read fIniFile stored false;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
function getSmallField( key : string ) : string;
function getSmallMultiField( key: string) :Tstringlist; {*FWT*}
end;
const
MAXTABLEFIELDS = 255; { no more than 255 fields displayed in HTML Table }
CGINOTFOUND = 'AAAKEY NOT FOUND';
MAX_CMDARGS = 8; { Max # of command line args }
ENUM_BUF_SIZE = 4096; { Key enumeration buffer, see GetProfile() }
{ These are the limits in the server }
MAX_XHDR = 100; { Max # of "extra" request headers }
MAX_ACCTYPE = 100; { Max # of Accept: types in request }
MAX_FORM_TUPLES = 100; { Max # form key=value pairs }
MAX_HUGE_TUPLES = 16; { Max # "huge" form fields }
procedure closeApp( app : TApplication );
procedure Register;
implementation
constructor TCGIEnvData.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fStdOut := -99;
fAddress := '';
{ CGI section }
fCGICGIVersion := '';
fCGIRequestProtocol := '';
fCGIRequestMethod := '';
fCGIExecutablePath := '';
fCGILogicalPath := '';
fCGIPhysicalPath := '';
fCGIQueryString := '';
fCGIContentType := '';
fCGIContentLength := -1; { init to -1 }
fCGIServerSoftware := '';
fCGIServerName := '';
fCGIServerPort := '';
fCGIServerAdmin := '';
fCGIReferer := '';
fCGIFrom := '';
fCGIRemoteHost := '';
fCGIRemoteAddress := '';
fCGIAuthenticatedUsername := '';
fCGIAuthenticatedPassword := '';
fCGIAuthenticationMethod := '';
fCGIAuthenticationRealm := '';
{ System section }
fSystemGMTOffset := 0;
fSystemOutputFile := '';
fSystemContentFile := '';
fSystemDebugMode := '';
{ Please realize that I thought there might be different
components for different web servers, and the correct one
would be linked in. That whole strategy was abandoned. }
fServerComponent := nil;
if NOT (csDesigning in ComponentState) then
fServerComponent := TWebsite.create( self );
end;
destructor TCGIEnvData.Destroy;
begin
if fStdOut > 0 then
closeStdOut;
if NOT (csDesigning in ComponentState) then
fServerComponent.free;
inherited Destroy;
end;
{ **************************************************************
get CGI information from variables from INI file
************************************************************** }
function TCGIEnvData.getCGICGIVersion : pString;
begin
result := addr( fCGICGIVersion );
end;
procedure TCGIEnvData.getCGIItem( p : pString; key : string; okEmpty : boolean );
var
x : TWebsite;
begin
x := TWebsite( fServerComponent );
x.CGIData( p, key, okEmpty );
end;
function TCGIEnvData.getCGIRequestProtocol : pstring ;
begin
getCGIitem( addr( fCGIRequestProtocol ), 'Request Protocol', TRUE );
result := addr( fCGIRequestProtocol );
end;
function TCGIEnvData.getCGIRequestMethod : pString;
begin
result := addr( fCGIRequestMethod );
end;
function TCGIEnvData.getCGIExecutablePath : pString;
begin
result := addr( fCGIExecutablePath );
end;
function TCGIEnvData.getCGILogicalPath : pstring ;
begin
getCGIItem( addr( fCGILogicalPath ), 'Logical Path', FALSE );
result := addr( fCGILogicalPath );
end;
function TCGIEnvData.getCGIPhysicalPath : pString ;
begin
getCGIItem( addr( fCGIPhysicalPath ), 'Physical Path', FALSE );
result := addr( fCGIPhysicalPath );
end;
function TCGIEnvData.getCGIQueryString : pString;
begin
{ it's because of QueryString being blank sometimes that the
okEmpty parameter was added throughout. }
getCGIItem( addr( fCGIQueryString ), 'Query String', TRUE );
result := addr( fCGIQueryString );
end;
function TCGIEnvData.getCGIContentType : pString;
begin
getCGIItem( addr( fCGIContentType ), 'Content Type', FALSE );
result := addr( fCGIContentType );
end;
function TCGIEnvData.getCGIContentLength : longInt;
var
x : TWebSite;
begin
if fCGIContentLength <> -1 then begin
{ we've already loaded the information }
result := fCGIContentLength;
exit;
end;
x := TWebsite( fServerComponent );
fCGIContentLength := x.fIniFile.readInteger( 'CGI', 'Content Length', 0 );
result := fCGIContentLength;
end;
function TCGIEnvData.getCGIServerSoftware : pString;
begin
result := addr( fCGIServerSoftware );
end;
function TCGIEnvData.getCGIServerName : pstring ;
begin
getCGIItem( addr( fCGIServerName ), 'Server Name', FALSE );
result := addr( fCGIServerName );
end;
function TCGIEnvData.getCGIServerPort : pstring ;
begin
getCGIItem( addr( fCGIServerPort ), 'Server Name', FALSE );
result := addr( fCGIServerPort );
end;
function TCGIEnvData.getCGIServerAdmin : pString;
begin
result := addr( fCGIServerAdmin );
end;
function TCGIEnvData.getCGIReferer : pstring ;
var
x : TWebSite;
begin
getCGIItem( addr( fCGIReferer ), 'Referer', FALSE );
if fCGIReferer = cginotfound then begin
x := TWebsite( fServerComponent );
fCGIReferer := x.fIniFile.readString( 'Extra Headers', 'Referer', cginotfound );
end;
result := addr( fCGIReferer );
end;
function TCGIEnvData.getCGIFrom : pstring ;
begin
getCGIItem( addr( fCGIFrom ), 'From', FALSE );
result := addr( fCGIFrom );
end;
function TCGIEnvData.getCGIRemoteHost : pstring ;
begin
getCGIItem( addr( fCGIRemoteHost ), 'Remote Host', FALSE );
result := addr( fCGIRemoteHost );
end;
function TCGIEnvData.getCGIRemoteAddress : pstring ;
begin
getCGIItem( addr( fCGIRemoteAddress ), 'Remote Address', FALSE );
result := addr( fCGIRemoteAddress );
end;
function TCGIEnvData.getCGIAuthenticatedUsername : pstring ;
begin
getCGIItem( addr( fCGIAuthenticatedUsername ), 'Authenticated Username', TRUE );
result := addr( fCGIAuthenticatedUsername );
end;
function TCGIEnvData.getCGIAuthenticatedPassword : pstring ;
begin
getCGIItem( addr( fCGIAuthenticatedPassword ), 'Authenticated Password', TRUE );
result := addr( fCGIAuthenticatedPassword );
end;
function TCGIEnvData.getCGIAuthenticationMethod : pstring ;
begin
getCGIItem( addr( fCGIAuthenticationMethod ), 'Authentication Method', TRUE );
result := addr( fCGIAuthenticationMethod );
end;
function TCGIEnvData.getCGIAuthenticationRealm : pstring ;
begin
getCGIItem( addr( fCGIAuthenticationRealm ), 'Authentication Realm', TRUE );
result := addr( fCGIAuthenticationRealm );
end;
function TCGIEnvData.getSectionValues( sectionName : string; strings : TStringList ) : boolean;
var
x : TWebsite;
begin
strings.clear;
x := TWebsite( fServerComponent );
x.fIniFile.readSectionValues( sectionName, strings );
result := (strings.count > 0);
end;
{ **************************************************************
get SYSTEM information from variables from INI file
************************************************************** }
function TCGIEnvData.getSystemOutputFile : pString;
begin
result := addr( fSystemOutputFile );
end;
function TCGIEnvData.getSystemContentFile : pstring ;
var
x : TWebSite;
begin
if fSystemContentFile = '' then begin
x := TWebsite( fServerComponent );
fSystemContentFile := x.fIniFile.readString( 'System', 'Content File', cginotfound );
end;
result := addr( fSystemContentFile );
end;
function TCGIEnvData.getSystemDebugMode : pstring ;
var
x : TWebSite;
begin
if fSystemDebugMode = '' then
begin
case fServerType of
webSite :
begin
x := TWebsite( fServerComponent );
fSystemDebugMode := x.fIniFile.readString( 'System', 'Debug Mode', cginotfound );
end;
else
raise exception.create( 'Can not get Debug Mode; invalid web server type' );
end;
end;
result := addr( fSystemDebugMode );
end;
{ Get the value of a "small" form field given the key
Signals an error if field does not exist }
function TCGIEnvData.getSmallField( key : string ) : string;
var
x : TWebsite;
FileName: string;
i,FileHandle: integer;
read: byte;
buffer:array[0..255] of char;
r1 : string;
begin
x := TWebsite( fServerComponent );
result := x.getSmallField( key );
{************* code added to handle long or control chars **********FWT*}
if result = cginotfound then begin
result := x.fIniFile.readString( 'Form External', key, cginotfound );
if result = cginotfound then
exit;
i := pos( ' ', result );
FileName := copy( result, 0, i - 1 );
i := strToInt( copy( result, i, 10 ) ) ;
read := 255;
FileHandle := fileOpen( FileName, fmOpenRead );
if FileHandle > 0 then begin
fileRead( FileHandle, buffer[0], read );
fileClose( FileHandle );
buffer[read] := #0; {mark the ending}
if i > read then begin {indicate truncation}
buffer[254] := '*';
buffer[255] := '$';
end
else begin
result := copy(strpas(buffer), 1, i); {...'i' contains the correct string length...}
end
end
else
result := cginotfound;
end;
{******************** end of code added for long or control chars***FWT*}
end;
{************************** routine added - start of change **************FWT*}
{ Get the values of a "small" multiple selection form field given the key
Signals an error if field does not exist }
function TCGIEnvData.getSmallMultiField( key : string ) : Tstringlist;
var
x : TWebsite;
begin
x := TWebsite( fServerComponent );
result := x.getSmallMultiField( key );
end;
{************************** routine added - end of change ***************FWT*}
function TCGIEnvData.getExternalField( key : string; var externFilename : string; dest : PChar ) : boolean;
var
x : TWebsite;
begin
x := TWebsite( fServerComponent );
result := x.getExternalField( key, externFilename, dest );
end;
function TCGIEnvData.getTextArea( key : string; dest : TStringList ) : boolean;
var
x : TWebsite;
begin
x := TWebsite( fServerComponent );
result := x.getTextArea( key, dest );
end;
{ ************************************************************}
function TCGIEnvData.createStdout : boolean ;
begin
{ create output file and save pointer to it }
fStdout := fileCreate( fSystemOutputFile );
if fStdOut < 0 then begin
raise exception.create( 'Error code [' + intToStr( fStdOut ) +
'] when creating file (' + fSystemOutputFile + ')' );
end;
result := TRUE;
end;
function TCGIEnvData.send( s : string ) : boolean ;
begin
result := sendString( s, TRUE );
end;
function TCGIEnvData.sendAuthRequest : boolean;
begin
closeStdout;
createStdout;
result := send( 'HTTP/1.0 401 Unauthorized' );
closeStdout;
end;
function TCGIEnvData.sendString( s : string; appendNewline : boolean ) : boolean;
const
newLine : string[4] = #13#10; {what's the minimum size here? 2? 3? 4? }
var
s2 : string;
count : longInt;
begin
if fStdOut < 0 then
if NOT createStdout then
raise exception.create( 'Can not create stdout' );
if appendNewline then
s2 := s + newLine
else
s2 := s; { will performance suffer? should there be a separate routine here? }
count := length( s2 );
{ since the first byte of s2 contains the length, we shouldn't write
that out. Start instead with the next byte, which is s2[1]. }
result := ( fileWrite( fStdout, s2[1], count ) = count );
end;
procedure TCGIEnvData.closeStdout;
begin
fileClose( fStdout );
end;
{ SendNoOp() - Tell browser to do nothing.
Most browsers will do nothing. Netscape 1.0N leaves hourglass
cursor until the mouse is waved around. Enhanced Mosaic 2.0
oputs up an alert saying "URL leads nowhere". Your results may
vary...}
procedure TCGIEnvData.sendNoOp;
begin
Send ('HTTP/1.0 204 No Response');
Send ('Server: ' + fCGIServerSoftware );
Send ('');
end;
{ WebDate - Return an HTTP/1.0 compliant date/time string
Inputs: dt = Local time as TDateTime (e.g., returned by Now)
Returns: Properly formatted HTTP/1.0 date/time in GMT }
function TCGIEnvData.webDate (dt : TDateTime ) : String ;
begin
WebDate := FormatDateTime('ddd dd mmm yyyy hh:mm:ss "GMT"',
dt - fSystemGMTOffset );
end;
procedure TCGIEnvData.bounceToLocation( goHere : string );
begin
closeStdout;
createStdout;
Send ('LOCATION: ' + goHere );
Send ('');
closeStdout;
end;
function TCGIEnvData.sendAddress : boolean;
begin
if fAddress = '' then
result := FALSE
else
result := send( '' + fAddress + '' );
end;
function TCGIEnvData.sendHR : boolean;
begin
result := send( '
' );
end;
function TCGIEnvData.sendHdr( hdrLevel : char; hdrText : string ) : boolean;
begin
if ( hdrLevel < '1' ) OR ( hdrLevel > '6' ) then
begin
sendComment( 'hdrLevel should be between 1 and 6. Ref: ' + hdrText );
result := FALSE;
end
else
result := send( '' + hdrText + '' );
end;
function TCGIEnvData.sendHREF( imageFilename : string;
imageAttrib : string;
visiblePhrase : string;
linkedURL : string ) : boolean;
begin
if linkedURL = '' then begin
result := FALSE;
exit;
end;
{ Here is a sample of what this can result in:
InfoBahn Construction Workshop!
}
send( '' );
if imageFilename <> '' then
send( '' );
result := send( visiblePhrase + '' );
end;
function TCGIEnvData.sendIMG( imageFilename : string; imageAttrib : string ) : boolean;
begin
result := send( '' );
end;
function TCGIEnvData.sendPrologue : boolean;
begin
try
send( 'HTTP/1.0 200 OK' );
send( 'SERVER: ' + fCGIServerSoftware );
send( 'DATE: ' + webDate( now ) );
send( 'Content-type: text/html' );
send( '' ); { required blank line }
result := TRUE;
except
result := FALSE;
end;
end;
function TCGIEnvData.sendTitle( title : string ) : boolean;
begin
result := send( '' + title + '' );
end;
function TCGIEnvData.sendBackground( imageFilename : string ) : boolean;
begin
{}
result := send( '' );
end;
procedure TCGIEnvData.sendMailto( emailAddress : string );
begin
send( '' + emailAddress + '' );
end;
procedure TCGIEnvData.cgiErrorHandler( sender: TObject; e : Exception );
begin
if fStdout = -99 then
{ haven't even gotten as far as opening stdout at all yet! }
{ this would be a bad time to count on writing to that file !! }
closeApp( application );
try
createStdout;
Send ('HTTP/1.0 500 Internal Error');
Send ('SERVER: ' + fCGIServerSoftware);
Send ('DATE: ' + WebDate(Now) );
Send ('Content-type: text/html' );
Send ('');
Send ('');
Send ('Error in ' + fCGIExecutablePath + '' );
Send ('');
SendHdr( '2', 'Error in ' + fCGIExecutablePath );
Send ('An internal error has occurred in this program: ' + fCGIExecutablePath + '.');
Send ('' + e.message + '
');
Send ('Please note what you were doing when this problem occurred, ');
Send ('so we can identify and correct it. Write down the Web page you were using, ');
Send ('any data you may have entered into a form or search box, the' );
Send ('date and time listed below, and ');
Send ('anything else that may help us duplicate the problem. Then contact the ');
Send ('administrator of this service: ');
Send ('' + fCGIServerAdmin + ' ' );
SendHR;
send( 'Generated on: ' + webDate( now ) );
Send ('');
fileClose( fStdOut );
fStdOut := -99;
finally
{ the bottom line! }
closeApp( application );
end;
end;
procedure TCGIEnvData.setIniFilename( value : string );
var
x : TWebSite;
begin
fINIFilename := value;
if NOT ( csDesigning in componentState ) then begin
x := TWebSite( fServerComponent );
x.initData;
end;
end;
function TCGIEnvData.swapChar( s : string; fromChar : char; toChar : char ) : string;
var
i : shortint;
begin
for i := 1 to length( s ) do
if s[i] = fromChar then
s[i] := toChar;
result := s;
end;
{***************************************************************}
{***************************************************************}
constructor TWebsite.create(AOwner: TComponent);
begin
if AOwner = nil then
raise exception.create( 'Tried to create TWebsite object with nil owner.' );
inherited Create(AOwner);
fIniFile := nil;
fServerType := WebSite;
{ this works only if AOwner is a valid pointer, which it should be
since we're only created from within a CGIEnvData component }
fCGI := TCGIEnvData(AOwner); { connect back to CGIEnvData }
end;
procedure TWebSite.initData;
begin
if fCGI.WebSiteINIFilename = '' then
raise exception.create( 'WebSiteINIFilename is blank' );
try
{ create pointer to INI file }
fIniFile := tInifile.create( fCGI.WebSiteIniFilename );
except
raise exception.create( 'Can not create tIniFile object' );
end;
with fCGI do begin
{ [CGI] <== The standard CGI variables }
fCGICGIVersion := readWebSiteCGIString( 'CGI Version', FALSE );
fCGIRequestMethod := readWebSiteCGIString( 'Request Method', FALSE );
{ Request Protocol handled elsewhere }
fCGIExecutablePath := readWebSiteCGIString( 'Executable Path', FALSE );
fCGIServerSoftware := readWebSiteCGIString( 'Server Software', FALSE );
fCGIServerAdmin := readWebSiteCGIString( 'Server Admin', TRUE );
end;
with fIniFile do begin
{ [System] <== Windows interface specifics }
{ in visual basic: CGI_GMTOffset = CVDate(CDbl(buf) / 86400#)' Timeserial offset }
fCGI.fSystemGMToffset := ( readInteger( 'System', 'GMT Offset', 0 ) / 86400 ); { fixed 6/12/95 aml }
fCGI.fSystemOutputFile := readString( 'System', 'Output File', 'ann_x.out' );
fCGI.fSystemContentFile := readString( 'System', 'Content File', '' );
end;
end;
destructor TWebsite.Destroy;
begin
fIniFile.free;
inherited Destroy;
end;
function TWebsite.readWebSiteCGIString( key : string; okEmpty : boolean ) : string;
begin
result := fINIfile.readString( 'CGI', key, cginotfound );
{ notfound is not always bad, e.g. user might not be authenticated first time around }
if result = cginotfound then
if NOT okEmpty then
fCGI.sendComment( '[CGI] ' + key + ' key not found in WebSite INI file' );
end;
procedure TWebsite.CGIData( p : pString; key : string; okEmpty : boolean );
begin
if p^ = '' then
p^ := readWebSiteCGIString( key, okEmpty );
end;
{ returns KEY NOT FOUND and logs sendComment if that happens; otherwise full text }
function TWebsite.getSmallField( key : string ) : string;
begin
with fIniFile do
result := readString( 'Form Literal', key, cginotfound );
if result = cginotfound then
fCGI.sendComment( 'Field ' + key + ' is not in [Form Literal] section of WebSite .ini file.' );
end;
{ returns KEY NOT FOUND and logs sendComment if that happens; otherwise full text }
function TWebsite.getSmallMultiField( key : string ) : Tstringlist;
var
varval, varname: string;
begin
result := TStringList.create;
varname := key;
varval := 'start';
while varval <> cginotfound do begin
with fIniFile do
varval := readString( 'Form Literal', varname, cginotfound );
if varval <> cginotfound then begin
result.add( varval );
varname := key+'_'+IntToStr(result.count);
end;
end;
end;
{ if key not found, then 3 things happen. 1. returns false
2. externFilename set to '' 3. error comment sent out }
function TWebsite.getExternalField( key : string;
var externFilename : string;
dest : PChar ) : boolean;
var
info : string;
buffer : string;
x : byte;
dataSize : integer;
fileHandle : integer;
begin
{ [Form External] notes written by Bob Denny and included in cgi.bas
If the decoded value string is more than 254 characters long,
or if the decoded value string contains any control characters,
the server puts the decoded value into an external tempfile and
lists the field in this section as:
key=
where is the path and name of the tempfile containing
the decoded value string, and is the length in bytes
of the decoded value string.
Data larger than 65,536 bytes goes to [Form Huge] section. }
with fIniFile do
info := readString( 'Form External', key, cginotfound );
if info = cginotfound then
begin
result := FALSE;
externFilename := '';
fCGI.sendComment( 'Field ' + key + ' is not in [Form External] section of WebSite .ini file.' );
exit;
end;
x := pos( ' ', info );
externFilename := copy( info, 0, x - 1 );
dataSize := strToInt( copy( info, x, 10 ) ) ;
dest := strAlloc( dataSize + 1 );
{ !!! need more error checking in this routine }
fileHandle := fileOpen( externFilename, fmOpenRead );
fileRead( fileHandle, dest, dataSize );
fileClose( fileHandle );
result := TRUE;
end;
function TWebsite.getTextArea( key : string; dest : TStringList ) : boolean;
var
info : string;
buffer : string;
x : byte;
dataSize : integer;
f : textFile;
externfilename : string;
begin
result := TRUE;
if dest = nil then
begin
dest := TStringList.create;
fCGI.sendComment( 'TstringList was nil in call to getExternalStrList. ' +
'You should be using TStringList.create and .free yourself.' );
end;
dest.clear;
{ first see whether it's there as a one-liner }
buffer := getSmallField( key );
if buffer <> cginotfound then
begin
dest.add( buffer ); { all done }
exit;
end;
with fIniFile do
info := readString( 'Form External', key, cginotfound );
if info = cginotfound then
begin
result := FALSE;
fCGI.sendComment( 'Field ' + key + ' is not in [Form External] section of WebSite .ini file.' );
exit;
end;
x := pos( ' ', info );
externFilename := copy( info, 0, x - 1 );
dataSize := strToInt( copy( info, x, 10 ) ) ;
{ !!! need more error checking in this routine }
assignFile( f, externFilename );
reset(f);
while NOT eof(f) do
begin
readLn( f, buffer );
dest.add( buffer );
end;
closeFile( f );
result := TRUE;
end;
procedure closeApp( app : TApplication );
begin
{Thanks to Charlie Calvert for the postMessage syntax. }
{ FYI: app.close; doesn't work and halt(1) is bad because resources aren't freed. }
postMessage( app.Handle, wm_Close, 0, 0);
end;
{***************************************************************}
{***************************************************************}
procedure Register;
begin
RegisterComponents('CGI', [TCGIEnvData]);
end;
end.