<!doctype linuxdoc system>

<article>

<!-- Title Information -->

<title>How to add support for Apache modules under Comanche
<author>Daniel Lopez Ridruejo, <tt/ridruejo@apache.org/
<date>v0.1, 1 March 2000

<!-- Abstract -->
<abstract>
This paper is a work in progress that documents the steps necessary to add support for 
configuring additional Apache modules with Comanche
</abstract> 


<!-- Table of Contents -->
<toc>


<sect> Introduction     

<p>

This documents gives detailed information of the steps involved in adding
support for a new Apache module in Comanche (like PHP3, mod_ldap, etc.) or
adding support for new directives.<p>
Comanche is a framework to build management modules (plugins). There are
plugins for Apache, Samba, etc.  Since Apache is a complex program the
plugin for Apache itself is modular and support for directives and modules is done via
an extension mechanism. <p>
The process is relatively simple. For most modules, you only have to write a few XML files
following certain simple rules.

<sect> What the Apache plugin needs to know from you
<p>

In order to add support for your module or directive the Apache plugin needs
to know a few things. 

<itemize>
<item>Where is your module located? :
At plugins/apache/modules you can find current supported modules each one of
them has a subdirectory which contains the necessary files
<item>Which files are necessary to support your module? :
<itemize>
<item>moduleDescription.xml:  Describes the module and gives pointers for the other files
<item>directives.xml: Describes the directives that we are configuring 
<item>propertyPages.xml: Arrange the directives for presentation to the user
<item>specialCases.tcl: For parsing complex directives back and forth between the
XML format and httpd.conf format
</itemize>
</itemize>


<sect1> moduleDescription.xml
<p>
<itemize>
<item>Description of module: name, where to find directive and property pages
definition, special cases definition and description of the module
<item>Nodes in which we are interested: Which nodes (main server, virtual
hosts, etc) we want our property pages to appear in
<item>Description of special cases: Apache uses httpd.conf format and Comanche
uses XML format. If the directives are simple, the mapping will be
automagically done by Comanche, if not we specify the name of the procedures
here
</itemize>

<sect1> specialCases.tcl
<p>
Actual Tcl code used to convert from XML format to httpd.conf

<sect1>directives.xml
<p>
Description of the directives that belong to this module

<sect1>propertyPages.xml

<p>Description of the property pages for this modules. They are presented to
the user during configuration. They arrange the directives on the screen
logically. It is possible to have different versions for beginners and for
advanced users.


<sect1>messages
<p>
In the subdirectory messages/ the texts corresponding to the messages are
stored there so they can easily be translated by other people. The messages
are stored with a suffix corresponding to the language: messages.en, messages.es, etc.


<sect> Step by step guide to create a module from scratch
<p>

The following is a detailed description of the necessary steps to add
support for a module. We will add support for the PHP scripting language
(<url name="http://www.php.net" url="http://www.php.net">). 


<sect1>Planning
<p>
We take a look at the configuration information for PHP:
<url name="http://www.php.net/manual/configuration.php3" url="http://www.php.net/manual/configuration.php3">
<p>
As it turns out PHP can be configured in different ways, either inside the
Apache httpd.conf file or in a separate file. The syntax varies too from
PHP3 to PHP4. Since this is just an example, we will configure PHP3 for
httpd.conf syntax. The user interface will remain unchanged among all of
them, so to support the other versions we just need to change the
translating functions since as can reuse the directive and property pages
definitions.
<p>
One thing to remember is that we do not need to support all directives at
once. Directives not known to Comanche are safely ignored and restored when
writing the output configuration file. We can provide support for the most
commonly used directives and then go for the more obscure parameters.
<p>
Before doing any coding we need to plan what the user interface is going to
look like. We plan on having the following property pages:
<p>
<verb>
PHP3   (Enable/disable and tags)
  |
  \__ Resources (Limit use if resources CPU/Time)
  |
  \__ Error Handling (Error logs, display, etc.)
  |
  \__ Data Handling (POST variables, prepend, autopend...)
  |
  \__ Paths (auto include directories)
</verb>  
<sect1>Directive definition
<p>
Next step is to actually define the directives. The XML language used here
is described in the programmer guide. It is really easy so you probably can
pick it up just by taking a look at how the other modules are described.
Most directives have a one to one correspondence, others will need more
elaborated XML directives.

<sect2>asp_tags
<p>
In the docs we find

<verb>
asp_tags boolean

