AdaCGI: An Ada 95 Binding to CGI

AdaCGI (formerly called "Package CGI") is an Ada 95 interface to the "Common Gateway Interface" (CGI). AdaCGI makes it easier to create Ada programs that can be invoked by World-Wide-Web (WWW) HTTP servers using the standard CGI interface. Using it, you can create Ada programs that perform queries or other processing by request from a WWW user.

The website for current versions of AdaCGI is http://www.dwheeler.com/adacgi.

AdaCGI is copyright (C) 1995-1999 David A. Wheeler (dwheeler@dwheeler.com, or alternatively dwheeler@ida.org and wheeler@ida.org). The actual library is released using the LGPL open source license as modified by a special exception and a clarification (see the CGI specification for the actual license). In short, you can use this library in proprietary programs but any changes to the library must stay as open source. No payment is required, but please provide credit if you use this package. The demonstration programs and this documentation are covered by the GPL.

The special exception is the same as that used by many Ada libraries, including the GNAT Ada compiler library and GtkAda. It simply eliminates the requirement to create special object files to permit re-linking new libraries. While this is desirable, it's onerous to do using today's heavily optimizing compilers and hard to do with generics, so this exception is common in Ada open source programs.

The clarification states that any user who uses the server remotely still has the right to acquire the source of this library as used by the serving program. The clarification was added because, as an interface to the web, the issue of "who is executing this program" could be a source of confusion. If you allow users to run your program, then you can't make the library proprietary from them.

This Ada package provides two data access approaches from the CGI:

  1. As an associative array; simply provide the key name (as a string) and the value associated with that key will be returned.
  2. As a sequence of key-value pairs, indexed from 1 to Argument_Count. This access approach is similar to the Ada library Ada.Command_Line.

The main access routines support both Ada 95 types String and Unbounded_String.


The AdaCGI package is available in various formats, including zip, tarball (tar.gz), and RPM foramts. Again, go to the AdaCGI website to get the latest version in a variety of formats.


This documentation assumes that you are already familiar with the Common Gateway Interface (CGI) and HTML. To use this package you'll need to learn a little about Ada 95; the Lovelace Ada 95 tutorial provides one good way to do so.

Below are the following:

  1. Installing AdaCGI.
  2. Trying out a sample program.
  3. Details on the Ada 95 Binding to CGI.
  4. A Minimal Example.
  5. Cookies
  6. Contents of the CGI Distribution.
  7. Limitations.
  8. Security.
  9. Related Information Sources.
  10. Version Information.


Installing AdaCGI

If you use an RPM-based system (e.g., Red Hat Linux), just get and install the RPM file as usual. For example, on an i386-derived system (including Pentiums), you can get the precompiled file and just run:
 rpm -Uvh adacgi-*.i386.rpm
(note that this installs the documentation in /usr/doc/adacgi*, the source files in /usr/lib/gnat/adainclude, and the compiled files in /usr/lib/gnat/adalib). At the time of this writing I don't have a Debian (.deb) format, sorry; use the Unix-like instructions (next).

Otherwise, on a Unix-like system get the tarball, create and move into a new directory, and install as usual:

  mkdir adacgi; cd adacgi
  gunzip ../adacgi*.tar.gz
  tar xvf ../adacgi*.tar
On a Microsoft-based platform, get the zip file, create and move into a new directory, and install as usual, using a command like:
  mkdir adacgi
  cd adacgi
  pkunzip ..\adacgi*.zip

If you want to use the demonstration programs, then you need to compile them. If you're using the RPM version, first copy the demonstration programs:

 cp /usr/doc/adacgi*/sample/* adacgisample
 cd adacgisample
Then, for any format, type "make" to build the programs. This assumes that you have a suitable Ada compiler installed, of course; the files use the GNAT naming convention.

The files cgi.ads and cgi.adb are the actual library; the other files are documentation and example programs. At this time only the RPM files automatically install themselves in a library directory; on other systems you'll need to compile the library into a shared area or simply include the library as part of your server's source code directory. The last "make" only compiles the sample programs.

If during compilations you get messages like "getenv not found", you probably need to add the C library to the list of searched libraries. On most systems, that's automatic, but it isn't for ObjectAda. Anthony A. Lowe sent in this tip for ObjectAda users:

Basically the key to making this work in ObjectAda is to add the Win32 library to the search path (in Project-Settings-Search). Once it is there, it links fine.

Trying out a sample program

To actually use this library, you need to write a program (this is, after all, only a library!), test the program, and then install it so it will be invoked by your web server. Included are some sample programs so you can try things out and see how the library works.

Impatient to do something? Well, compile the demonstration programs as described above, and then run the "minimal" program by typing:

  ./minimal

The output will look like this:

Content-type: text/html

<HTML><HEAD><TITLE>Minimal Form Demonstration</TITLE>
</HEAD><BODY>
<FORM METHOD=POST>What's your Name?<INPUT NAME="username">
<INPUT TYPE="submit"></FORM>
</BODY></HTML>
The first line means that the program is returning an HTML file (the common case). The second (blank) line means that there is no more meta-information about the data to be returned to the user (such as cookies to be set). The rest of the lines are a tiny HTML file, which in this case presents a trivial form.

Notice that no web server is required here; we can just invoke the program directly. That's really handy in debugging, because sometimes when you're interacting with a buggy server program it's hard to determing why things are failing unless you can see EXACTLY what is being sent back.

So, how can we send data to this script? The answer is by setting the REQUEST_METHOD and QUERY_STRING environment variables. Setting the REQUEST_METHOD variable to GET causes the library to get its data from the QUERY_STRING variable. This QUERY_STRING environment variable is of the form "fieldname=value"; if there's more than one field, the entries should be separated by ampersands (&). If you want the values "=", "&", " ", or "%" in the value, write them as "%3D", "%26", "%20", and "%25" respectively using the standard URL escaping mechanism. On Unix-compatible systems running an sh-compatible shell (including Linux running bash), just do the following:

  REQUEST_METHOD="GET"
  export REQUEST_METHOD
  QUERY_STRING="name=David%20Wheeler&email=dwheeler@dwheeler.com"
  export QUERY_STRING

Now, when you re-run ./minimal, you'll see that the program instead simply lists all of the fields that you've sent and their data.

So, how could you get this running through a web server? Well, you'll need to copy this program into an area that the web server accepts for executables. By implication, that means that your web server will have to be configured to run programs in certain directories and that you have permission to write to such a directory. On a typical Linux system running the Apache web server, such a directory is probably called "/home/httpd/cgi-bin", and is writable by root, so on such a configuration you'd do this to make the server "minimal" available to all:

  su
  cp minimal /home/httpd/cgi-bin

Assuming that your web server will run programs and you've installed your server in an appropriate place, now you need to try it out. Start up a web browser and open up:

  http://localhost/cgi-bin/minimal
replacing "localhost" with the name of the machine the web server is at if it's not your local machine. You should see a request to enter your name, a text box for entry, and a submit button. You can provide preset values to it by opening the URL using a format like:
  http://localhost/cgi-bin/minimal?name=David%20Wheeler&email=dwheeler@dwheeler.com

You can also see the screenshot showing the demo.adb program in action.


Details on Ada 95 Binding to CGI

Now, let's talk about how to write your own programs using this library.

To use package CGI, "with CGI" in your Ada program. Package CGI will automatically load the CGI information when your Ada program begins executing. CGI handles both "GET" and "POST" forms of CGI data automatically. The form information from a GET or POST form is loaded into a sequence of variables; each variable has a Key and a Value. Package CGI transforms "Isindex" queries into a form with a single key (named "isindex"), and the key's value is the query value.

Once the main Ada program starts, it can make various calls to the CGI subprograms to get information or to send information back out.

A typical program using package CGI would first call "CGI.Put_CGI_Header", which tells the calling HTTP server what kind of information will be returned. Usually the CGI header is a reply saying "I will reply a generated HTML document", so that is the default of Put_CGI_Header, but you could reply something else (for example, a Location: header to automatically redirect someone to a different URL).

Most CGI programs handle various types of forms, and most should automatically reply with a blank form if the user hasn't provided a filled-in form. Thus, your program will probably call CGI.Input_Received, which returns True if input has been received (and otherwise it returns False). You should reply with a blank form if CGI.Input_Received is False.

You can then use various routines to query what data values were sent. You can query either by the name of a variable (what is the value of 'name'?) or by position (what was the first variable's key name and value sent?):

There are also a number of useful output functions:

Function Get_Environment simply calls the underlying operating system and requests the value of the given operating system variable; this may be useful for acquiring less-often-used CGI values. If the variable does not exist, function Get_Environment replies with a null string ("").


Minimal Example

Here is a minimal example. Procedure "Minimal" always replies with an HTML document. If input is received, the document is simply a list of the variable values. If no input is received, procedure Minimal replies with a simple fill-in form:

with CGI, Text_IO; use CGI, Text_IO;

procedure Minimal is
-- Demonstrate CGI interface.

-- To run this program directly (without an HTTP server), set the
-- environment variable REQUEST_METHOD to "GET" and the variable
-- QUERY_STRING to either "" or "x=a&y=b".

begin
  Put_CGI_Header;   -- We will reply with a generated HTML document.
  Put_HTML_Head("Minimal Form Demonstration"; -- Output <H1>Minimal ..</H1>
  if CGI.Input_Received then  -- Check if input was received.
    Put_Variables;  -- Input received; show all variable values.
  else
    -- No input received; reply with a simple HTML form.
    Put_Line("<FORM METHOD=POST>What's your Name?<INPUT NAME=""name"">" &
             "<INPUT TYPE=""submit""></FORM>");
  end if;
  Put_HTML_Tail;  -- End the HTML document, sending </BODY></HTML>
end Minimal;
Procedure Minimal is stored in file minimal.adb. More sophisticated sample programs are demo.adb and search.adb.


Cookies

Cookies are supported by this package. To set a cookie's value in a remote browser, call Set_Cookie with the appropriate parameters. Note that Set_Cookie must be called before Put_CGI_Header.

Cookie values are automatically loaded by this package on initialization. You can retrieve their values by calling Cookie_Value. You can retrieve cookies by their key value or by simple index. Just like form fields, a key can occur multiple times. Information other than the key and value (such as expiration time, domain, path, and secure setting) is not available, because this information is not sent in the underlying protocol.

You can send cookie values to your program without using a web server by setting the environment variable HTTP_COOKIE. This has the format "key=value", separated by a semicolon, using URL escapes. The distribution includes a sample program, cookie_test, that prints the "first" cookie and the first value of the cookie named "problem". Here's how to try it out on a Unix-like machine using an sh-like command shell (e.g., a typical Linux system):

  HTTP_COOKIE="first_cookie=first_value;problem=my%20problem"
  export HTTP_COOKIE
  ./test_cookie


Contents of CGI Distribution

File cgi.zip is the distribution collection of package CGI. It contains the following files:

cgi.html      - Documentation for package CGI.
cgi-doc.htm   - A duplicate of cgi.html (see README for an explanation).
cgi.ads       - The Ada 95 package specification for CGI.
cgi.adb       - The Ada 95 package body for CGI.
minimal.adb   - A minimal demonstration of how to use CGI.
demo.adb      - A larger demonstration of how to use CGI.
search.adb    - A larger demo that searches a set of files.
test_cookie   - A demo for getting cookie values.
test_send.adb - A demo for setting cookie values.
makefile      - The makefile to compile demo and minimal using GNAT.
README        - A short list of the files.

Note: all of the text files are stored in Unix text file format. MS-DOS users will need to convert them to MS-DOS text file format (some MS-DOS text editors will do this automatically).


Limitations

This package has the following known limitations:

  1. It only interfaces using the standard CGI interface. It doesn't support FastCGI or other interfaces.
  2. Doesn't support an object-oriented interface. You might want to look at the Perl5 CGI library CGI.pm.
  3. It doesn't support generation of forms with widgets automatically set to existing values. This is easily solved by creating a higher-level package that uses this package as an interface. That way, users can access capabilities more directly or not, their choice.
  4. It automatically initializes by reading in the CGI input; in a few odd cases this is limiting (though it also eliminates a common error).
  5. The way it handles String and Unbounded_String is at times awkward; perhaps requiring users to use the "+" convention or explicit type changes would be better.
  6. The current packaging could use some improvement for ease-of-use. In particular, the current zip and tarball packaging puts all the files in the current directory; they should create a subdirectory (note that you must fix the RPM spec file to do the same). A Debian package would be nice too.


Security

As with all CGI programs, there are security ramifications. If you don't already know them, examine the extant literature on the subject. For example, search altavista for "CGI" and "security": http://www.altavista.com/cgi-bin/query?q=%2BCGI+%2Bsecurity.

In general, ALWAYS check any values sent to you, and be conservative: identify the list of acceptable values, and reject anything that doesn't meet that list. Thus for strings, identify the legal characters and maximum length you'll accept. You may need to escape shell characters. For numbers, identify minimum and maximum values. Be very cautious about filenames; beware of filenames with ".." or "/" in them (it's best not to accept them at all, if you can).

It's worth noting that Ada (and AdaCGI) can easily handle the "NIL" character (ASCII 0, represented as %00 in URLs). However, many system functions called by an Ada program assume that NIL is the end of a string, so calling system functions with such values may cause surprises. This isn't really unique to Ada; Perl can also handle NIL.


Related Information Sources

  1. The current website for AdaCGI is http://www.dwheeler.com/adacgi.
  2. Old versions of this Ada binding to CGI are available via the Public Ada Library (PAL). The PAL card catalog provides a nice general way to find Ada components (this binding is categorized as a "software component").
  3. CGI information at the W3C.
  4. "Adding Cookies to your site" by Paul Bonner; an introduction to cookies including notes on privacy issues.
  5. Netscape's original specification on cookies.
  6. Cookie Central.
  7. General information on CGI is available from the NSCA.
  8. Package CGI is inspired by the perl CGI interface by Steven E. Brenner (S.E.Brenner@bioc.cam.ac.uk).
  9. Another source of inspiration was the perl CGI interface by L. Stein.
  10. Doug Smith uses a different interface from Ada to CGI in his WebAda program; you can see the webada source for more information.
  11. A different method for interfacing binding Ada with CGI is to use the "Un-CGI" interface.
  12. Many Ada resources are available through the Ada Home, including an on-line Ada 95 reference manual and a free on-line Ada 95 tutorial, Lovelace.
  13. Ada Power is another useful Ada site.
  14. The Ada for Linux team has useful Linux-specific information.
  15. GNAT, a no-cost Ada 95 compiler, is available through New York University (NYU).
  16. You can download an `unzip' program from the Info-ZIP archives, which has software to unzip files as well as create zip files (including both a free implementation and the shareware pkzip/pkunzip programs). The Info-ZIP archive index lists what files are available., An alternate (mirror) site for these zip format utilities is wuarchive.wustl.edu.


Version Information

This is version 1.4, released 21-Oct-1999. I changed the library license to the LGPL with minor additions as described in cgi.ads. I don't mind proprietary products using this library, but if they make improvements to this component and "release" its use to users I want EVERY user to be able to get the improvements. I also changed the documentation license to be a straight GPL license. All the demo programs are (C) 1995-1999 David A. Wheeler, licensed under the GPL license. The name of the packaged collection of files was changed to "AdaCGI", to clearly differentiate it from non-Ada CGI interfaces (the actual Ada package is still named "CGI", for backwards compatibility; after all, there's no ambiguity when calling from Ada :-) ). The program "minimal.adb" was changed to be more aesthetically pleasing (in particular, the submit button comes AFTER the data request). This version includes a patch by Juergen Pfeifer (Juergen.Pfeifer@t-online.de) that fixed an ambiguity in search.adb, and made use of his RPM packaging. I also added a patch by Bob Holcomb (bob_holcomb@hotmail.com) to directly support cookies, and modified his patch to eliminate a bug involving semicolons in cookie values. I added a major documentation section on how to start using the program (the "trying out" section).

Version 1.3 fixes a nasty bug in the low-level "getenv" routine, which kept this program from working on OS/2 and some other systems.

Version 1.2 added routines which get a Value and then get a Line or Line_Count all at once, developed by Clyde Roby (roby@ida.org). The Ustrings package (used by the search demo) has had two minor changes: Put_Line to a designated file now works correctly (instead of putting to the current output), and Get_Line can read in a line up to the maximum length of an Unbounded_String.

Major additions in version 1.1 are:

Version 1.0 was released June 1995.

This documentation is (C) 1995-1999 David A. Wheeler. This documentation is free; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This documentation is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

David A. Wheeler (dwheeler@dwheeler.com / dwheeler@ida.org)