A CGI KioSlave Tutorial for KDE

What KDE help (in version 3.5.3) has to say about CGI is just:

The CGI slave provides a way to execute CGI programs without the need to have a running web server. This can for example be used for local testing of CGI programs or for using search engines that only provide a CGI frontend like the one from Doxygen.

The slave implements the cgi: protocol. It uses the filename from the given URL and searches a configurable list of folders. If it finds an executable with the given name it executes it, passes the arguments of the URL and sets the environment variables needed by CGI programs.

At this place some hopefully more useful information for power users and programmers will be given. The text was submitted to the KDE project (and can be used by KDE under whatever license they want) to improve their documentation.

Please note

The CGI KioSlave needs to know where to find the script (or program) to be executed. It searches in special folders that can be configured using the kcontrol application (search for CGI). Alternatively you might press Alt+F2 and type in kcmshell kcmcgi. Add the folder where you keep your script (or program) to the list.

Another important point is that your script must be executable. Type something like chmod 755 myscript to be sure.

Examples

Three examples using bash scripting follow next to give you a starting point and to demonstrate the power of the CGI KioSlave. Combining Bash (or another scripting language) with HTML (eventually using CSS and Javascript) will allow you to write powerful GUI applications without any C++ programming!

Example 1

This is the minimal CGI hello world program. The important line of code is here the Content-Type: text/html thing that instructs Konqueror to render the output of the script as HTML.

 #!/usr/bin/env bash
 # The HTTP header followed by an empty line:
 echo -e "Content-Type: text/html\n"
 # The content:
 echo "<html><h1>Hello World!</h1></html>"

To make it work create a folder, save the above example as ``demo1'' and type in a console window:

 chmod 775 demo1
 kwriteconfig --file kcmcgirc --group General --key Paths $PWD
 konqueror cgi:demo1

Beware: The way kwriteconfig is used above would clear all other registered CGI paths of the current user. Consider 1st reading the Paths key with kreadconfig and then merging it with the new path. See the 3rd example on how to implement this in code.

That the 1st line of the script just contains #!/usr/bin/env bash (starting at column 1) is a little trick that allows us to run the bash shell without knowing it's path (on non-Linux systems a simple #!/bin/bash won't work). The later examples use bash features and may therefore not work with other shells.

Example 2

The second example is a bit more complex and demonstrates four features (watch the script comments for the numbers):

(1)

Finding out if a script is called via CGI or from the command line

(2)

Checking if the script got input from an HTML Form

(3)

Converting the url encoded form input to text in bash variables

(4)

Escaping & and < in output to become HTML compatible