Enables the use of ASP-like <% %> tags in addition to the
usual <?php ?> tags. This includes the variable-value printing shorthand of
<%=$value %>. For more information, see Escaping from HTML.
</verb>
<p>
It is clear that this maps directly into the XML boolean type so we write:
<verb>
<boolean name="php3_asp_tags" label="php_asp_tags">
    <default>0 &etago;default>
 &etago;boolean>       
</verb>
<p>
Name is php3_asp_tags because this is how it will appear on Apache config
files. This way Apache already nows which httpd.conf directives match to
which XML directives.
<p>
Instead of writting the description of the directive directly on the label
we store the description in the messages/ subdirectory. The contents of the
messages.en file:
<verb>
php_asp_tags {Enable ASP (<% %>) style tags?}
</verb>  
If we want an additional Spanish version, we write (lenguage can be later on
selected by the user) 
messages.es:
<verb>
php_asp_tags {Permitir tags como las de ASP (<% %>)?}
</verb>

<sect2>max_execution_time
<p>
This one, which accepts a number as an argument gets turned into
<verb>
<number name="php3_max_execution_time" label="php_max_execution_time">
<default>30 &etago;default>
 &etago;number>         
</verb>
with messages.en
<verb>php_max_execution_time {Limit in execution time (seconds)}  </verb>


<sect2>doc_root
<p>
This one sets the document root for safe mode. This is a string, more
precisely a directory, so the corresponding entry:
<verb>
<string name="php3_doc_root" label="php_doc_root" classes="directory" >
<default> &etago;default>
 &etago;string>
</verb>
(note the classes attribute, it hints the gui to provide a directory selector
button and to write back quoted directories with blank spaces)

with messages.en:
<p>
php_doc_root {PHP root directory (for safe mode)}   
</p>

<sect2>gpc_order
<p>
This one could have been modeled in a different way, but this ones works
just fine at it is clear
<verb>
<choice name="php3_gpc_order" label="php_gpc_order">
    <syntax>
        <option name="GPC" value="GET/POST/COOKIE" />
        <option name="GCP" value="GET/COOKIE/POST" />
        <option name="PGC" value="POST/GET/COOKIE" />
        <option name="PCG" value="POST/COOKIE/GET" />
        <option name="CPG" value="COOKIE/POST/GET" />
        <option name="CGP" value="COOKIE/GET/POST" />
        <option name="GP" value="GET/POST" />
        <option name="GC" value="GET/COOKIE" />
        <option name="PG" value="POST/GET" />
        <option name="PC" value="POST/COOKIE" />
        <option name="CP" value="COOKIE/POST" />
        <option name="CG" value="COOKIE/GET" />
        <option name="G" value="GET" />
        <option name="P" value="POST" />
        <option name="C" value="COOKIE" />
     &etago;syntax>
    <default>GPC &etago;default>
 &etago;choice>     
</verb>
messages.en:
<verb>
php_gpc_order {Order of GET/POST/COOKIE variable parsing}  
</verb>
So far the mapping between httpd.conf and XML directives has been pretty
straightforward. For the previous entries the conversion is handled
automatically by Comanche and we do not need to do anything else!
<p>
Now comes the interesting part. It requires a little bit more of work but it
pays off in a more easy to use interface for the user. There are some
directives that could be better expressed in some other way.
Take for example the directive auto_append_file (File to always parse
after processing the main file). It can have a special value of 'none' to
disable this feature. Instead of mapping it to a string and have the user
know that piece of information we make it a little bit easier and provide
a pair of radiobuttons. First one is simply 'none' the other is the name of
the file. 
<p> 
 This, translated to XML gets us:
<verb> 
 <alternate name="php3_auto_append_file" label="php_auto_append_file"
            style="normal">
    <syntax>
        <label name="none" label="php_auto_append_file_none" />
        <string name="file" label="php_auto_append_file_file" classes="file" />
     &etago;syntax>
    <default>none &etago;default>
 &etago;alternate>     
</verb>    
 You get the idea. By default the 'none' element is selected.
 
We need a way to convert from httpd.conf to this XML format and the other
way around. This involves a little bit of programming, which is shown below
<verb>
# This procedure is called whenever the php3_auto_append_file is encountered
# in httpd.conf
 
proc ::apache1.3::parse_php3_auto_append_file {text parser dirDef xmlConf currentContainer} {

    # Create a new instance of this directive or overwrite the existing one
    
    set xuiObj [apacheparserutils::getOrCreateIfNotExists php3_auto_append_file \
            $dirDef $xmlConf $currentContainer]
	    
    # Depending on the value (none or a filename), select the appropriate component and store the value
	    
    set value [lindex $text 1]
    switch [string tolower $value] {
       none {
          $xuiObj selectComponentByName none
       } default {
          $xuiObj selectComponentByName file
          $xuiObj.file setValue $value
       }
    }
}
 
 
# Procedure called to dump the contents to httpd.conf

proc ::apache1.3::dump_php3_auto_append_file {xuiObj} {
    set value [[$xuiObj getSelectedComponent] getValue]
    return "php3_auto_append_file $value" 
}
</verb> 
Similar goes for php3_auto_prepend directive or php3_error_log


<sect2>php3_include_path
<p>
This is another directive that would benefit of special treatment is
php3_include_path (and php3_open_basedir)

From the PHP docs:
<verb>
include_path string

Specifies a list of directories where the require(), include() and
fopen_with_path() functions look for files. The format is like the system's 

PATH environment variable: a list of directories separated with a colon in
UNIX or semicolon in Windows. 

Example 3-1. UNIX include_path

include_path=.:/home/httpd/php-lib


Example 3-2. Windows include_path

include_path=".;c:\www\phplib"

The default value for this directive is . (only the current directory).
</verb>
 Now we could map this directive to a string directive and hvae the user
enter a string of directories separated by : or ;  Instead we make it a
little bit easier for them by abstracting it and presenting them with a list
of directories.
<p>
The XML description:
<verb>
<list name="php3_include_path" label="php_include_path">
    <syntax>
        <string name="php3_include_path_file" label="php_include_path_file" classes="directory" />
     &etago;syntax>
    <default>
    <item>. &etago;item>
     &etago;default>
 &etago;list>
</verb>
We need now a couple of functions for converting from : or ; format
(httpd.conf) to XML and viceversa.
<verb>
proc ::apache1.3::parse_php3_include_path {text parser dirDef xmlConf
currentContainer} {
    set xuiObj [apacheparserutils::getOrCreateIfNotExists php3_include_path \
            $dirDef $xmlConf $currentContainer]
    global tcl_platform
    switch $tcl_platform(platform) {
       windows {
           set separator {;}
       } default {
           set separator :
       }
    }
    foreach element [split [lrange $text 1 end] $separator] {
        set child [$xuiObj newChild]
 
        # Split fscks up quoted filenames, so this is required
 
        regsub {\\\"} $element {"} result
        $child setValue $result
        $xuiObj insertChild $child
    }
}
 
 
proc ::apache1.3::dump_php3_include_path {xuiObj} {
   global tcl_platform
   switch $tcl_platform(platform) {
       windows {
           set separator {;}
       } default {
           set separator :
       }
    }
    set values {}
    foreach child [$xuiObj getChildren] {
        lappend values [$child getValue]
    }
    set result [join $values $separator]
    if [llength $result] {
        return "php3_include_path $result"
    }
    return {}
}   
</verb>

Once we have described all the directives (take a look at
plugins/apache/modules/php3/directives.xml for a complete listing)
We need to tell Comanche how do we want them organized in property pages
(like in the tree described at the beginning)

In plugins/apache/modules/php3/propertyPages.xml

As an example, the main page:
<verb>
<propertyPage label="php3_pp_phpEngine" icon="smallWheel" name="pp_phpEngine" align="vertical">
        <directiveInclude name="php3_engine"/>
        <group name="php3_pp_phpEngine_tags"align="vertical"label="php3_pp_phpEngine_tags" style="normal">
            <directiveInclude name="php3_asp_tags"/>
            <directiveInclude name="php3_short_open_tag"/>
         &etago;group>
 &etago;propertyPage>   


Finally, the moduleDescription.xml puts everything together:

<apacheModuleDescription name="php3"
                         directivesXMLDefinition="directives.xml"
                         propertyPagesXMLDefinition="propertyPages.xml"
                         description="PHP3 Apache module." >
    <nodesInterested>
        <node type="mainserver,virtualhost">
             <propertyPage name="pp_phpEngine"  />

        [...]

             <propertyPage name="pp_phpPaths" hookUnder="pp_phpEngine" />
         &etago;node>
     &etago;nodesInterested>
 <specialCases file="specialCases.tcl">
        <specialCase confDir="php3_open_basedir"
                     xmlDir="php3_open_basedir"
                     parser="apache1.3::parse_php3_open_basedir"
                     dumper="apache1.3::dump_php3_open_basedir" />
	 
	 [...]

 &etago;specialCases>
 &etago;apacheModuleDescription>    
</verb>
</article>