class ref name: CGIclient
category-group: www
layer: 12
header file: z_www.h

synopsis.
A class object for making a CGI program. This object is designed to be a singleton instance within a program, and it can work in conjuction with a separate server program. The concept is that the CGI program is "lightweight", containing a minimal amount of code whos job it is to read the variables from a form provided by a web server and originating in a web client (eg a browser, such as Internet Explorer, Mosaic, Firefox, Opera, or Safari), and forward the data to a server. The server is a continuous-running program that does the heavy operations (database query, document printing, LDAP directory lookup, etc). The CGI program waits for the server's response - thus, it does a synchronous communiation with a server, and when it gets the reply, formats it into HTML. From that point, the web server forwards the CGI program's output (as a block of text) to the client.

 

CGI client data flow

description.
The CGIclient_o class is an all-in-1 solution. It is designed to interact with the server_o class. The object loads the environment variables (common to both GET and POST methods) via the member function load_environment(). This loads variables such as CONTENT_TYPE, CONTENT_LENGTH, DOCUMENT_ROOT, HTTP_REFERER, QUERY_STRING, REMOTE_ADDR, and many others into the internal variables myvars_kv and myvars_bag. Why two containers instead of one? This allows you to choose whether you want this list as a namevalue_set object or as a recursive data bag (rec_dbag_o). Both are loaded simultaneously. The data is small, so efficiency is not an issue. You have the choice of whether or not to include the PATH variable. The default is to exclude it. Why? All the other variables are relatively short, fitting into an 80-character line, whilst PATH typically spans 3-6 rows. And it is normally of little or no use to most CGI programs. To include it, set the run/addpath INI variable to YES.

Since normally a form in an HTML page can call an executable, but not provide command-line parameters, the object has been designed to load an INI file at the beginning of its run. There are 3 ways the file can be found. Each way is checked in the sequence given here. Once a matching INI file is found, the search is over:

  1. The application program can set a path to a specific file, via object's member function call 'set_inifile()'. Thus, the name of the file can be set in any way (the file name can be hard-coded).
  2. Environment variable INI_FILE is checked. If it is set, its value becomes the path to the object/program's INI file. The problem with this approach is that, if multiple CGI programs (with different names) are used, they will all use the same file. This may or may not be the desired result.
  3. The program's base name is used as the base name for its INI file. The INI file must be in the same directory as the program (assuming the program did not change its directory prior to using the object). The path name used is derived from argv[0] of the entry point for the program. This value can be artificial - that is, you can "fake out" the object by supplying your own argc/argv set of parameters. If the CGI program path, as found in argv[0], is "/var/www/cgi-bin/gateway.exe", the INI file searched for will be "gateway.ini".

Here is a sample INI file:
[run]
host      = localhost
port      = 1234
method    = POST
force_meth= NO
timeout   = 0
addpath   = NO

[log] volume = 40 debug = 1
[other] help = NO version = NO

The object automatically determines whether the incoming onslaught of variables is provided by GET or via POST method. This fact is encoded in the REQUEST_METHOD environment variable, which is common to both methods. You can over-ride this variable and force one or the other method by specifying the INI parameter run/force_meth = YES (default value is NO), which will then look for the INI parameter run/method (which should be there, if you're using 'force_meth'). If run/method is not found in the INI file, or has an invalid value (should be either GET or POST), the object reverts back to setting the method according to the REQUEST_METHOD environment variable. Why someone would want to do this over-ride, we don't know, but perhaps it will turn out to be a useful security feature. After all, you (or a teammate; people in your group) are probably the authors of both the web page and the CGI processor, so you know in advance which method will be used. Note that setting the run/method INI variable alone (without adding 'force_meth') is effectively pointless, as the object will look to REQUEST_METHOD for guidance, unless it is forced to look away.

The INI file parameter log/debug can be set to non-zero to cause dumps of either the application or environment variables. Set it to 1 for application variables, 2 for environment variables. The format of the messages is hard-coded. This INI variable is provided as a convenience for troubleshooting. For production programs, it should always be set to 0 (the default).

If an error occurs, the CGI program should print some sort of error message. This is done in the die() member function. If the application relies on the base class (CGIclient_o) to do the server connection, if there is an error in communicating with the server, die() will be called internally (eg, within CGIclient_o::sendto_server()). Since the base class does not know if headers have been sent, it relies on its own function to print the header, namely dumppage_head(), which by default is simply:

Content-type: text/html

    

Conversely, it has its own "footer" printing function, dumppage_tail(), which emits:
    

Both of these functions are virtual, so you can implement your own. Also, die() is virtual, so you can define your own error handler. You should mind the return values: 0 if it was printed; 1 if it was already called earlier, and -1 if an error ocurred inside the function call.

If overloading die(), you should handle the error codes (defined in z_types.h) that CGIclient_o::sendto_server()) passes to die():

