For Development HEAD DRAFTSearch (procedure/syntax/module):

12.97 www.cgi - CGI utility

Module: www.cgi

Provides a few basic functions useful to write a CGI script.

In order to write CGI script easily, you may want to use other modules, such as rfc.uri (see rfc.uri - URI parsing and construction), text.html-lite (see text.html-lite - Simple HTML document construction) and text.tree (see text.tree - Lazy text construction).

Note: it seems that there is no active formal specification for CGI. See http://w3c.org/CGI/ for more information.

Metavariables

Parameter: cgi-metavariables :optional metavariables

{www.cgi} Normally, httpd passes a cgi program various information via environment variables. Most procedures in www.cgi refer to them (meta-variables). However, it is sometimes inconvenient to require environment variable access while you’re developing cgi-related programs. With this parameter, you can overrides the information of meta-variables.

Metavariables should be a list of two-element lists. Car of each inner list names the variable, and its cadr gives the value of the variable by string.

For example, the following code overrides REQUEST_METHOD and QUERY_STRING meta-variables during execution of my-cgi-procedure. (See Parameters, for the details of parameterize).

(parameterize ((cgi-metavariables '(("REQUEST_METHOD" "GET")
                                    ("QUERY_STRING" "x=foo"))))
  (my-cgi-procedure))
Function: cgi-get-metavariable name

{www.cgi} Returns a value of cgi metavariable name. This function first searches the parameter cgi-metavariables, and if the named variable is not found, calls sys-getenv.

CGI scripts may want to use cgi-get-metavariable instead of directly calling sys-getenv; doing so makes reuse of the script easier.

Parameter extraction

Function: cgi-parse-parameters :key :query-string :merge-cookies :part-handlers

{www.cgi} Parses query string and returns associative list of parameters. When a keyword argument query-string is given, it is used as a source query string. Otherwise, the function checks the metavariable REQUEST_METHOD and obtain the query string depending on the value (either from stdin or from the metavariable QUERY_STRING). If such a metavariable is not defined and the current input port is a terminal, the function prompts the user to type parameters; it is useful for interactive debugging.

(If you need just to decompose URL query string, you can use uri-decompose-query in rfc.uri (see rfc.uri - URI parsing and construction). This procedure has more features useful for CGI scripting.)

If REQUEST_METHOD is POST, this procedure can handle both application/x-www-form-urlencoded and multipart/form-data as the enctype. The latter is usually used if the form has file-uploading capability.

When the post data is sent by multipart/form-data, each content of the part is treated as a value of the parameter. That is, the content of uploaded file will be seen as one big chunk of a string. The other information, such as the original file name, is discarded. If it is not desirable to read entire file into a string, you can customize the behavior by the part-handler argument. The details are explained in the "Handling file uploads" section below.

When a true value is given to merge-cookies, the cookie values obtained from the metavariable HTTP_COOKIE are appended to the result.

Note that the query parameter may have multiple values, so cdr of each element in the result is a list, not an atom. If no value is given to the parameter, #t is placed as its value. See the following example:

(cgi-parse-parameters
  :query-string "foo=123&bar=%22%3f%3f%22&bar=zz&buzz")
 ⇒ (("foo" "123") ("bar "\"??\"" "zz") ("buzz" #t))
Function: cgi-get-parameter name params :key :default :list :convert

{www.cgi} A convenient function to obtain a value of the parameter name from parsed query string params, which is the value cgi-parse-parameters returns. Name should be a string.

Unless true value is given to list, the returned value is a scalar value. If more than one value is associated to name, only the first value is returned. If list is true, the returned value is always a list, even name has only one value.

After the value is retrieved, you can apply a procedure to convert the string value to the appropriate type by giving a procedure to the convert argument. The procedure must take one string argument. If list is true, the convert procedure is applied to each values.

If the parameter name doesn’t appear in the query, a value given to the keyword argument default is returned; the default value of default is #f if list is false, or () otherwise.

Output generation

Function: cgi-header :key status content-type location cookies

{www.cgi} Creates a text tree (see text.tree - Lazy text construction) for the HTTP header of the reply message. The most simple form is like this:

(tree->string (cgi-header))
  ⇒ "Content-type: text/html\r\n\r\n"

You can specify alternative content-type by the keyword argument content-type. If you want to set cookies to the client, specify a list of cookie strings to the keyword argument cookies. You can use construct-cookie-string (see rfc.cookie - HTTP cookie handling) to build such a list of cookie strings.

The keyword argument location may be used to generate a Location: header to redirect the client to the specified URI. You can also specify the Status: header by the keyword argument status. A typical way to redirect the client is as follows:

(cgi-header :status "302 Moved Temporarily"
            :location target-uri)
Parameter: cgi-output-character-encoding :optional encoding

{www.cgi} The value of this parameter specifies the character encoding scheme (CES) used for CGI output by cgi-main defined below. The default value is Gauche’s native encoding. If the parameter is set other than the native encoding, cgi-main converts the output encoding by gauche.charconv module (see gauche.charconv - Character Code Conversion).

Convenience procedures

Function: cgi-main proc :key on-error merge-cookies output-proc part-handlers

{www.cgi} A convenient wrapper function for CGI script. This function calls cgi-parse-parameters, then calls proc with the result of cgi-parse-parameters. The keyword argument merge-cookies is passed to cgi-parse-parameters.

proc has to return a tree of strings (see text.tree - Lazy text construction), including the HTTP header. cgi-main outputs the returned tree to the current output port by write-tree, then returns zero.

If an error is signaled in proc, it is caught and an HTML page reporting the error is generated. You can customize the error page by providing a procedure to the on-error keyword argument. The procedure takes an <condition> object (see Conditions), and has to return a tree of string for the error reporting HTML page, including an HTTP header.

When output the result, cgi-main refers to the value of the parameter cgi-output-character-encoding, and converts the character encoding if necessary.

The output behavior of cgi-main can be customized by a keyword argument output-proc; if it is given, the text tree (either the normal return value of proc, or an error page constructed by the error handler) is passed to the procedure given to output-proc. The procedure is responsible to format and output a text to the current output port, including character conversions, if necessary.

The keyword argument part-handlers are simply passed to cgi-parse-parameters, by which you can customize how the file uploads should be handled. See the "Handling file uploads" section below for the details.

If you specify to use temporary file(s) by it, cgi-main makes sure to clean up them whenever proc exits, even by error. See cgi-add-temporary-file below to utilize this feature for other purpose.

Before calling proc, cgi-main changes the buffering mode of the current error port to :line (See port-buffering in Common port operations for the details about the buffering mode). This makes the error output easier for web servers to capture.

The following example shows the parameters given to the CGI program.

#!/usr/local/bin/gosh

(use text.html-lite)
(use www.cgi)

(define (main args)
  (cgi-main
    (lambda (params)
      `(,(cgi-header)
        ,(html-doctype)
        ,(html:html
          (html:head (html:title "Example"))
          (html:body
           (html:table
            :border 1
            (html:tr (html:th "Name") (html:th "Value"))
            (map (lambda (p)
                   (html:tr
                    (html:td (html-escape-string (car p)))
                    (html:td (html-escape-string (x->string (cdr p))))))
                 params))))
       ))))
Function: cgi-add-temporary-file filename

{www.cgi} This is supposed to be called inside proc of cgi-main. It registers filename as a temporary file, which should be unlinked when proc exits. It is a convenient way to ensure that your cgi script won’t leave garbages even if it throws an error. It is OK in proc to unlink or rename filename after calling this procedure.

Parameter: cgi-temporary-files

{www.cgi} Keeps a list of filenames registered by cgi-add-temporary-file.

Handling file uploads

As explained in cgi-parse-parameters above, file uploads are handled transparently by default, taking the file content as the value of the parameter. Sometimes you might want to change this behavior, for the file might be quite big and you don’t want to keep around a huge chunk of a string in memory. It is possible to customize handling of file uploads of cgi-parse-parameters and cgi-main by part-handlers argument. (The argument is only effective for the form data submitted by multipart/form-data enctype)

The part-handlers argument is, if given, a list of lists; each inner list is a form of (name-pattern action kv-list …). Each uploaded file with a matching parameter name with name-pattern is handled according to action. (Here, a parameter name is the ’name’ attribute given to the input element in the submitted form, not the name of the uploaded file).

Name-pattern must be either a list of string (matches one of them), a regexp, or #t (matches anything).

Action must be either one of the followings:

#f

Default action, i.e. the content of the uploaded file is turned into a string and becomes the value of the parameter.

ignore

The uploaded content is discarded.

file

The uploaded content is saved in a temporary file. The value of the parameter is the pathname of the temporary file.

For this action, you can write an entry like (name-pattern file prefix), to specify the prefix of the pathname of the temporary file. For example, if you specify ("image" file "/var/mycgi/incoming/img"), the file uploaded as "image" parameter will be stored as something like /var/mycgi/incoming/img49g2Ua.

The application should move the temporary file to appropriate location; if you’re using cgi-main, the temporary files created by this action will be unlinked when cgi-main exits.

file+name

Like file, but the value of the parameter is a list of temporary filename and the filename passed by the client. It is useful if you want to use client’s filename (but do not blindly assume the client sends a valid pathname; for example, you shouldn’t use it to rename the uploaded file without validating it).

procedure

In this case, procedure is called to handle the uploaded contents. It is called with four arguments: (procedure name filename part-info iport).

Name is the name of the parameter. Filename is the name of the original file (pathname in the client). Part-info is a <mime-part> object that keeps information about this mime part, and iport is where the body can be read from. For the details about these arguments, see rfc.mime - MIME message handling; you might be able to use procedures provided by rfc.mime, such as mime-retrieve-body, to construct your own procedure.

If you create a temporary file in procedure, you can call cgi-add-temporary-file to make sure it is removed even if an error occurs during cgi processing.

If kv-list is given after action, it must be a keyword-value list and further modifies action. The following keywords are supported.

:prefix

Valid only if action is either file or file+name. Specifies the prefix of the temporary file. If you give :prefix "/tmp/foo", for example, the file is saved as something like /tmp/fooxAgjeQ.

:mode

Valid only if action is either file or file+name. Specifies the mode of the temporary file in unix-style integer. By default it is #o600.

Note that the parameters that are not file uploads are not the subject of part-handlers; such parameter values are always turned into a string.

Here’s a short example. Suppose you have a form like this:

<form enctype="multipart/form-data" method="POST" action="mycgi.cgi">
<input type="file" name="imagefile" />
<input type="text" name="description" />
<input type="hidden" name="mode" value="normal" />
</form>

If you use cgi-parse-parameters in mycgi.cgi without part-handlers argument, you’ll get something like the following as the result. (The actual values depend on how the web client filled the form).

(("imagefile" #**".....(image file content as a string)....")
 ("description" "my image")
 ("mode" "normal"))

If you pass '(("imagefile" file :prefix "/tmp/mycgi")) to part-handlers instead, you might get something like the following, with the content of uploaded file saved in /tmp/mycgi7gq0B

(("imagefile" "/tmp/mycgi7gq0B")
 ("description" "my image")
 ("mode" "normal"))

If you use a symbol file+name instead of file above, you’ll get something like ("/tmp/mycgi7gq0B" "logo.jpg") as the value of "imagefile", where "logo.jpg" is the client-side filename. (Note: the client can send any string as the name of the file, so never assume it is a valid pathname).



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT