Contents:
The dullcgi framework consists of some (but not all) modules from the
thalcgi.cgi
program, accompanied with the dedicated module
named dullcgi.o
, and is intended to ease creation of
additional (custom) CGI programs for Thalassa-based web sites. Basically
it enables the user to create a CGI program, which uses its own
configuration file (in ini format) as well as
the macroprocessor.
In the present version, dullcgi only allows to handle GET requests; also it doesn't provide access to the session information and user accounts. This will likely change in future versions.
Dullcgi is a framework, not a library in the classic sense. It
provides its own main
function, and expects the user to
provide a function named dullcgi_main
and two globally visible
constants of the type const char *
, named
dullcgi_config_path
and dullcgi_program_name
.
Facilities provided by the framework are mainly accessed through an object
of the DullCgi
class; the object is created by the framework,
and its address is passed to the user-supplied function as its only
parameter.
The framework is built as a static library archive file (one with the
``.a
'' suffix), in which all necessary object files are
placed, not only those from Thalassa own codebase (the
$thalassa/cms/
directory), but also all the necessary
libraries from the $thalassa/lib/*
subdirectories. The
archive is named simply dullcgi.a
, without the usual
lib
prefix, to make it clear it is not a
library despite the file is in the static library format.
It might be a good idea to take the $thalassa/examples/testcgi
directory's content as a kind of starting point. Well, the user module in
this example is perhaps the simplest thing possible, here is it:
#include <dullcgi.hpp> int dullcgi_main(DullCgi *cgi) { cgi->SendPage("default"); return 0; } const char *dullcgi_config_path = "test.ini"; const char *dullcgi_program_name = "TEST";
As you might guess, the dullcgi_config_path
constant's value
sets the name for the configuration file the framework must read. The
dullcgi_program_name
constant is a more or less
arbitrary name for your CGI program; it is only used as a part of the
default error page, which is displayed only in case the framework couldn't
read the configuration file.
The dullcgi_main
function in this (perhaps the simplest
possible) version causes the framework to unconditionally send to the
client the HTML page configured by the [page default]
section
within the configuration file. Please note the function
returns 0
; the value it returns is then returned by the
framework from its main
function, so it becomes the exit code
for the whole CGI program. You should only return a non-zero value in case
everything went so wrong that you don't want even an error message to be
sent back to the requesting client; well, hardly you'll ever need it.
A really simple configuration
example is provided in the dumb.ini
file (yes, it's not the
default version), here is the full contents of the file:
[page default] template = <?xml version="1.0" encoding="us-ascii"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=us-ascii" /> + <title>Simple test CGI program with dumb configuration</title> + </head> + <body> + <h1>It WORKS!</h1> + </body> +</html> +
The simplest version of the CGI program doesn't detect any errors on its
own, only the errors detected by the framework are processed, so it is more
or less acceptable to have no custom error page. The default page in this
example is configured in the simplest possible way — its full text is
given, with no calls to macros. If you decide to follow this way in your
own configuration, please keep in mind that the percent char
“%
” is used by the macroprocessor to recognize
macro calls, so in case you need the “%
”
char in your text, the char must be doubled.
To build your CGI program, first make sure the dullcgi.a
file
is ready for use; it builds by default if you just order your Thalassa
sources to build with a simple ``make
'' command. Then, a
command like
THAL=path/to/your/thalassa/directory \ g++ -Wall -g -I${THAL}/cms -I${THAL}/lib mycgi.cpp \ ${THAL}/cms/dullcgi.a -o mycgi.cgi
will do the thing.
You might want to authomate the things a bit, using a
Makefile
. A reasonable simplistic example of such file is
provided in the same directory.
The first thing the dullcgi framework does after the CGI program start is reading and parsing the configuration file. In case this fails, the framework emits its default (hardcoded) error page and quits; the user-supplied function doesn't even get the control.
Normally, the configuration file, which has the ini format, should contain:
[page NNN]
sections (where
NNN
is an internal page identifier, intended for the
user-supplied code to identify the desired page);[html]
section which contains snippets and templates;[errorpage]
that defines the error page template;[redirectpage]
that defines the page sent to the client
along with a redirect directive.The very minimum here is a single [page NNN]
section, as it is shown in the getting
started section. Parameters recognized within each of the
configuration sections are documented below.
Besides the configuration sections and parameters recognized by the dullcgi
framework, the configuration file may contain any information specific for
a particular CGI program. The DullCgi
class provides a method
to access such custom parameters.
The present version of dullcgi recognizes exactly one parameter, named
template
, for each of the page-defining sections
([page NNN]
, [errorpage]
and
[redirectpage]
). The parameter's value is passed through the
macroprocessor to make the full page text.
In the [errorpage]
template
parameter's value, three additional macros are
recognized:
%progname%
expands to the program name as set by
the dullcgi_program_name
constant;%errcode%
expands to the HTML error code for the error to
be reported, like 404
, 500
etc.;%errmessage%
expands to the HTML error message, like
page not found
.In the [redirectpage]
template
parameter's value, the framework recognizes one
additional macro, named url
, which expands to the target URL
for the redirection being processed.
The [html]
section of the configuration file, introduced to
contain text snippets and simple templates, works exactly the same way as
[html]
sections for both static content generator and the
thalcgi.cgi
program (use the link for details).
The dullcgi framework makes all common
macros available in the configuration file. Besides that, the
following macros are made available by the framework and work exactly the
same way as for the thalcgi.cgi
program:
Some context-specific macros may also be in effect within certain configuration parameters.
Besides that, the user-supplied (that is, your) module can introduce
additional macros using the DullCgi
class methods.
DullCgi
class objectThe function dullcgi_main
, which your module must provide, is
called by the framework with the address of DullCgi
class
object as its argument. All features of the framework are mostly accessed
through this object.
Before we go on with the object's methods documentation, please note the
classes provided by the Scriptpp library are
heavily used by these methods. Be sure to take a look at the brief
overview of the library (follow the link for it) before reading the
DullCgi
class interface documentation, or else you're at risk
not to understand anything.
To be really short, let's remind that ScriptVariable
is a
class representing strings, ScriptVector
represents a
(resizeable) vector of strings. The Scriptpp library also provides
implementation for the macroprocessor used by Thalassa CMS; the
macroprocessor itself is represented by the
ScriptMacroprocessor
class (or its descendant), and every
macro is implemented by a subclass of the (abstract) class
ScriptMacroprocessorMacro
.
It is also useful to keep in mind that ScriptVariable
provides
a converting constructor for const char*
, so for all
arguments of type const ScriptVariable&
you can use
simple string literals (in doublequotes).
It is highly likely you'll want your configuration file to hold some
parameters specific to your very particular CGI program. From within the
dullcgi_main
function, as well as from any other (your)
function you passed the DullCgi
object pointer to, you can
access the configuration file contents with the following method:
ScriptVariable GetConfigValue(const ScriptVariable §grp, const ScriptVariable §, const ScriptVariable ¶m, const ScriptVariable &specifier, bool expand_macros) const;
The first four parameters determine what information you'd like to access,
while the last one (expand_macros
) tells the framework whether
should the information extracted from the config file get passed through
the macroprocessor before it will be returned to you.
To understand the first four parameters, suppose you have something like
[foo bar] manager:friday = John manager = Alice
in your configuration file. Then, to get that name John
, you
should use code like this:
ScriptVariable manager_name = cgi->GetConfigValue("foo", "bar", "manager", "friday", true);
(use manager_name.c_str()
after that to get the C string).
Please note that the parameter value given with no
specifier
(Alice
) serves as a kind of default, so it will be
returned for any value of the specifier
argument other than
"friday"
. You can use 0
(which produces
“invalid” ScriptVariable
) if you just need the
default value.
In case the section you're trying to access is not a part of a group, that is, its name consists of only one word, then the second argument for the method must be left zero, too. So, if you've got smth. like
[abra] cadabra = blah-blah
then use
cgi->GetConfigValue("abra", 0, "cadabra", 0, true);
to get that "blah-blah"
value.
Sometimes these section groups can be used to define a kind of an array, or a list, or whatever else you actually don't know the number of elements nor their names in advance; actually, Thalassa CMS heavily uses this capability, e.g., to create ini-based lists. If you do something like that in your configuration file, you might want to be able to figure out which sections there are in a given section group. This is done by the
int GetSectionNames(const ScriptVariable &group_id, ScriptVector &names) const;
method. Suppose your configuration file contains the following:
[item foo] enable = yes [item bar] enable = yes [item bazz] enable = yes
(it is critical to have at least one non-empty parameter in each of the sections). Then, you can act like this:
ScriptVector names; int count; count = cgi->GetSectionNames("item", names);
After that, the count
variable will contain the
number 3
, and the name
object will hold
three strings: "foo"
, "bar"
and
"bazz"
.
The framework creates the macroprocessor object on its own, filling it with the predefined macros. You can control the macroprocessor to some extent. To work with it, you will perhaps need to add
#include <scriptpp/scrmacro.hpp>
to your module.
To add your own macro, specific to your CGI program, either derive
a class from
ScriptMacroprocessorMacro
,
or use one of the predefined classes, such as
ScriptMacroConst
or ScriptMacroScrVar
.
Make an object with operator new
and pass the pointer to the
framework using the method
void AddMacro(ScriptMacroprocessorMacro *p);
Please note the ownership over the object is considered to be transferred
here, that is, once you called this method, the macro object from now on
belongs to the macroprocessor and will be deleted by its destructor.
That's why it must be created with new
and in
no other way.
You can set the vector of
positionals
for the
macroprocessor using metod
void SetPositionals(const ScriptVector &v);
Sometimes it may be necessary to add some macros locally, perform some
transformations, then forget the macros. This may be achieved by creating
a temporary (local) clone of the macroprocessor. The DullCgi
class provides the following two methods for that:
ScriptMacroprocessor *MakeMacroprocessorClone() const; static void DisposeMacroprocessorClone(ScriptMacroprocessor *p);
Please keep in mind that objects of macros you pass to the local clone will belong to the clone and will be deleted once you dispose the clone object.
Once you're done with all preparations and other things your CGI might need to complete, you must tell the framework what actually to send to the client. To do so you must call exactly one of the following methods:
void SendPage(const ScriptVariable &pgid); void SendErrorPage(int code, const ScriptVariable &cmt); void SendRedirect(const ScriptVariable &url);
The SendPage
method is used in case everything is Ok, so your
program should just send a web page to the client. The page must be
configured in the configuration file using a
page
section, like this:
[page mypage] template = ...
To instruct the framework that this page is the one to be sent, simply use a statement like this:
cgi->SendPage("mypage");
The text taken from the respective template
parameter will be
passed through the macroprocessor, and the result will be sent to the
client with the “success” code (200
).
In case something went wrong, you might want to call the
SendErrorPage
instead of SendPage
, like this:
cgi->SendErrorPage(404, "page not found");
or even
cgi->SendErrorPage(418, "i'm a teapot");
(to have an idea what's the matter with teapots here, please read this Wikipedia article).
The page sent to the client will be generated using the
[errorpage]
section of the
configuration file.
Please note that, although the framework really tries to set the error code
in the Status
field of the response, in most cases the HTTP
server has its own idea what to send to the client in that field.
The last possibility is to request the client to redirect the user to some other place. It is done like this:
cgi->SendRedirect("http://www.example.com/place/to/go");
Together with the redirect response, a “backup” page is usually
sent, which informs the user what's going on and provides a link to click
in case something went wrong with the browser's ablility to redirect
authomatically. The dullcgi framework generates a page for this purpose
using the [redirectpage]
section.