zErr_NotConfigured
zErr_NoConnection
zErr_Write_Failed
zErr_Read_Failed
zErr_CantTalk
Alternatively, you can call this classe's (CGIclient_o) implementation of die() within your overloaded implementation for these error code values.

member functions (primary)

CGIclient_o()
SIGNATURE: CGIclient_o ()
SYNOPSIS: creates a a new, 'empty' CGI clientg object. All of its settings are at default values after this construction.
 

destructor
SIGNATURE: ~CGIclient_o ()
SYNOPSIS:
virtual destructor. The CGI clientg object instance is "reset"; all its internal variables are reset to their at-construction values.
 

input_method()
SIGNATURE: int input_method (int *pi = NULL)
SYNOPSIS:
informs what input method (POST, GET) is to be used to get data. The method is controlled by REQUEST_METHOD environment variable, but it can be forced to a specific value by the application (by setting the INI variable "run/force_meth" to YES).
PARAMETERS

  • pi: [output] error indicator variable. values:
    0: method successfully retrieved and is valid;
    zErr_NotInitialized: INI file was not loaded prior (this is a warning only);
    zErr_NotConfigured: method cannot be determined. The INI parameter value was not found, or it was an illegal value, and the var REQUEST_METHOD was not set in the environment;
    zErr_NotFound: no valid method set (reliance on the environment); zErr_Data_Corrupted: the input method value stored inside is bad
  • RETURNS: integer enumeration, one of these values: zProto_CGI_INVALID, zProto_CGI_NONE, zProto_CGI_GET, or zProto_CGI_POST.
     

    host()
    SIGNATURE: string_o host (int *pi = NULL)
    SYNOPSIS: returns a string containing the name of the computer hosting the program's corresponding server program.
    PARAMETERS

  • pi: [output] error indicator variable. values:
    0: host name has been set. name retrieved;
    zErr_NotInitialized: no host name set
  •  

    port()
    SIGNATURE: int port (int *pi = NULL)
    SYNOPSIS: returns the number of the socket port of the server program that the object is to connect to.
    PARAMETERS

  • pi: [output] error indicator variable. values:
    0: port number has been set. Value retrieved;
    zErr_NotInitialized: no port number was set
  • RETURNS:
    [n >= 0]: value of the port of the server program
    -1: no port number provided to the object
     

    include_path()
    SIGNATURE: boolean include_path () const
    SYNOPSIS:
    returns a boolean value to indicate if the object is to load the PATH variable into the object's internal set of environment variables relevant to CGI functionality.
    The value is simply passed through to the subroutines z_CGI_loadenv_to_namevalue() and z_CGI_loadenv_to_dbag().
    TRAITS: This functgion is not so important.
     

    is_loaded()
    SIGNATURE: boolean is_loaded () const
    SYNOPSIS:
    tells if the object parameters have been set via a INI file load() operation. This is informational, and useful in circumstances where the application relies on behaviour to be controlled via an INI file.
     

    enviro_str()
    SIGNATURE: string_o enviro_str (const string_o &name, int *pi = NULL)
    SYNOPSIS:
    returns the value of a CGI environment variable. This function could be considered to be a special case of z_env(). The environment variables must have been loaded into the object [via member function load_environment()] prior in order for this function to succeed.
    PARAMETERS

  • pi: [output] error indicator variable. values:
    0: environment variable exists, and was retrieved
    zErr_NotInitialized: 'load_environment()' not done prior
    zErr_NotFound: item not found in the object's internal list of environment variables
  •  

    vars_as_nameval()
    SIGNATURE: const namevalue_set_o &vars_as_nameval (int witch = 0) const
    SYNOPSIS:
    returns a handle (as a reference) to the internal variable that contains a list of variables. There are 2 groups of variables that this function can access:

    witch = 0 application variables defined in the browser's form
    witch = 1 environment environment variables common to all methods, provides info about the client and web browser

    This function merely provides a reference-handle to the container variable, whether the container has been filled or not. Thus there are no associated error conditions. The type of container returned is a namevalue_set object. The same data can be accessed as a list databag via the analous member function vars_as_dbag().
     

    vars_as_dbag()
    SIGNATURE: const list_dbag_o &vars_as_dbag (int witch = 0) const
    SYNOPSIS:
    this function is analogous to vars_as_nameval(). It provides a handle to the databag container
    This function merely provides a reference-handle to the container variable, whether the container has been filled or not. Thus there are no associated error conditions. The type of container returned is a list_dbag_ object. The same data can be accessed as a name-value set via the analous member function vars_as_nameval().
    PARAMETERS

  • witch: switch controller to define which list of variables is to be accessed: 0 for application (form) variables, 1 for environment variables. Any non-zero value will return the latter group (though the official value is 1).
  •  

    set_host()
    SIGNATURE: int set_host (const string_o &snam, int *pi = NULL)
    SYNOPSIS: sets the host computer name where the server is located
    PARAMETERS

  • pi: [output] error indicator variable. values:
    0: host name set
    zErr_NoMatch: host info entry not maintained (panic)
  • RETURNS:
    0: successfully set
    -1: error occured (should never happen)
     

    set_port()
    SIGNATURE: int set_port (const int the_port, int *pi = NULL)
    SYNOPSIS:
    sets the port number where the server is located. This member function should be used in conjunction with set_host().
    PARAMETERS

  • pi: [output] error indicator variable. values:
    0: port number set
    zErr_NoMatch: port number entry not maintained internally (panic situation)
    zErr_Param_BadVal: the_port value invalid (< 0)
  • RETURNS:
    0: successfully set
    -1: error occured (see value of 'pi' for reasons why)
     

    set_method()
    SIGNATURE: int set_method (const int new_meth, boolean do_force = FALSE, int *pi)
    SYNOPSIS:
    sets the access method (POST or GET). If do_force is not set [to TRUE], this member function will effectively not do anything as the object will not access it when it's time to if run/force_meth is set to OFF.
    PARAMETERS

    • new_meth: one of zProto_CGI_NONE, zProto_CGI_GET, or zProto_CGI_POST.
    • do_force: if TRUE, the entry for the run/force_meth parameter will be set "ON". if FALSE, it is set to "OFF". The former will result in ignore and over-ride of any REQUEST_METHOD environment variable.
    • pi: [output] error indicator variable. The values of this variable also include those of progconf_o::config(). values set in this routine:
      0: method set/changed
      zErr_NoMatch: method - internal entry not found (panic)
      zErr_Param_BadVal: first parameter value ('new_metho') is invalid
    RETURNS:
    0: successfully set
    -1: error occured
     

    set_inifile()
    SIGNATURE: int set_inifile (const string_o &sfnam, int *pi)
    SYNOPSIS:
    sets the INI file name to 'sfnam'. This function should be called before the file is accessed (which occurs in config() or cmdline()). Using this function replaces any previous existing value for the location of the INI file.
    PARAMETERS

    • sfnam: [input] string containing the file name (with or without the path) to the INI file to load. If the string is empty (""), no INI file will be used. In that case, the object will use its factory preset default values for all behaviour, unless overriden by command-line parameters.
    • pi: [output] error indicator variable. values:
      0: file address set
      zErr_NotFound: the file specified by 'sfname' was not found (informational extra; could be considered a warning, or error - depends on the application)
     

    reset_args()
    SIGNATURE: int reset_args ()
    SYNOPSIS:
    clears the internal argc / argv data structure. This function is used by reset() and is not intended for public consumption (ie, don't use it).
    RETURNS: 0 (always)
     

    config()
    SIGNATURE: int config (const int argc, const char *argv[], int *pi)
    SYNOPSIS:
    this significant member function:

    • sets up the argc / argv internal data;
    • configures object parameters by calling cmdline();
    • loads environment variablas;
    • loads application variablas.
    This function is automatically called by run() if it was not explicitly called earlier. The non-zero values of 'pi' are that of the functions called: setup_args(), cmdline(), load_environment(), and load_data().
     

    set_cmdvars()
    SIGNATURE: int set_cmdvars (const int the_argc, const char * the_argv[], int *pi = NULL)
    SYNOPSIS:
    copies the command-line parameters into the object. The intent of this function is to forward the parameters provided to main() (or whatever function is used as the entry point to the application) to the object. By calling this function, the application is relieved of having to explicitly call cmdline(). CGIclient_o::run() will call config(), which will then call cmdline(). The parameters used there will be based on the ones supplied by this call. If '-ini' was in the list of parameters, cmdline() will try to load an INI file (if there is one to load).
    PARAMETERS

    • the_argc: the number of char * parameters in parameter 'argv'. This should be the same value as main()'s argc. It can be synthetic (eg, a pseudo-command line).
    • the_argv: a list of character strings. The number of char[] buffers in the array 'the_argv' must match exactly the value of 'the_argc'. It is the responsibility of the application to do this. The passed-in variable can be synthetic (eg, a pseudo-command line).
    • pi: [output] error indicator variable. values:
      0: the vars were successfully set
      zErr_Param_NullPointer: null pointer or negative integer input parameter(s) provided
      zErr_Memory_Exhausted: internal variable(s) could not be allocated. This ia a panic situation, indicating memory is no longer available.
    RETURNS:
    0: arguments were successfully copied
    -1: error in argument structure (either the number of actual strings in 'the_argv[]' do not match 'the_argc', or a NULL pointer was detected, or 'the_argc' value was invalid (< 0).
     

    mark_headout()
    SIGNATURE: void mark_headout (boolean ison = TRUE)
    SYNOPSIS:
    this function, along with its sibling mark_tailout(), is used by derived classes to tell this [base] class that the "top part" of an HTML page has been emitted. This way, if an error occurs in CGIclient_o, the header text will not be re-transmitted (reprinted).
    PARAMETERS

  • ison: control variable to turn the 'top_out' internal flag on (TRUE) or off (FALSE). The TRUE case is expected to be done all the time in most cases, but if the application deems it necessary to turn it off, it may do so by explicitly providing this variable.
  • DESCRIPTION:
    the printing of HTML header and footer text is required in order to properly present a page in HTML format. These tasks are done automatically if an error occurs.
    TRAITS: this is a simple inline "setter" function.
     

    mark_tailout()
    SIGNATURE: void mark_tailout (boolean ison = TRUE)
    SYNOPSIS:
    this function, along with its sibling mark_headout(), is used by derived classes to inform this [base] class that the "bottom part" of an HTML page has been emitted. This way, if an error occurs in CGIclient_o, the footer text will not be re-transmitted (reprinted).
    PARAMETERS

  • ison: control variable to turn the 'bot_out' internal flag on or off. The default is TRUE (on), meaning that the application has printed the HTML "footer".
  • DESCRIPTION:
    the printing of HTML header and footer text is required in order to properly present a page in HTML format. These tasks are done automatically if an error occurs.
    TRAITS: this is a simple inline "setter" function.
     

    cmdline()
    SIGNATURE: int cmdline (const int argc, const char *argv[], int *pi)
    SYNOPSIS:
    this function will parse any command-line variables and load any INI file if argv[] contains "-ini" (used to specify the path to an INI file).
    Under normal operation for this object, there will be no command-line arguments. Therefore, this implemention of this cmdline() member function differs markedly from that of most other Z Directory objects. In this subroutine, if no command-line parameters are specified (argc <= 1), it will make a "synthetic" command-line if there is an INI file. It searches for a file to load, based on the following rules:

    • if it was set via explicit call to 'set_inifile()', use that.
    • if there is an environment variable INI_FILE, use that.
    • if we can extract the program's base name (from argv[0]), and find a file in the current diectory ([basename].ini)
    Each of these will be tried in this order until there is a match. Thus, if say [1] & [2] fail, and we find an INI file via [3], the last file will be used.
    PARAMETERS
    • argc: the number of parameters in the command-line. should be provided from the application entry-point function (eg, main()). It can be synthetic (eg, a pseudo-command line).
    • argv: a list of character strings representing the program's command-line parameters. The number of char[] buffers in the array 'argv' must match exactly the value of 'argc'. It is the responsibility of the application to do this. The passed-in variable can be synthetic (eg, a pseudo-command line).
    • pi: [output] error indicator variable. values:
      0: command-line parsing (and any INI file loading) done successfully.
      zErr_Resource_Missing: INI file name was specified, but no corresponding file found. zErr_NoMatch: no INI file name specified (warning only)
     

    run()
    SIGNATURE: int run (int *pi)
    SYNOPSIS:
    run() does any initial configuration tasks (handled by member function config()), formats the form-application variables into a message packet, sends off the packet to the server (if one exists), waits for the server's reply, formats the results and prints it out to stdout ("standard output"). At that point, it is the job of the web server to deliver the output to the client (eg, a web browser).
    This function can be redefined in a subclass. All of its tasks can be manually done via other member functions of this class. This way, the application can control the object's behaviours, including not sending the information to any server.
    TRAITS: this is a virtual function.
     

    load_environment()
    SIGNATURE: int load_environment (int *pi = NULL)
    SYNOPSIS:
    this function copies the values of the environment variables (relevant to CGI programs) into the object. Two internal containers are used. Hence there is duplicate data - the containers are of different types, allowing for a choice of which the application prefers to use for getting data values.
    PARAMETERS

  • pi: [output] error indicator variable. values:
    0: successful load
    [n != 0]: error occurred. Any non-zero values match those of global-scope functions z_CGI_loadenv_to_namevalue() and z_CGI_loadenv_to_dbag().
  • RETURNS:
    0: success
    -1: error
     

    load_data()
    SIGNATURE: int load_data (int *pi = NULL)
    SYNOPSIS:
    this subroutine loads the form-application data into the object. There are two internal containers of different types, just as with member function load_environment() (which see).
    PARAMETERS

  • pi: [output] error indicator variable. values:
    0: successful load
    [n != 0]: error occurred. Any non-zero values match those of global-scope functions z_CGI_loadenv_to_namevalue() and z_CGI_loadenv_to_dbag().
  • RETURNS:
    0: success
    -1: error
     

    version()
    SIGNATURE: void version (const char *progname) const
    SYNOPSIS:
    this function is intended to allow the application to display its version. It is an attempt to let the object have the look and feel of a rundriver object.
    PARAMETERS

  • progname: [input] character buffer text containing the application name.
  • TRAITS: this is a virtual function.
     

    help()
    SIGNATURE: void help (const char *progname = NULL) const
    SYNOPSIS: displays any help message. This classe's function does nothing.
    PARAMETERS

  • progname: [input] character buffer text containing the application name.
  • TRAITS: this is a virtual function.
     

    getvalue_byID()
    SIGNATURE: string_o getvalue_byID (const int x, int *pi) const
    SYNOPSIS:
    returns the value of the object's parameter entry in a 'struct progconf_param' list of records (see progconf_o class for more information).
    PARAMETERS

    • x: [input] index into object's 'struct progconf_param' list of records. The values must be one of those in the "zCGIC_.." block of values (see 'z_www.h').
    • pi: [output] error indicator variable. values:
      0: value retrieved
      zErr_Param_BadVal: 'x' is an out of bounds value
     

    debug_msg()
    SIGNATURE: int debug_msg (const string_o &msg, boolean do_top = TRUE, boolean do_bot = FALSE)
    SYNOPSIS:
    prints out a message string, in HTML format. intended to be used for debugging messages only. This is patterend after panic(). See the description of that function for details on how to use the parameters 'do_top' and 'do_bot'. This function uses the object's notion of whether the header and/or footer has been emitted.
    If the application has printed the headers without using the facility of this object, you should explicitly set 'do_top' to FALSE. Since this function is intended to be called repeatedly (whereas 'panic()' is to be used just once), the "footer" part of an HTML page is not printed (by default).
     

    panic()
    SIGNATURE: int panic (const string_o &msg, boolean do_top = TRUE, boolean do_bot = TRUE)
    SYNOPSIS:
    prints out the contents of 'msg' and returns -1. This can be used when the program encounters an irrecoverable error. The message format is in HTML, like so:

    "<B> + msg + </B> <P/>\n"
    Also, if 'do_top' is TRUE, this calls dumppage_head(), which prints an opening "HTML - HEAD - BODY" block of text. If 'do_bot' is TRUE, this calls dumppage_tail(), which prints a closing HTML "tail".
    This function is designed to be usable both by the object's internal code and application code. The internal code attempts to track whether the opening and closing components have been emitted or not.
    RETURNS: -1
     

    dumppage_head()
    SIGNATURE: int dumppage_head ()
    SYNOPSIS:
    This simply prints the following string to stdout:

    Content-type: text/html
    
    <HTML> <BODY>\n

    DESCRIPTION:
    If overloading this function (that is, you will define your own, with the same name and signiture in a subclass), your function should match the return code conventions as this [base] class.
    RETURNS:
    0: successfully printed the headers
    1: this function was already called [prior] (not an error)
    -1: error occured while trying to print headers
     

    dumppage_tail()
    SIGNATURE: int dumppage_tail ()
    SYNOPSIS:
    This simply prints the following string to stdout:

    </BODY>
    </HTML>
    

    DESCRIPTION:
    If overloading this function (that is, you will define your own, with the same name and signiture in a subclass), your function should match the return code conventions as this [base] class.
    RETURNS:
    0: successfully printed the footer
    1: this function was already called [prior] (not an error)
    -1: error occured while trying to print footer
     

    note.
    Web servers typically want the CGI program called to be as efficient as possible. Possibly, the act of running a program may be considered too much overhead. The architecture presented by this object is a halfway compromise. Although resource-intensive actions such as opening a database are not encouraged with this object, there is a lot of plumbing in this layer-12 object, resulting in some overhead incurred.

    Although firing up an executable and passing control to the CGIclient_o was the original design, this is by no means a mandatory architure for applications. The individual, atomic services provided by this class can be used in radically different designs and implementations. Thus, more efficient programs can still employ this object.

    examples.
    Compile this CGI client program named "mycgi.exe" (for example). Put a INI file name "mycgi.ini" in the same directory as the executable. You will need to create, configure, and run a Z Directory server program for this to fully work. This program forwards the data from a web page to a server listening on port 1592, and running on a computer named "columbus".

    Note that it overrides the base class run() with its own implementation.

    #include "z_www.h"
    
    class FormProcessor : public CGIclient_o
    {
    public:
        FormProcessor () { }
    private:
    };
    
    void main(int argc, char *argv[])
    {
        int ie1, ie2;
        z_start();
        FormProcessor x;
        ie1 = x.cmdline (argc, argv, &ie2);
        if (ie1) exit(1);
    
        x.set_host ("columbus");
        x.set_port (1592);
        x.run(&ie2);
        z_finish();
        exit(0);
    }
    
    int FormProcessor::run()
    {
        return 0;
    }