The example itself displays two pages, one prompts for input and the other shows a result. The result page echoes the text from the input page and can optionally display the environment variables of the executing shell.

 #!/usr/bin/env bash
 # function called when run from command line ...
 commandline_call() {
    echo "Please open Konqueror and enter 'cgi:${0##*/}' as url"
    exit 1
 }
 # function to format html content - input form
 input_page() {
    cat <<EOF
    <h2>An Input Form</h2>
    <form action="cgi:${0##*/}" method="GET">
    <table><tr>
       <td align=right>Display environment settings:</td>
       <td><input name="chkBox1" type="checkbox" value="x"></td>
    </tr><tr>
       <td align=right>Type some text please:</td>
       <td><input name="txtText1"></td>
    </tr><tr>
       <td colspan="2" align=center>
       <INPUT type="reset"> <INPUT type="submit"></td>
    </tr></table>
    </form>
 EOF
 }
 # Function to format html content - result page
 output_page() {
    # (3) convert url encoded query string to bash variables...
    set x ${QUERY_STRING//&/ }
    while [ $# -gt 1 ] ; do
       local nam="${2%%=*}" ; val="${2#*=}" ; shift
       local val="${val//\%5C/ }" ; val="${val//+/ }"
       read $nam <<<"$(printf "${val//\%/\x}")"
    done
    # (4) prepare html output: '&' -> '&amp;' and '<' -> '&lt'
    local ltxt="${txtText1//&/&amp;}" ; ltxt="${ltxt//</&lt;}"
    # now create the output...
    echo "<h2>This is the Output Page</h2>"
    echo "<h3>Here comes the echo text: $ltxt</h3>"
    [ -n "$chkBox1" ] &&
       echo "<h3>The environment is:</h3><pre>$(set)</pre>"
 }
 # (1) Are we called from the command line?
 [ -z "$REQUEST_METHOD" ] && commandline_call
 # (2) Called via cgi - What action to take?
 echo -e "Content-Type: text/html\n"
 echo "<html><title>This is CGI Demo2</title>"
 [ -z "$QUERY_STRING" ] && input_page || output_page
 echo "<div style='margin-top: 2cm'>This is my footer</div></html>"

Here some details about the before-mentioned features that got demonstrated by the previous example:

Finding out if a script is called via CGI or from the command line

Use the REQUEST_METHOD variable - when called via CGI the content should be ``POST'' or ``GET''. Otherwise the script was called as a command.

Checking if the script got input from an HTML Form

Here the ``QUERY_STRING'' variable is used. When a form uses the ``GET'' method the input becomes encoded in a special way and is appended to the url. Spaces in the input are replaced by + and multiple parameters are separated by &. Most special characters get encoded by their hexadecimal value.

Converting the url encoded form input to text in bash variables

It takes a bit of bash woo-do to convert url parameters back to normal text. The code in the example uses ``printf'' to convert back from hexadecimal and ``read'' to set a local bash variable with the name of the input field to the field's value. So if your input field was named ``txtHello'' the result is a variable of that name containing what the user typed into the form.

Escaping & and < in output to become HTML compatible

When you send back HTML encoded output to Konqueror you have to maintain syntactical correctness. At least you should replace the & and < characters by HTML entities, which is what the example does.

Did you notice that this conversion is missing from the ``set'' command output? So you got a buggy example! Can you fix it as an exercise?

Example 3

This adds the following features to your code (watch the script comments for the numbers):

(1)

Command line options like --help.

(2)

Automatic registration of the CGI folder (see --setup and --nocheck).

(3)

Automatic launching of Konqueror including a profile (see --profile and --query).

(4)

A configuration file (but only the CSS styles are used in the example).

The listing omits the functions input_page and output_page which you might copy from demo2.

 #!/usr/bin/env bash
 declare this="${0##*/}"
 # (1) implementing the help option ...
 function show_help()
 {  cat <<EOF
    This is the demo3 application.
    usage:  $this --setup
            $this [--nocheck] [--profile name] [--query urlargs]
    options: --setup    do the KDE setup for this user and quit
             --nocheck  do not check if --setup was run before
             --profile  Konqueror profile, use '' to override '$this'
             --query    url-encoded query arguments passed to Konqueror
             --help     show this text and quit
 EOF
    exit 1
 }
 # (2) automatic CGI setup ...
 function run_setup()
 {  local paths="$(kreadconfig --file kcmcgirc --group General --key Paths)"
    local instd="$(readlink -f "$0")" ; instd="${instd%/*}"
    if ! grep -q -E "(^|,)${instd}/?(\$|,)" <<<$paths ; then
       echo "$this: user $USER: CGI config updated to include: $instd"
       [ -n "$paths" ] && instd="$paths,$instd"
       kwriteconfig --file kcmcgirc --group General --key Paths "$instd"
    fi
    [ -n "$1" ] && exit 0
 }
 # (1) implementing command line options ...
 function commandline_call()
 {  local pdir="$HOME/.kde/share/apps/konqueror/profiles"
    [ -r "$pdir/$this" ] && prof="$this"
    while [ $# -gt 0 ] ; do case $1 in
       -pro*|--pro*) prof="$2"; shift ;;
       -que*|--que*) uarg="?$2"; shift ;;
       -noc*|--noc*) fnoc="x" ;;
       -set*|--set*) fset="x" ;;
       -h*|--h*)     show_help ;;
       *)            echo "$this: unknown option - try '--help'"
                     exit 2 ;;
    esac ; shift ; done
    [ -z "$fnoc" -o -n "$fset" ] && run_setup "$fset"
    [ -z "$prof" ] && exec konqueror "cgi:${this}$uarg"
    exec konqueror --profile $prof "cgi:${this}$uarg"
 }
 # the input_page and output_page functions were omitted here,
 # please copy them from example 2!
 # (3) launch konqueror if not called as cgi script...
 [ -z "$REQUEST_METHOD" ] && commandline_call $*
 # (4) set defaults and read the configuration file
 CHK_X1="checked"
 CHK_X2=
 TXT_T1="10000"
 TXT_T2="$HOSTNAME"
 if   [ -r "$HOME/${this}.conf" ] ; then . $HOME/${this}.conf
 elif [ -r "/etc/${this}.conf" ]  ; then . /etc/${this}.conf
 fi
 echo -e "Content-Type: text/html\n"
 echo "<html><title>This is CGI Demo3</title>"
 echo "<body><style>$STYLE</style>"
 [ -z "$QUERY_STRING" ] && input_page || output_page
 echo "<div style='margin-top: 2cm'>This is my footer</div></html>"

The following is an example of a configuration file (which could be installed either in your home folder or as /etc/demo3.conf):

 # These are css styles used to decorate the generated HTML ...
 STYLE="
 H2 { font-weight: bold; color: blue; background-color:
      rgb(220,220,220); border: 1px solid rgb(128,128,128); padding: 2pt }
 H3 { color: black; blue; background-color:
      rgb(220,220,220); border: 1px solid rgb(128,128,128); padding: 2pt }
 "
 # The following defines can be used to set change defaults ...
 # CHK_X1=
 # CHK_X2="checked"
 # TXT_T1="2000"
 # TXT_T2=
 # end

As you see the configuration file is read by the shell (hackers say it gets sourced). The advantage of such an implementation is that you almost don't need to write any code. On the other hand the disadvantage is that users must follow bash syntax in such config files and might inject code into your scripts. Debian users might compare this to /etc/defaults.

Bugs and Limitations

-

Unfortunately KDE prior to version 3.5.4 contained a bug that occasionally inserted extra spaces in the HTML code that gets sent back to Konqueror. Depending on the output this sometimes corrupted the HTML rendering.

-

Some 7 billion people on the planet will have to wait for KDE 4 to let CGI KioSlave accept utf-8 or other specific character encodings. Up to KDE version 3.5.x a fixed build-in encoder for ``latin1'' is used. In later versions of KDE the charset keyword in the HTTP header will be used to select an encoder. Example:

echo -e "Content-Type: text/html; charset: utf-8\n"

References

KDE KioSlave:

http://de.wikipedia.org/wiki/KDE_Input/Output (deutsch)

CGI (Common Gateway Interface) in wikipedia:

http://en.wikipedia.org/wiki/Common_Gateway_Interface (english)

http://de.wikipedia.org/wiki/Common_Gateway_Interface (deutsch)

A Well Known HTML/CSS Tutorial:

http://www.selfhtml.org/ (multilingual)

A Sample Application (Log File Viewer):

http://www.j-pfennig.de/LinuxImHaus/SyslogEinrichtenUndAnsehen.html (english/deutsch)