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 {

DELPHI CGI routines

} result := 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 ('

AltStyle によって変換されたページ (->オリジナル) /

'); 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.