Easy CGI is a package of Delphi 2 units and sample programs to support the creation of dynamic Internet/Internet applications using CGI, a standard for external gateway programs to interface with information servers such as HTTP servers (for details refer to NCSA). Web applications created with Delphi and this toolbox are extremely fast and easy to handle.
The Easy CGI package contains 2 units (CgiUtil
and HTMLUtil) and a sample application for a guest book using
different methods to access data that demonstrates how to
This unit provides a powerful class to make the CGI communication with a web server as easy and fast as possible. Access the fieldnames, field values and parameters sent by the web server simply and comfortable as arrays/stringlists with a minimum of programming overhead. You don't have to care for handling different communication methods like GET or POST, conversion of special characters and translation of logical to physical path names.
Usually a CGI program communicates with the web server via standard input/output. You may use the variable InputFName to "redirect" input reading from a file. Default: Empty for Standard-I/O.
The unit CgiUtil provides a class TCGIInput containing a set of properties and methods to access the information sent by the web server.
| Property Name | Type | Description |
|---|---|---|
| CGIInputStr | string | a string in the form 'fieldname1=fieldvalue1&fieldname3=fieldvalue2...' |
| count | integer | number of fields in the CGIInputString |
| FieldNames[index : integer] | string | fieldnames accessible as array in the range of 0..count-1 |
| FieldList | TStringList | fieldnames accessible as stringlist |
| FieldValues | TStringList | fieldvalues accessible as stringlist |
Refer to the form example to see
how to use these methods and properties.
To parse and filter field contents the following functions / procedures are available:
| Function / procedure name | Returns | Description |
|---|---|---|
| ParseField(const FieldName : String) | string | Returns value of field referenced by
FieldName=...&... Special chars (e.g. '+' for a blank and '%[hexcode]' are converted |
| FilterFields | - |
Removes HTML-comments, Script-Tags and SSI-Tags from CGIInputStr to prevent hackers from including commands like <!--#exec cmd="format c:" --> |
Additionally TCGIInput supports some basic database functions:
| Function / procedure name | Returns | Description |
|---|---|---|
| OpenDatabase(const DatabaseName : String) | - | try to open database; at first the name is assigned to a dabase file, afterwards ResetDatabase is called |
| ResetDatabase | - | try to get access to database; if locked then retry for 20 seconds; if still locked it will raise exception ENoDBAccess; a name must already be assigned to (global text file) TF (e.g. via OpenDatabase) |
| ReadFromDatabase | - | reads a line from a database and interpretes this as a CGI input string e.g. setting FieldNames, FieldValues etc. |
| AppendDatabase(const DatabaseName : String) | - | Simply append record to database without existance-check of keyfields; currently no retry function implemented (if file is locked by other process); more sophisticated: see AddToDatabase |
| AddToDatabase(const DatabaseName : String; const KeyFields : array of string) | integer | add record (CGIInputStr) to database;
if another record with same keyfields already exists, it
will be removed and the new record will be appended.
returns: 0 if record was simply appended, 1 if record replaced an existing one, 2 if an error occured |
| CloseDatabase | - | Closes database |
| parseDatabase(const DatabaseName : String; const KeyFields : array of string) | boolean | scans database for the first occurence of a record with the given keyfields; if found, record will be returned in CGIInputStr and result is TRUE otherwise result is set to FALSE |
| EOFDataBase | boolean | indicates that end of database is reached |
Additional information from the calling server is provided via environment variables. The contents of the most important environment variables is stored in the following string vars:
| Var Name / Const Name | Description |
|---|---|
| ScriptName | Name of Script (for self references) |
| ScriptTranslated | Physical name of Script (ParamStr(0)) |
| PathInfo | Contents of the environment variable PATH_INFO |
| PathTranslated | Contents of the
environment variable PATH_TRANSLATED or retrieved from an ini-file (Netscape C.Server gives no PATH_TRANSLATED) |
| HTTPReferer | Contents of the environment variable HTTP_REFERER |
| RequestMethod | Contents of the env. var. REQUEST_METHOD (GET or POST) |
| QueryString | Contents of the env. var. QUERY_STRING |
| RemoteAddr | IP address of remote user |
| RequestMethodRequired : boolean | = True (default); disable method check e.g. for debugging if set to False |
| YesNoStr : array [False..True] of string[4] | = ('No','Yes'); will be set at initialization to 'Nein', 'Ja', if language of operating system is german |
| TrueOptionStr | = 'TRUE WAHR YES JA'; strings representing boolean True |
| FalseOptionStr | = 'FALSE FALSCH NO NEIN'; strings representing boolean False |
Additional environment vars can be retrieved using the
function GetEnvStr (see below). More details about
environment vars set by the calling server may be found at
NCSA.
The following exceptions will be raised:
| Exception name | Description |
|---|---|
| ENoDBAccess | DB is locked too long |
| EUnknownRequestMethod | exception raised when REQUEST_METHOD < > GET or POST |
Some utility functions are available too:
| Function / procedure name | Returns | Description |
|---|---|---|
| GetEnvStr(const Key : ShortString) | ShortString | returns an environment string referenced by Key |
| ConvertToFieldValue(const S : string) | String | replaces FieldSeparators, AssignOperators ('&' and '='), '+' and CrLf with special characters ('%' + hexcode) |
Here is a simple form:
This is the HTML code for the form:
<form action="[insert your cgi program name here!]" method="POST">
<p>Name: <input type="text" size="40" name="Name" value="Your name"></p>
<p>Gender:
<input type="radio" checked
name="gender" value="male">male
<input type="radio" name="gender" value="female">female</p>
<p>Operating
system: <select
name="system" size="1">
<option selected>Windows 95</option>
<option>Windows NT</option>
<option>OS/2</option>
<option>Mac OS</option>
<option>Other</option>
</select></p>
<input type="hidden" name="HiddenField" value="Hidden value">
<p><input type="submit" name="SubmitBtn" value=" Submit ">
<input type="reset" name="ResetBtn" value=" Reset "></p>
</form>
Here are some examples how you can access the contents of the form within your CGI program using methods and properties of the class TCGIInput:
CGIInputStr will be set to 'Name=Your+name&gender=male&system=Windows+95&HiddenField=Hidden+value', the string that was posted from the server to your program.
Count will be set to 4, the number of fields in your form (there is also one hidden field!).
FieldNames[0] will be set to 'Name', FieldNames[1] will be set to 'gender' etc., the names of the fields in your form.
FieldValues.strings[0] will be set to 'Your name', FieldValues.strings[2] will be set to 'Windows 95' etc., the values of the fields entered in the form by the user.
ParseField('Name') will return 'Your name'.
This unit contains a set of functions to support the creation of dynamic web pages. You will find functions to create basic HTML prologues as well as advanced functions for dynamic tables and forms. A very powerful function is included to merge predefined HTML template pages with data retrieved online by your application - a comfortable way to get nice looking dynamic, interactive pages with a minimum of programming. Details about HTML (HyperText Markup Language) can be found at NCSA.
Generally everything that is written from your CGI-program to standard output will be sent from the server to the user as dynamically created HTML-page. One option of the unit HtmlUtil is to write the ouptut in a temporary file first and send the whole bunch of information in one go, when the program ends. The advantage is that the user will still see the previous page and an hour glass as cursor - he does not have to wait in front of an empty page with a standard cursor thinking that the server has already died meanwhile. The third option is to create or modify a file and hand over a link in the returned CGI-header that the server will recognize. Al these options are controlled via the variables OutputF, OutputFName and LocationName. See the unit interfaces and the demo application for details.
HtmlUtil will support creating HTML pages real-time using the following basic output procedures:
| Basic Output Procedures | Description |
|---|---|
| SendPrologue | send a CGI prologue. Format and the way, how output will be handled depends on the global variables OutputFName and LocationName. Must be sent before any other text. |
| SendPageHeader(const Title : String; Refresh : integer) | Sends several tags that define the
header of a page including the title. Refresh >= 0: Page will be reloaded in ... seconds; Refresh = -1: Page does not change, can be reloaded from cache later; Refresh < -1: Page must be reloaded every time, when called |
| SendPageFooter | This will send several HTML tags to close a page |
| ShowMessage(const s:string) | Sends a text line |
| ShowError(const s:string) | Sends an error message 'Error: ...' |
| SendHREF(const RefLink,RefTxt : string) | inserts a link |
Table creation is simple using the the following table
procedures:
| Table Procedures | Description |
|---|---|
| StartTable(const TableCaption : string; BorderWidth : integer; Header : array of string) | Open a new table giving a certain caption (can be left blank), borderwidth and a headline |
| ShowTableRow(const CS: array of string) | Add a row to that table, fields are handed over as an array of strings (number of fields may change from row to row) |
| EndTable | Close a table |
Forms can be easily defined using the following form procedures:
| Form Procedures | Description |
|---|---|
| SendFormHeader(const Method : string) | open a form, parameter is the action-method (name of the CGI script to be executed plus path_info and parameters) |
| SendFormFooter(const SubmitStr, ResetStr : string) | close a form with submit button and optional reset button (if ResetStr <> '') |
| SendFormSelectStart(const SelectLabel, SelectName : string) | open an input field for a selection list |
| SendFormOption(const Option : string) | send an option of a form-selection list |
| SendFormSelectEnd | close a selection list |
| SendHiddenField(const Name,value : string) | send a hidden field to pass invisible information to the CGI script |
You can save a lot of programming if you already predefine the
layout of your dynamic page using HTML
templates created with a HTML editor and insert
dynamically the actual information with your CGI program:
| Template Procedures | Description |
|---|---|
| CopyTemplate_L(const FileName : string; FromMarker, ToMarker : string; SearchStr, ReplStr : TStringList) | copy a file with given Filename from FromMarker to ToMarker to OutputF and replace all occurences of SearchStr (StringList!) by ReplStr (StringList of same length); search is not case-sensitive, markers will not be copied. |
| CopyTemplate_A(const FileName : string; const FromMarker, ToMarker : string; const SearchStr, ReplStr : array of AnsiString) | copy a file with given Filename from FromMarker to ToMarker to OutputF and replace all occurences of SearchStr (open array!) by ReplStr (open array of same dimension); search is not case-sensitive, markers will not be copied. |
| ScanForFieldnames(const FileName : string; FromMarker, ToMarker : string; Delimiter : char; var FieldList : TStringList) | scan a file from FromMarker to ToMarker for strings enclosed in the given delimeter (e.g. '%'). These strings will be returned as Stringlist in FieldList. |
Refer to the sample guestbook application that makes excessive use of templates.
The guest book sample program makes excessive use of the units CgiUtil and HtmlUtil; it can be compiled in three different ways:
The guest book application also shows how to merge data into predefined HTML template pages to minimize programming overhead and to improve the layout of dynamically created pages.
To control the method, how the CGI program will handle the database you can use two conditional defines for the compiler:
{$DEFINE UseBDE} will activate the usage of the
Borland Database Engine instead of internal database procedures
of the unit CGIUtil. Application code size grows significantly
and requests delay due to a big overhead for initializing
database access. This method will be prefered, if you have to
handle huge databases or complex queries comfortable with the
powerful BDE. The compiler will include an additional unit
GBDBUtil that contains the code controlling the BDE.
{$DEFINE UseXL} will activate the usage of MS-Excel via OLE
automation instead of internal database procedures of unit
CGIUtil. The codesize of the CGI application is kept small and
delays happen only the first time, when Excel and the database
table is loaded. This is also a powerful way to create more
sophisticated 3 Tier web applications that use transactions with
several consequtive screens. The compiler will include an
additional unit GBXLUtil that contains code to control OLE
automated communication with MS Excel. Handling of the database
table happens inside Excel.
Don't activate both conditional defines or you will end up with a compiler error.
If none of the conditional defines is set, the guestbook application will use the internal database procedures of unit CGIUtil. Code size is kept very small, you will recognize nearly no delay.
The sample guestbook application requires that you have access to a web server that supports the HTTP protocol and CGI scripts, but this is true for nearly every server on the market. You must have the permission to store executable files in the servers script directories. In the following steps the default directory structure is described.
1. Below your WWW-Root directory you should create a direcory 'GuestBk' with the following files:
[WWW-Root]
|
+---[GuestBk]
|
+---- register.htm (the start page of your guestbook)
guestlst.htm (a template for listing guestbook entries)
guestrcp.htm (a receipt confirmation template)
graustr1.jpg (a background image)
2. Below your scripts directory you should create a directory 'GuestBk' with the following files:
[Scripts]
|
+---[GuestBk]
|
+--- guestbk.exe (the CGI program for the guestbook application)
guestbk.dat (a guestbook database file)
guestbk.dbf (another guestbook database file in dBase format)
Thats it!
If you do not store the files in the default directory structure you will have to update the HTML files. E.g. if you use a directory 'forms' instead of 'GuestBk' and your scripts are stored in a directory called 'cgi-bin' without any subfolders, an entry for the action method of a form would look like
<form action="/cgi-bin/guestbk.exe/forms?VIEW" method="POST">
==================== ===== ====
| | |
pathname of path to command passed to CGI program
CGI program templates
instead of
<form action="/scripts/guestbk/guestbk.exe/guestbk?VIEW" method="POST">
The program GuestBk supports three commands:
| ADD | Add a new data record to the guestbook | |
| VIEW | View the guestbook | |
| SHUTDOWN | With the 3 tier client/server version of the guestbook using Excel via OLE automation as database server, you can initiate a remote shutdown of Excel with this command | |
| TEST | The CGI program will return a lot of useful information, e.g. a set of environment variables |
You can call the program directly from your web browser e.g. by entering in your browser's address field:
http://127.0.0.1/scripts/guestbk/guestbk.exe?TEST