From e2d6ed954b42c72645aeb2476591e6d42cf48991 Mon Sep 17 00:00:00 2001
From: tbrehm <t.brehm@ispconfig.org>
Date: Fri, 29 Jun 2007 14:32:16 +0000
Subject: [PATCH] - added basic update script - improved several functions in
 site module - added server classes to restart services

---
 install/install.php                           |   24 +-
 install/lib/installer_base.lib.php            |    2 +
 install/sql/ispconfig3.sql                    |    2 +
 install/update.php                            |  155 ++
 interface/lib/classes/tform.inc.php           |   51 +-
 interface/lib/classes/tform_actions.inc.php   |   39 +-
 interface/web/sites/form/ftp_user.tform.php   |   13 +-
 .../web/sites/templates/ftp_user_advanced.htm |    4 +-
 interface/web/sites/web_domain_edit.php       |    8 +-
 server/conf/vhost.conf.master                 |   39 +-
 server/lib/classes/file.inc.php               |  319 ++++
 server/lib/classes/services.inc.php           |   79 +
 server/lib/classes/system.inc.php             |  999 +++++++++++
 server/lib/classes/tpl.inc.php                | 1499 +++++++++++++++++
 server/lib/classes/tpl_cache.inc.php          |  186 ++
 server/lib/classes/tpl_error.inc.php          |   92 +
 server/lib/classes/tpl_ini.inc.php            |  116 ++
 server/mods-enabled/web_module.inc.php        |   17 +-
 server/plugins-enabled/apache2_plugin.inc.php |   93 +-
 server/server.php                             |   11 +-
 20 files changed, 3676 insertions(+), 72 deletions(-)
 create mode 100644 install/update.php
 create mode 100644 server/lib/classes/file.inc.php
 create mode 100644 server/lib/classes/services.inc.php
 create mode 100644 server/lib/classes/system.inc.php
 create mode 100644 server/lib/classes/tpl.inc.php
 create mode 100644 server/lib/classes/tpl_cache.inc.php
 create mode 100644 server/lib/classes/tpl_error.inc.php
 create mode 100644 server/lib/classes/tpl_ini.inc.php

diff --git a/install/install.php b/install/install.php
index 140db21a9..05fba1fd3 100644
--- a/install/install.php
+++ b/install/install.php
@@ -115,17 +115,19 @@ calmd
 spamd
 */
 
-exec("/etc/init.d/mysql restart");
-exec("/etc/init.d/postfix restart");
-exec("/etc/init.d/saslauthd restart");
-exec("/etc/init.d/amavis restart");
-exec("/etc/init.d/clamav-daemon restart");
-exec("/etc/init.d/courier-authdaemon restart");
-exec("/etc/init.d/courier-imap restart");
-exec("/etc/init.d/courier-imap-ssl restart");
-exec("/etc/init.d/courier-pop restart");
-exec("/etc/init.d/courier-pop-ssl restart");
-exec("/etc/init.d/apache2 restart");
+swriteln('Restarting services ...');
+system("/etc/init.d/mysql restart");
+system("/etc/init.d/postfix restart");
+system("/etc/init.d/saslauthd restart");
+system("/etc/init.d/amavis restart");
+system("/etc/init.d/clamav-daemon restart");
+system("/etc/init.d/courier-authdaemon restart");
+system("/etc/init.d/courier-imap restart");
+system("/etc/init.d/courier-imap-ssl restart");
+system("/etc/init.d/courier-pop restart");
+system("/etc/init.d/courier-pop-ssl restart");
+system("/etc/init.d/apache2 restart");
+system("/etc/init.d/pure-ftpd-mysql restart");
 
 echo "Installation finished.\n";
 
diff --git a/install/lib/installer_base.lib.php b/install/lib/installer_base.lib.php
index 00f60cb9a..f97c4b442 100644
--- a/install/lib/installer_base.lib.php
+++ b/install/lib/installer_base.lib.php
@@ -467,6 +467,8 @@ maildrop  unix  -       n       n       -       -       pipe
 		wf($conf["dist_pureftpd_config_dir"].'/'.$configfile,$content);
 		exec('chmod 600 '.$conf["dist_pureftpd_config_dir"].'/'.$configfile);
 		exec('chown root:root '.$conf["dist_pureftpd_config_dir"].'/'.$configfile);
+		// enable chrooting
+		exec('echo "yes" > /etc/pure-ftpd/conf/ChrootEveryone');
 	
 	}
 	
diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql
index c80510db3..1e552beee 100644
--- a/install/sql/ispconfig3.sql
+++ b/install/sql/ispconfig3.sql
@@ -404,6 +404,8 @@ CREATE TABLE `server` (
 -- Daten für Tabelle `server`
 -- 
 
+INSERT INTO `server` (`server_id`, `sys_userid`, `sys_groupid`, `sys_perm_user`, `sys_perm_group`, `sys_perm_other`, `server_name`, `mail_server`, `web_server`, `dns_server`, `file_server`, `db_server`, `vserver_server`, `config`, `updated`, `active`) VALUES (1, 1, 1, 'riud', 'riud', 'r', 'Server 1', 1, 1, 1, 1, 1, 1, '[global]\nwebserver=apache\nmailserver=postfix\ndnsserver=mydns\n\n[mail]\nmodule=postfix_mysql\nmaildir_path=/home/vmail/[domain]/[localpart]/\nhomedir_path=/home/vmail/\nmailuser_uid=5000\nmailuser_gid=5000\nmailuser_name=vmail\nmailuser_group=vmail\n\n[getmail]\ngetmail_config_dir=/etc/getmail\n\n[web]\nwebsite_path=/var/clients/client[client_id]/web[website_id]\nwebsite_symlinks=/var/www/[website_domain]/:/var/clients/client[client_id]/[website_domain]/\n\n', 0, 1);
+
 -- --------------------------------------------------------
 
 -- 
diff --git a/install/update.php b/install/update.php
new file mode 100644
index 000000000..0dc79198c
--- /dev/null
+++ b/install/update.php
@@ -0,0 +1,155 @@
+<?php
+
+/*
+Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of ISPConfig nor the names of its contributors
+      may be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+	ISPConfig 3 installer.
+*/
+
+// Include the library with the basic installer functions
+require_once('lib/install.lib.php');
+
+// Include the base class of the installer class
+require_once('lib/installer_base.lib.php');
+
+$distname = get_distname();
+
+include_once("/usr/local/ispconfig/server/lib/config.inc.php");
+$conf_old = $conf;
+unset $conf;
+
+// Include the distribution specific installer class library
+// and configuration
+include_once('dist/lib/'.$distname.'.lib.php');
+include_once('dist/conf/'.$distname.'.conf.php');
+
+// Set the mysql login information
+$conf["mysql_server_host"] = $conf_old["db_host"];
+$conf["mysql_server_database"] = $conf_old["db_database"];
+$conf["mysql_server_ispconfig_user"] = $conf_old["db_user"];
+$conf["mysql_server_ispconfig_password"] = $conf_old["db_password"];
+
+$inst = new installer();
+
+
+echo "This application will update ISPConfig 3 on your server.\n";
+
+// $conf["language"] = $inst->request_language();
+
+// TODO: all other queries, for testing I will setup everything in $conf
+
+// Initialize the MySQL server connection
+include_once('lib/mysql.lib.php');
+$inst->db = new db();
+
+// Database update is a bit brute force and should be rebuild later ;)
+
+// export the current database data
+exec("mysqldump -h $conf[mysql_server_host] -u $conf[mysql_server_ispconfig_user] -p$conf[mysql_server_ispconfig_password] -c -t --add-drop-table --add-locks --all --quick --lock-tables $conf[mysql_server_database] > existing_db.sql  &> /dev/null");
+
+// Delete the old database
+exec("/etc/init.d/mysql stop");
+exec("rm -rf /var/lib/mysql/".$conf["db_database"]);
+exec("/etc/init.d/mysql start");
+
+// Create the mysql database
+$inst->configure_database();
+
+// empty all databases
+$db_tables = $inst->db->getTables();
+foreach($db_tables as $table) {
+	$inst->db->query("TRUNCATE $table");
+}
+
+// load old data back into database
+exec("mysql -h $conf[mysql_server_host] -u $conf[mysql_server_ispconfig_user] -p$conf[mysql_server_ispconfig_password] $conf[mysql_server_database] < existing_db.sql  &> /dev/null");
+
+// Configure postfix
+$inst->configure_postfix();
+
+// Configure saslauthd
+swriteln('Configuring SASL');
+$inst->configure_saslauthd();
+
+
+// Configure PAM
+swriteln('Configuring PAM');
+$inst->configure_pam();
+
+// Configure courier
+swriteln('Configuring Courier');
+$inst->configure_courier();
+
+// Configure Spamasassin
+swriteln('Configuring Spamassassin');
+$inst->configure_spamassassin();
+
+// Configure Amavis
+swriteln('Configuring Amavisd');
+$inst->configure_amavis();
+
+// Configure Getmail
+swriteln('Configuring Getmail');
+$inst->configure_getmail();
+
+// Configure Getmail
+swriteln('Configuring Pureftpd');
+$inst->configure_pureftpd();
+
+// Configure ISPConfig
+swriteln('Installing ISPConfig');
+$inst->install_ispconfig();
+
+// Configure ISPConfig
+swriteln('Installing Crontab');
+$inst->install_crontab();
+
+
+/*
+Restart services:
+*/
+
+swriteln('Restarting services ...');
+system("/etc/init.d/mysql restart");
+system("/etc/init.d/postfix restart");
+system("/etc/init.d/saslauthd restart");
+system("/etc/init.d/amavis restart");
+system("/etc/init.d/clamav-daemon restart");
+system("/etc/init.d/courier-authdaemon restart");
+system("/etc/init.d/courier-imap restart");
+system("/etc/init.d/courier-imap-ssl restart");
+system("/etc/init.d/courier-pop restart");
+system("/etc/init.d/courier-pop-ssl restart");
+system("/etc/init.d/apache2 restart");
+system("/etc/init.d/pure-ftpd-mysql restart");
+
+echo "Update finished.\n";
+
+
+?>
\ No newline at end of file
diff --git a/interface/lib/classes/tform.inc.php b/interface/lib/classes/tform.inc.php
index 81810b9cf..e631e2309 100644
--- a/interface/lib/classes/tform.inc.php
+++ b/interface/lib/classes/tform.inc.php
@@ -766,9 +766,7 @@ class tform {
                                 $app->error("Primary ID fehlt!");
                         }
                 }
-
-                // Daten in History tabelle speichern
-                if($this->errorMessage == '' and $this->formDef['db_history'] == 'yes') $this->datalogSave($action,$primary_id,$record);
+                
                 return $sql;
         }
 
@@ -839,11 +837,16 @@ class tform {
                 // Set Wordbook for this form
 
                 $app->tpl->setVar($this->wordbook);
-    }
-
+    	}
 
+		function getDataRecord($primary_id) {
+			global $app;
+			$sql = "SELECT * FROM ".$escape.$this->formDef['db_table'].$escape." WHERE ".$this->formDef['db_table_idx']." = ".$primary_id;
+            return $app->db->queryOneRecord($sql);
+		}
+		
 
-        function datalogSave($action,$primary_id,$record_new) {
+        function datalogSave($action,$primary_id, $record_old, $record_new) {
                 global $app,$conf;
 
                 // Füge Backticks nur bei unvollständigen Tabellennamen ein
@@ -852,17 +855,19 @@ class tform {
                 } else {
                         $escape = '`';
                 }
-
-                if($action == "UPDATE") {
+				
+				/*
+                if($action == "UPDATE" or $action == "DELETE") {
                         $sql = "SELECT * FROM ".$escape.$this->formDef['db_table'].$escape." WHERE ".$this->formDef['db_table_idx']." = ".$primary_id;
                         $record_old = $app->db->queryOneRecord($sql);
                 } else {
                         $record_old = array();
                 }
+				*/
 
                 $diffrec = array();
 				
-                if(is_array($record_new)) {
+                if(is_array($record_new) && count($record_new) > 0) {
                         foreach($record_new as $key => $val) {
                                 if($record_old[$key] != $val) {
 										// Record has changed
@@ -870,13 +875,22 @@ class tform {
                                                                'new' => $val);
                                 }
                         }
+                } elseif(is_array($record_old)) {
+                        foreach($record_old as $key => $val) {
+                                if($record_new[$key] != $val) {
+										// Record has changed
+                                        $diffrec[$key] = array('new' => $record_new[$key],
+                                                               'old' => $val);
+                                }
+                        }
                 }
 				$this->diffrec = $diffrec;
 				
+				
 				// Full diff records for ISPConfig, they have a different format then the simple diffrec
 				$diffrec_full = array();
 
-                if(is_array($record_old)) {
+                if(is_array($record_old) && count($record_old) > 0) {
                         foreach($record_old as $key => $val) {
                                 if(isset($record_new[$key]) && $record_new[$key] != $val) {
                                     // Record has changed
@@ -887,6 +901,17 @@ class tform {
 									$diffrec_full['new'][$key] = $val;
 								}
                         }
+                } elseif(is_array($record_new)) {
+                        foreach($record_new as $key => $val) {
+                                if(isset($record_new[$key]) && $record_old[$key] != $val) {
+                                    // Record has changed
+									$diffrec_full['new'][$key] = $val;
+									$diffrec_full['old'][$key] = $record_old[$key];
+                                } else {
+									$diffrec_full['new'][$key] = $val;
+									$diffrec_full['old'][$key] = $val;
+								}
+                        }
                 }
 				
 				/*
@@ -903,7 +928,11 @@ class tform {
 						$diffstr = $app->db->quote(serialize($diffrec_full));
                         $username = $app->db->quote($_SESSION["s"]["user"]["username"]);
                         $dbidx = $this->formDef['db_table_idx'].":".$primary_id;
-                        $action = ($action == 'INSERT')?'i':'u';
+                        // $action = ($action == 'INSERT')?'i':'u';
+						
+						if($action == 'INSERT') $action = 'i';
+						if($action == 'UPDATE') $action = 'u';
+						if($action == 'DELETE') $action = 'd';
                         $sql = "INSERT INTO sys_datalog (dbtable,dbidx,server_id,action,tstamp,user,data) VALUES ('".$this->formDef['db_table']."','$dbidx','$server_id','$action','".time()."','$username','$diffstr')";
 						$app->db->query($sql);
                 }
diff --git a/interface/lib/classes/tform_actions.inc.php b/interface/lib/classes/tform_actions.inc.php
index f228e5414..89d974abd 100644
--- a/interface/lib/classes/tform_actions.inc.php
+++ b/interface/lib/classes/tform_actions.inc.php
@@ -102,9 +102,13 @@ class tform_actions {
                 $ext_where = '';
                 $sql = $app->tform->getSQL($this->dataRecord,$app->tform->getCurrentTab(),'UPDATE',$this->id,$ext_where);
                 if($app->tform->errorMessage == '') {
+						
+						if($app->tform->formDef['db_history'] == 'yes') {
+							$old_data_record = $app->tform->getDataRecord($this->id);
+						}
 
                         if(!empty($sql)) {
-                                $app->db->query($sql);
+                            $app->db->query($sql);
                             if($app->db->errorMessage != '') die($app->db->errorMessage);
                         }
 						
@@ -118,6 +122,14 @@ class tform_actions {
                         }
 
                         $this->onAfterUpdate();
+						
+						// Write data history (sys_datalog)
+						if($app->tform->formDef['db_history'] == 'yes') {
+							$new_data_record = $app->tform->getDataRecord($this->id);
+							$app->tform->datalogSave('UPDATE',$this->id,$old_data_record,$new_data_record);
+							unset($new_data_record);
+							unset($old_data_record);
+						}
 
                         if($_REQUEST["next_tab"] == '') {
                            $list_name = $_SESSION["s"]["form"]["return_to"];
@@ -176,6 +188,13 @@ class tform_actions {
 
                         $this->onAfterInsert();
 						
+						// Write data history (sys_datalog)
+						if($app->tform->formDef['db_history'] == 'yes') {
+							$new_data_record = $app->tform->getDataRecord($this->id);
+							$app->tform->datalogSave('INSERT',$this->id,array(),$new_data_record);
+							unset($new_data_record);
+						}
+						
 
                      if($_REQUEST["next_tab"] == '') {
                          $list_name = $_SESSION["s"]["form"]["return_to"];
@@ -260,20 +279,10 @@ class tform_actions {
                         $record_old = $app->db->queryOneRecord("SELECT * FROM ".$liste["table"]." WHERE ".$liste["table_idx"]." = ".$this->id);
 
                         // Saving record to datalog when db_history enabled
-                        if($form["db_history"] == 'yes') {
-                                $diffrec = array();
-
-                                foreach($record_old as $key => $val) {
-                                        // Record has changed
-                                        $diffrec[$key] = array('old' => $val,
-                                                                                           'new' => '');
-                                }
-
-                                $diffstr = $app->db->quote(serialize($diffrec));
-                                $username = $app->db->quote($_SESSION["s"]["user"]["username"]);
-                                $dbidx = $app->tform->formDef['db_table_idx'].":".$this->id;
-                                $sql = "INSERT INTO sys_datalog (dbtable,dbidx,action,tstamp,user,data) VALUES ('".$app->tform->formDef['db_table']."','$dbidx','d','".time()."','$username','$diffstr')";
-                                $app->db->query($sql);
+                        if($app->tform->formDef["db_history"] == 'yes') {
+							$old_data_record = $app->tform->getDataRecord($this->id);
+							$app->tform->datalogSave('DELETE',$this->id,$old_data_record,array());
+							unset($old_data_record);
                         }
 
                         $app->db->query("DELETE FROM ".$liste["table"]." WHERE ".$liste["table_idx"]." = ".$this->id);
diff --git a/interface/web/sites/form/ftp_user.tform.php b/interface/web/sites/form/ftp_user.tform.php
index 9c1399172..4950a1b2d 100644
--- a/interface/web/sites/form/ftp_user.tform.php
+++ b/interface/web/sites/form/ftp_user.tform.php
@@ -99,6 +99,7 @@ $form["tabs"]['ftp'] = array (
 		'password' => array (
 			'datatype'	=> 'VARCHAR',
 			'formtype'	=> 'PASSWORD',
+			'encryption' => 'CRYPT',
 			'default'	=> '',
 			'value'		=> '',
 			'width'		=> '30',
@@ -138,26 +139,26 @@ $form["tabs"]['advanced'] = array (
 	# Begin Datatable fields
 	##################################
 		'uid' => array (
-			'datatype'	=> 'INTEGER',
+			'datatype'	=> 'VARCHAR',
 			'formtype'	=> 'TEXT',
 			'validators'	=> array ( 	0 => array (	'type'	=> 'NOTEMPTY',
 														'errmsg'=> 'uid_error_empty'),
 									),
 			'default'	=> '0',
 			'value'		=> '',
-			'width'		=> '5',
-			'maxlength'	=> '5'
+			'width'		=> '30',
+			'maxlength'	=> '255'
 		),
 		'gid' => array (
-			'datatype'	=> 'INTEGER',
+			'datatype'	=> 'VARCHAR',
 			'formtype'	=> 'TEXT',
 			'validators'	=> array ( 	0 => array (	'type'	=> 'NOTEMPTY',
 														'errmsg'=> 'uid_error_empty'),
 									),
 			'default'	=> '0',
 			'value'		=> '',
-			'width'		=> '5',
-			'maxlength'	=> '5'
+			'width'		=> '30',
+			'maxlength'	=> '255'
 		),
 		'dir' => array (
 			'datatype'	=> 'VARCHAR',
diff --git a/interface/web/sites/templates/ftp_user_advanced.htm b/interface/web/sites/templates/ftp_user_advanced.htm
index 97a09a468..b77b1e0e5 100644
--- a/interface/web/sites/templates/ftp_user_advanced.htm
+++ b/interface/web/sites/templates/ftp_user_advanced.htm
@@ -1,11 +1,11 @@
 <table width="500" border="0" cellspacing="0" cellpadding="2">
   <tr>
     <td class="frmText11">{tmpl_var name='uid_txt'}:</td>
-    <td class="frmText11"><input name="uid" type="text" class="text" value="{tmpl_var name='uid'}" size="5" maxlength="5"></td>
+    <td class="frmText11"><input name="uid" type="text" class="text" value="{tmpl_var name='uid'}" size="30" maxlength="255"></td>
   </tr>
   <tr>
     <td class="frmText11">{tmpl_var name='gid_txt'}:</td>
-    <td class="frmText11"><input name="gid" type="text" class="text" value="{tmpl_var name='gid'}" size="5" maxlength="5"></td>
+    <td class="frmText11"><input name="gid" type="text" class="text" value="{tmpl_var name='gid'}" size="30" maxlength="255"></td>
   </tr>
   <tr>
     <td class="frmText11">{tmpl_var name='dir_txt'}:</td>
diff --git a/interface/web/sites/web_domain_edit.php b/interface/web/sites/web_domain_edit.php
index e912002cc..5f208dbaf 100644
--- a/interface/web/sites/web_domain_edit.php
+++ b/interface/web/sites/web_domain_edit.php
@@ -167,7 +167,8 @@ class page_action extends tform_actions {
 		
 		// Get configuration for the web system
 		$app->uses("getconf");
-		$web_config = $app->getconf->get_server_config(intval($this->dataRecord["server_id"]),'web');
+		$web_rec = $app->tform->getDataRecord($this->id);
+		$web_config = $app->getconf->get_server_config(intval($web_rec["server_id"]),'web');
 		$document_root = str_replace("[website_id]",$this->id,$web_config["website_path"]);
 		
 		// get the ID of the client
@@ -202,7 +203,8 @@ class page_action extends tform_actions {
 		
 		// Get configuration for the web system
 		$app->uses("getconf");
-		$web_config = $app->getconf->get_server_config(intval($this->dataRecord["server_id"]),'web');
+		$web_rec = $app->tform->getDataRecord($this->id);
+		$web_config = $app->getconf->get_server_config(intval($web_rec["server_id"]),'web');
 		$document_root = str_replace("[website_id]",$this->id,$web_config["website_path"]);
 		
 		// get the ID of the client
@@ -211,7 +213,7 @@ class page_action extends tform_actions {
 			$client = $app->db->queryOneRecord("SELECT client_id FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
 			$client_id = intval($client["client_id"]);
 		} else {
-			$client_id = intval($this->dataRecord["client_group_id"]);
+			$client_id = intval($web_rec["client_group_id"]);
 			$client = $app->db->queryOneRecord("SELECT client_id FROM sys_group WHERE sys_group.groupid = ".intval($this->dataRecord["client_group_id"]));
 			$client_id = intval($client["client_id"]);
 		}
diff --git a/server/conf/vhost.conf.master b/server/conf/vhost.conf.master
index ca4aa3414..79ea7d724 100644
--- a/server/conf/vhost.conf.master
+++ b/server/conf/vhost.conf.master
@@ -1,25 +1,26 @@
-NameVirtualHost *:80
+# NameVirtualHost *:80
 
 <VirtualHost <tmpl_var name='ip_address'>:80>
     DocumentRoot <tmpl_var name='document_root'>
     ServerName <tmpl_var name='domain'>
+	ServerAlias <tmpl_var name='alias'>
     
-	<tmpl_if name='cgi'>
-	
-	</tmpl_if>
-	<tmpl_if name='ssi'>
-	
-	</tmpl_if>
-	<tmpl_if name='suexec'>
-	
-	</tmpl_if>
-	<tmpl_if name='php' op='==' value='mod'>
-	
-	</tmpl_if>
-	<tmpl_if name='php' op='==' value='suphp'>
-	
-	</tmpl_if>
-	<tmpl_if name='php' op='==' value='cgi'>
-	
-	</tmpl_if>
+<tmpl_if name='cgi'>
+	# cgi enabled
+</tmpl_if>
+<tmpl_if name='ssi'>
+	# ssi enabled
+</tmpl_if>
+<tmpl_if name='suexec'>
+	# suexec enabled
+</tmpl_if>
+<tmpl_if name='php' op='==' value='mod'>
+	# mod_php enabled
+</tmpl_if>
+<tmpl_if name='php' op='==' value='suphp'>
+	# suphp enabled
+</tmpl_if>
+<tmpl_if name='php' op='==' value='cgi'>
+	# php as cgi enabled
+</tmpl_if>
 </VirtualHost>
\ No newline at end of file
diff --git a/server/lib/classes/file.inc.php b/server/lib/classes/file.inc.php
new file mode 100644
index 000000000..e0bd12f6b
--- /dev/null
+++ b/server/lib/classes/file.inc.php
@@ -0,0 +1,319 @@
+<?php
+/*
+Copyright (c) 2007, projektfarm Gmbh, Till Brehm, Falko Timme
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of ISPConfig nor the names of its contributors
+      may be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+class file{
+
+function rf($file){
+  global $app;
+  clearstatcache();
+  if(!$fp = fopen ($file, "rb")){
+    $app->log("WARNING: could not open file ".$file, 2);
+    return false;
+  } else {
+    if(filesize($file) > 0){
+      $content = fread($fp, filesize($file));
+    } else {
+      $content = "";
+    }
+    fclose($fp);
+    return $content;
+  }
+}
+
+function wf($file, $content){
+  global $app;
+  $this->mkdirs(dirname($file));
+  if(!$fp = fopen ($file, "wb")){
+    $app->log("WARNING: could not open file ".$file, 2);
+    return false;
+  } else {
+    fwrite($fp,$content);
+    fclose($fp);
+    return true;
+  }
+}
+
+function af($file, $content){
+  global $app;
+  $this->mkdirs(dirname($file));
+  if(!$fp = fopen ($file, "ab")){
+    $app->log("WARNING: could not open file ".$file, 2);
+    return false;
+  } else {
+    fwrite($fp,$content);
+    fclose($fp);
+    return true;
+  }
+}
+
+function no_comments($file, $comment = '#'){
+  $content = $this->unix_nl($this->rf($file));
+  $lines = explode("\n", $content);
+  if(!empty($lines)){
+    foreach($lines as $line){
+      if(strstr($line, $comment)){
+        $pos = strpos($line, $comment);
+        if($pos != 0){
+          $new_lines[] = substr($line,0,$pos);
+        } else {
+          $new_lines[] = "";
+        }
+      } else {
+        $new_lines[] = $line;
+      }
+    }
+  }
+  if(is_array($new_lines)){
+    $content_without_comments = implode("\n", $new_lines);
+    $new_lines = NULL;
+    return $content_without_comments;
+  } else {
+    return "";
+  }
+}
+
+function manual_entries($file, $separator = '#### MAKE MANUAL ENTRIES BELOW THIS LINE! ####'){
+  if(is_file($file)){
+    $content = $this->rf($file);
+    $parts = explode($separator, $content);
+    $manual = "\n".trim($parts[1]);
+    return $manual;
+  } else {
+    return "";
+  }
+}
+
+function remove_blank_lines($input, $file = 1){
+  //Leerzeilen löschen
+  if($file){
+    $content = $this->unix_nl($this->rf($input));
+  } else {
+    $content = $input;
+  }
+  $lines = explode("\n", $content);
+  if(!empty($lines)){
+    foreach($lines as $line){
+      if(trim($line) != "") $new_lines[] = $line;
+    }
+  }
+  if(is_array($new_lines)){
+    $content = implode("\n", $new_lines);
+  } else {
+    $content = "";
+  }
+  if($file){
+    $this->wf($input, $content);
+  } else {
+    return $content;
+  }
+}
+
+function unix_nl($input){
+  $output = str_replace("\r\n", "\n", $input);
+  $output = str_replace("\r", "\n", $output);
+  return $output;
+}
+
+function fileowner($file){
+  $owner_id = fileowner($file);
+  clearstatcache();
+  return $owner_id;
+}
+
+function mkdirs($strPath, $mode = '0755'){
+  // Verzeichnisse rekursiv erzeugen
+  if(is_dir($strPath)) return true;
+  $pStrPath = dirname($strPath);
+  if(!$this->mkdirs($pStrPath, $mode)) return false;
+  $old_umask = umask(0);
+  $ret_val = mkdir($strPath, octdec($mode));
+  umask($old_umask);
+  return $ret_val;
+}
+
+function find_includes($file){
+  ob_start();
+  $httpd_root = system('httpd -V | awk -F"\"" \'$1==" -D HTTPD_ROOT="{print $2}\'');
+  ob_end_clean();
+  clearstatcache();
+  if(is_file($file) && filesize($file) > 0){
+    $includes[] = $file;
+    $inhalt = $this->unix_nl($this->no_comments($file));
+    $lines = explode("\n", $inhalt);
+    if(!empty($lines)){
+      foreach($lines as $line){
+        if(stristr($line, "include ")){
+          $include_file = str_replace("\n", "", trim(shell_exec("echo \"$line\" | awk '{print \$2}'")));
+          if(substr($include_file,0,1) != "/"){
+            $include_file = $httpd_root."/".$include_file;
+          }
+          if(is_file($include_file)){
+            if($further_includes = $this->find_includes($include_file)){
+              $includes = array_merge($includes, $further_includes);
+            }
+          } else {
+            if(strstr($include_file, "*")){
+              $more_files = explode("\n", shell_exec("ls -l $include_file | awk '{print \$9}'"));
+              if(!empty($more_files)){
+                foreach($more_files as $more_file){
+                  if(is_file($more_file)){
+                    if($further_includes = $this->find_includes($more_file)){
+                      $includes = array_merge($includes, $further_includes);
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  if(is_array($includes)){
+    $includes = array_unique($includes);
+    return $includes;
+  } else {
+    return false;
+  }
+}
+
+function edit_dist($var, $val){
+  global $$var;
+  $files = array("/root/ispconfig/dist.inc.php");
+  foreach($files as $file){
+    if(is_file($file)){
+      $file_content = $this->unix_nl($this->rf($file));
+      $lines = explode("\n", $file_content);
+      for($i=0;$i<sizeof($lines);$i++){
+        $parts = explode("=", $lines[$i]);
+        if($parts[0] == $var || $parts[0] == '$'.$var.' '){
+          $parts[1] = str_replace($$var, $val, $parts[1]);
+        }
+        $lines[$i] = implode("=", $parts);
+      }
+      $file_content = implode("\n", $lines);
+      $this->wf($file, $file_content);
+    }
+  }
+}
+
+function getDirectoryListing($dirname, $sortorder = "a", $show_subdirs = 0, $show_subdirfiles = 0, $exts = "", $ext_save = 1){
+// This function will return an array with filenames based on the criteria you can set in the variables
+// @sortorder : a for ascending (the standard) or d for descending (you can use the "r" for reverse as well, works the same)
+// @show_subdirs : 0 for NO, 1 for YES - meaning it will show the names of subdirectories if there are any
+// Logically subdirnames will not be checked for the required extentions
+// @show_subdirfiles : 0 for NO, 1 for YES - meaning it will show files from the subdirs
+// Files from subdirs will be prefixed with the subdir name and checked for the required extentions.
+// @exts can be either a string or an array, if not passed to the function, then the default will be a check for common image files
+// If exts is set to "all" then all extentions are allowed
+// @ext_save : 1 for YES, 0 for NO - meaning it will filter out system files or not (such as .htaccess)
+
+   $dirname = realpath($dirname);
+   if (!$exts || empty($exts) || $exts == "") {
+       $exts = array("jpg", "gif", "jpeg", "png");
+   }
+   if ($handle = opendir($dirname)) {
+       $filelist = array();
+       while (false !== ($file = readdir($handle))) {
+
+           // Filter out higher directory references
+           if ($file != "." && $file != "..") {
+               // Only look at directories or files, filter out symbolic links
+               if ( filetype ($dirname."/".$file) != "link") {
+                   // If it's a file, check against valid extentions and add to the list
+                   if ( filetype ($dirname."/".$file) == "file" ) {
+                       if ($this->checkFileExtension($file, $exts, $ext_save)) {
+                                       $filelist[] = $file;
+                       }
+                   }
+                   // If it's a directory and either subdirs should be listed or files from subdirs add relevant names to the list
+                   else if ( filetype ($dirname."/".$file) == "dir" && ($show_subdirs == 1 || $show_subdirfiles == 1)) {
+                       if ($show_subdirs == 1) {
+                           $filelist[] = $file;
+                       }
+                       if ($show_subdirfiles == 1) {
+                           $subdirname = $file;
+                           $subdirfilelist = $this->getDirectoryListing($dirname."/".$subdirname."/", $sortorder, $show_subdirs, $show_subdirfiles, $exts, $ext_save);
+                           for ($i = 0 ; $i < count($subdirfilelist) ; $i++) {
+                               $subdirfilelist[$i] = $subdirname."/".$subdirfilelist[$i];
+                           }
+                           $filelist = array_merge($filelist, $subdirfilelist);
+                       }
+
+                   }
+
+               }
+           }
+       }
+       closedir($handle);
+
+       // Sort the results
+       if (count($filelist) > 1) {
+           natcasesort($filelist);
+           if ($sortorder == "d" || $sortorder == "r" ) {
+               $filelist = array_reverse($filelist, TRUE);
+           }
+       }
+       return $filelist;
+   }
+   else {
+       return false;
+   }
+}
+
+function checkFileExtension($filename, $exts, $ext_save = 1){
+   $passed = FALSE;
+   if ($ext_save == 1) {
+       if (preg_match("/^\./", $filename)) {
+           return $passed;
+       }
+   }
+   if ($exts == "all") {
+                   $passed = TRUE;
+       return $passed;
+   }
+   if (is_string($exts)) {
+       if (eregi("\.". $exts ."$", $filename)) {
+                       $passed = TRUE;
+           return $passed;
+       }
+   } else if (is_array($exts)) {
+       foreach ($exts as $theExt) {
+           if (eregi("\.". $theExt ."$", $filename)) {
+               $passed = TRUE;
+               return $passed;
+           }
+       }
+   }
+   return $passed;
+}
+
+}
+?>
\ No newline at end of file
diff --git a/server/lib/classes/services.inc.php b/server/lib/classes/services.inc.php
new file mode 100644
index 000000000..260d3f0ed
--- /dev/null
+++ b/server/lib/classes/services.inc.php
@@ -0,0 +1,79 @@
+<?php
+/*
+Copyright (c) 2007, projektfarm Gmbh, Till Brehm, Falko Timme
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of ISPConfig nor the names of its contributors
+      may be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+class services {
+
+	var $registered_services = array();
+	var $delayed_restarts = array();
+	
+	// This function adds a request for restarting 
+	// a service at the end of the configuration run.
+	function restartServiceDelayed($service_name,$action = 'restart') {
+		global $app;
+		if(is_array($this->registered_services[$service_name])) {
+			$this->delayed_restarts[$service_name] = $action;
+		} else {
+			$app->log("Unable to add a delayed restart for  '$service_name'. Service '$service_name' is not registered.",LOGLEVEL_WARNING);
+		}
+		
+	}
+	
+	// This function restarts a service when the function is called
+	function restartService($service_name,$action = 'restart') {
+		global $app;
+		
+		if(is_array($this->registered_services[$service_name])) {
+			$module_name = $this->registered_services[$service_name]["module"];
+			$function_name = $this->registered_services[$service_name]["function"];
+			$app->log("Call function '$function_name' in module '$module_name'.",LOGLEVEL_DEBUG);
+			call_user_method($function_name,$app->loaded_modules[$module_name],$action);
+		} else {
+			$app->log("Unable to restart $service_name. Service $service_name is not registered.",LOGLEVEL_WARNING);
+		}
+		
+	}
+	
+	// This function is used to register callback functions for services that can be restarted
+	function registerService($service_name,$module_name, $function_name) {
+		global $app;
+		$this->registered_services[$service_name] = array('module' => $module_name, 'function' => $function_name);
+		$app->log("Registered Service '$service_name' in module '$module_name' for processing function '$function_name'",LOGLEVEL_DEBUG);
+	}
+	
+	// This function is called at the end of the server script to restart services.
+	function processDelayedActions() {
+		global $app;
+		foreach($this->delayed_restarts as $service_name => $action) {
+			$this->restartService($service_name,$action);
+		}
+	}
+
+}
+?>
\ No newline at end of file
diff --git a/server/lib/classes/system.inc.php b/server/lib/classes/system.inc.php
new file mode 100644
index 000000000..977ed33cd
--- /dev/null
+++ b/server/lib/classes/system.inc.php
@@ -0,0 +1,999 @@
+<?php
+/*
+Copyright (c) 2007, projektfarm Gmbh, Till Brehm, Falko Timme
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of ISPConfig nor the names of its contributors
+      may be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+class system{
+
+var $FILE = "/root/ispconfig/scripts/lib/classes/ispconfig_system.lib.php";
+var $server_id;
+var $server_conf;
+var $data;
+
+function system(){
+  global $go_info;
+  $this->server_id = $go_info["isp"]["server_id"];
+  $this->server_conf = $go_info["isp"]["server_conf"];
+  $this->server_conf["passwd_datei"] = '/etc/passwd';
+  $this->server_conf["shadow_datei"] = '/etc/shadow';
+  $this->server_conf["group_datei"] = '/etc/group';
+}
+
+function hostname(){
+  $dist = $this->server_conf["dist"];
+
+  ob_start();
+  passthru("hostname");
+  $hostname = ob_get_contents();
+  ob_end_clean();
+  $hostname = trim($hostname);
+  ob_start();
+  if(!strstr($dist, "freebsd")){
+    passthru("dnsdomainname");
+  } else {
+    passthru("domainname");
+  }
+  $domainname = ob_get_contents();
+  ob_end_clean();
+  $domainname = trim($domainname);
+  if($domainname != ""){
+    if(!strstr($hostname, $domainname)) $hostname .= ".".$domainname;
+  }
+  return $hostname;
+}
+
+function adduser($user_username, $uid, $gid, $username, $homedir, $shell, $passwort = '*'){
+  global $app;
+  if($this->is_user($user_username)){
+    return false;
+  } else {
+          if(trim($user_username) != '') {
+                $user_datei = $this->server_conf["passwd_datei"];
+            $shadow_datei = $this->server_conf["shadow_datei"];
+            $shell = realpath($shell);
+            if(trim($passwort) == "") $passwort = '*';
+            $new_user = "\n$user_username:x:$uid:$gid:$username:$homedir:$shell\n";
+                $app->log->msg("USER: $new_user");
+            $app->file->af($user_datei, $new_user);
+            if($shadow_datei == "/etc/shadow"){
+                      $datum = time();
+                      $tage = floor($datum/86400);
+                      $new_passwd = "\n$user_username:$passwort:$tage:0:99999:7:::\n";
+            } else {
+                      $new_passwd = "\n$user_username:$passwort:$uid:$gid::0:0:$username:$homedir:$shell\n";
+            }
+            $app->file->af($shadow_datei, $new_passwd);
+
+                // TB: leere Zeilen entfernen
+                $app->file->remove_blank_lines($shadow_datei);
+                $app->file->remove_blank_lines($user_datei);
+            // TB: user Sortierung deaktiviert
+                //$this->order_users_groups();
+            if($shadow_datei != "/etc/shadow"){
+                      $app->file->af($shadow_datei, "\n");
+
+                        // TB: leere Zeilen entfernen
+                        $app->file->remove_blank_lines($shadow_datei);
+
+                      $app->log->caselog("pwd_mkdb $shadow_datei &> /dev/null", $this->FILE, __LINE__);
+            }
+
+            return true;
+        }
+  }
+}
+
+function updateuser($user_username, $uid, $gid, $username, $homedir, $shell, $passwort = '*'){
+  $this->deluser($user_username);
+  $this->adduser($user_username, $uid, $gid, $username, $homedir, $shell, $passwort);
+}
+
+function deactivateuser($user_username){
+  $passwort = str_rot13($this->getpasswd($user_username));
+  $user_attr = $this->get_user_attributes($user_username);
+  $uid = $user_attr["uid"];
+  $gid = $user_attr["gid"];
+  $username = $user_attr["name"];
+  $homedir = $user_attr["homedir"];
+  $shell = "/dev/null";
+  $this->deluser($user_username);
+  $this->adduser($user_username, $uid, $gid, $username, $homedir, $shell, $passwort);
+}
+
+function deluser($user_username){
+  global $app;
+  if($this->is_user($user_username)){
+    $user_datei = $this->server_conf["passwd_datei"];
+    $shadow_datei = $this->server_conf["shadow_datei"];
+    $users = $app->file->rf($user_datei);
+    $lines = explode("\n", $users);
+    if(is_array($lines)){
+      $num_lines = sizeof($lines);
+      for($i=0;$i<$num_lines;$i++){
+        if(trim($lines[$i]) != ""){
+          list($f1,) = explode(":", $lines[$i]);
+          if($f1 != $user_username) $new_lines[] = $lines[$i];
+        }
+      }
+      $new_users = implode("\n", $new_lines);
+      $app->file->wf($user_datei, $new_users);
+      unset($new_lines);
+      unset($lines);
+      unset($new_users);
+    }
+    $app->file->remove_blank_lines($user_datei);
+
+    $passwds = $app->file->rf($shadow_datei);
+    $lines = explode("\n", $passwds);
+    if(is_array($lines)){
+      $num_lines = sizeof($lines);
+      for($i=0;$i<$num_lines;$i++){
+        if(trim($lines[$i]) != ""){
+          list($f1,) = explode(":", $lines[$i]);
+          if($f1 != $user_username) $new_lines[] = $lines[$i];
+        }
+      }
+      $new_passwds = implode("\n", $new_lines);
+      $app->file->wf($shadow_datei, $new_passwds);
+      unset($new_lines);
+      unset($lines);
+      unset($new_passwds);
+    }
+    $app->file->remove_blank_lines($shadow_datei);
+
+    $group_file = $app->file->rf($this->server_conf["group_datei"]);
+    $group_file_lines = explode("\n", $group_file);
+    foreach($group_file_lines as $group_file_line){
+      if(trim($group_file_line) != ""){
+        list($f1, $f2, $f3, $f4) = explode(":", $group_file_line);
+        $group_users = explode(",", str_replace(" ", "", $f4));
+        if(in_array($user_username, $group_users)){
+          $g_users = array();
+          foreach($group_users as $group_user){
+            if($group_user != $user_username) $g_users[] = $group_user;
+          }
+          $f4 = implode(",", $g_users);
+        }
+        $new_group_file[] = $f1.":".$f2.":".$f3.":".$f4;
+      }
+    }
+    $new_group_file = implode("\n", $new_group_file);
+    $app->file->wf($this->server_conf["group_datei"], $new_group_file);
+    // TB: auskommentiert
+        //$this->order_users_groups();
+
+    if($shadow_datei != "/etc/shadow"){
+      $app->file->af($shadow_datei, "\n");
+      $app->log->caselog("pwd_mkdb $shadow_datei &> /dev/null", $this->FILE, __LINE__);
+    }
+    return true;
+  } else {
+    return false;
+  }
+}
+
+function addgroup($group, $gid, $members = ''){
+  global $app;
+  if($this->is_group($group)){
+    return false;
+  } else {
+    $group_datei = $this->server_conf["group_datei"];
+    $shadow_datei = $this->server_conf["shadow_datei"];
+    $new_group = "\n$group:x:$gid:$members\n";
+    $app->file->af($group_datei, $new_group);
+
+        // TB: auskommentiert
+        //$this->order_users_groups();
+    if($shadow_datei != "/etc/shadow"){
+      $app->log->caselog("pwd_mkdb $shadow_datei &> /dev/null", $this->FILE, __LINE__);
+    }
+    return true;
+  }
+}
+
+function updategroup($group, $gid, $members = ''){
+  $this->delgroup($group);
+  $this->addgroup($group, $gid, $members);
+}
+
+function delgroup($group){
+  global $app;
+  if($this->is_group($group)){
+    $group_datei = $this->server_conf["group_datei"];
+    $shadow_datei = $this->server_conf["shadow_datei"];
+    $groups = $app->file->rf($group_datei);
+    $lines = explode("\n", $groups);
+    if(is_array($lines)){
+      $num_lines = sizeof($lines);
+      for($i=0;$i<$num_lines;$i++){
+        if(trim($lines[$i]) != ""){
+          list($f1,) = explode(":", $lines[$i]);
+          if($f1 != $group) $new_lines[] = $lines[$i];
+        }
+      }
+      $new_groups = implode("\n", $new_lines);
+      $app->file->wf($group_datei, $new_groups);
+      unset($new_lines);
+      unset($lines);
+      unset($new_groups);
+    }
+        // TB: auskommentiert
+    //$this->order_users_groups();
+    if($shadow_datei != "/etc/shadow"){
+      $app->log->caselog("pwd_mkdb $shadow_datei &> /dev/null", $this->FILE, __LINE__);
+    }
+    return true;
+  } else {
+    return false;
+  }
+}
+
+function order_users_groups(){
+  global $app;
+  $user_datei = $this->server_conf["passwd_datei"];
+  $shadow_datei = $this->server_conf["shadow_datei"];
+  $group_datei = $this->server_conf["group_datei"];
+
+  $groups = $app->file->no_comments($group_datei);
+  $lines = explode("\n", $groups);
+  if(is_array($lines)){
+    foreach($lines as $line){
+      if(trim($line) != ""){
+        list($f1, $f2, $f3, $f4) = explode(":", $line);
+        $arr[$f3] = $line;
+      }
+    }
+  }
+  ksort($arr);
+  reset($arr);
+  if($shadow_datei != "/etc/shadow"){
+    $app->file->wf($group_datei, $app->file->remove_blank_lines(implode("\n", $arr), 0)."\n");
+  }else {
+    $app->file->wf($group_datei, $app->file->remove_blank_lines(implode("\n", $arr), 0));
+  }
+  unset($arr);
+
+  $users = $app->file->no_comments($user_datei);
+  $lines = explode("\n", $users);
+  if(is_array($lines)){
+    foreach($lines as $line){
+      if(trim($line) != ""){
+        list($f1, $f2, $f3,) = explode(":", $line);
+        if($f1 != "toor"){
+          $arr[$f3] = $line;
+        } else {
+          $arr[70000] = $line;
+        }
+      }
+    }
+  }
+  ksort($arr);
+  reset($arr);
+  $app->file->wf($user_datei, $app->file->remove_blank_lines(implode("\n", $arr), 0));
+  unset($arr);
+
+  $passwds = $app->file->no_comments($shadow_datei);
+  $lines = explode("\n", $passwds);
+  if(is_array($lines)){
+    foreach($lines as $line){
+      if(trim($line) != ""){
+        list($f1, $f2, $f3,) = explode(":", $line);
+        if($f1 != "toor"){
+          $uid = $this->getuid($f1);
+          if(!is_bool($uid)) $arr[$uid] = $line;
+        } else {
+          $arr[70000] = $line;
+        }
+      }
+    }
+  }
+  ksort($arr);
+  reset($arr);
+  $app->file->wf($shadow_datei, $app->file->remove_blank_lines(implode("\n", $arr), 0));
+  unset($arr);
+}
+
+function find_uid_gid($min, $max){
+  global $app;
+  if($min < $max && $min >= 0 && $max >= 0 && $min <= 65536 && $max <= 65536 && is_int($min) && is_int($max)){
+    for($i=$min;$i<=$max;$i++){
+      $uid_arr[$i] = $gid_arr[$i] = 1;
+    }
+    $user_datei = $this->server_conf["passwd_datei"];
+    $group_datei = $this->server_conf["group_datei"];
+
+    $users = $app->file->no_comments($user_datei);
+    $lines = explode("\n", $users);
+    if(is_array($lines)){
+      foreach($lines as $line){
+        if(trim($line) != ""){
+          list($f1, $f2, $f3, $f4, $f5, $f6, $f7) = explode(":", $line);
+          if($f3 >= $min && $f3 <= $max) unset($uid_arr[$f3]);
+        }
+      }
+      if(!empty($uid_arr)){
+        foreach($uid_arr as $key => $val){
+          $uids[] = $key;
+        }
+        $min_uid = min($uids);
+        unset($uid_arr);
+      } else {
+        return false;
+      }
+    }
+
+    $groups = $app->file->no_comments($group_datei);
+    $lines = explode("\n", $groups);
+    if(is_array($lines)){
+      foreach($lines as $line){
+        if(trim($line) != ""){
+          list($f1, $f2, $f3, $f4) = explode(":", $line);
+          if($f3 >= $min && $f3 <= $max) unset($gid_arr[$f3]);
+        }
+      }
+      if(!empty($gid_arr)){
+        foreach($gid_arr as $key => $val){
+          $gids[] = $key;
+        }
+        $min_gid = min($gids);
+        unset($gid_arr);
+      } else {
+        return false;
+      }
+    }
+
+    $result = array_intersect($uids, $gids);
+    $new_id = (max($result));
+    unset($uids);
+    unset($gids);
+    unset($result);
+    if($new_id <= $max){
+      return $new_id;
+    } else {
+      return false;
+    }
+  } else {
+    return false;
+  }
+}
+
+function is_user($user){
+  global $app;
+  $user_datei = $this->server_conf["passwd_datei"];
+  $users = $app->file->no_comments($user_datei);
+  $lines = explode("\n", $users);
+  if(is_array($lines)){
+    foreach($lines as $line){
+      if(trim($line) != ""){
+        list($f1, $f2, $f3, $f4, $f5, $f6, $f7) = explode(":", $line);
+        if($f1 == $user) return true;
+      }
+    }
+  }
+  return false;
+}
+
+function is_group($group){
+  global $app;
+  $group_datei = $this->server_conf["group_datei"];
+  $groups = $app->file->no_comments($group_datei);
+  $lines = explode("\n", $groups);
+  if(is_array($lines)){
+    foreach($lines as $line){
+      if(trim($line) != ""){
+        list($f1, $f2, $f3, $f4) = explode(":", $line);
+        if($f1 == $group) return true;
+      }
+    }
+  }
+  return false;
+}
+
+function root_group(){
+  global $app;
+  $group_datei = $this->server_conf["group_datei"];
+  $groups = $app->file->no_comments($group_datei);
+  $lines = explode("\n", $groups);
+  if(is_array($lines)){
+    foreach($lines as $line){
+      if(trim($line) != ""){
+        list($f1, $f2, $f3, $f4) = explode(":", $line);
+        if($f3 == 0) return $f1;
+      }
+    }
+  }
+  return false;
+}
+
+function get_user_groups($username){
+  global $app;
+  $user_groups = array();
+  $group_datei = $this->server_conf["group_datei"];
+  $groups = $app->file->no_comments($group_datei);
+  $lines = explode("\n", $groups);
+  if(is_array($lines)){
+    foreach($lines as $line){
+      if(trim($line) != ""){
+        list($f1, $f2, $f3, $f4) = explode(":", $line);
+        if(intval($f3) < intval($this->server_conf["groupid_von"]) && trim($f1) != 'users'){
+          $tmp_group_users = explode(',', str_replace(' ', '', $f4));
+          if(in_array($username, $tmp_group_users) && trim($f1) != '') $user_groups[] = $f1;
+          unset($tmp_group_users);
+        }
+      }
+    }
+  }
+  if(!empty($user_groups)) return implode(',', $user_groups);
+  return '';
+}
+
+function getpasswd($user){
+  global $app;
+  if($this->is_user($user)){
+    $shadow_datei = $this->server_conf["shadow_datei"];
+    $passwds = $app->file->no_comments($shadow_datei);
+    $lines = explode("\n", $passwds);
+    if(is_array($lines)){
+      foreach($lines as $line){
+        if(trim($line) != ""){
+          list($f1, $f2,) = explode(":", $line);
+          if($f1 == $user) return $f2;
+        }
+      }
+    }
+  } else {
+    return false;
+  }
+}
+
+function getuid($user){
+  global $app;
+  if($this->is_user($user)){
+    $user_datei = $this->server_conf["passwd_datei"];
+    $users = $app->file->no_comments($user_datei);
+    $lines = explode("\n", $users);
+    if(is_array($lines)){
+      foreach($lines as $line){
+        if(trim($line) != ""){
+          list($f1, $f2, $f3,) = explode(":", $line);
+          if($f1 == $user) return $f3;
+        }
+      }
+    }
+  } else {
+    return false;
+  }
+}
+
+function get_user_attributes($user){
+  global $app;
+  if($this->is_user($user)){
+    $user_datei = $this->server_conf["passwd_datei"];
+    $users = $app->file->no_comments($user_datei);
+    $lines = explode("\n", $users);
+    if(is_array($lines)){
+      foreach($lines as $line){
+        if(trim($line) != ""){
+          list($f1, $f2, $f3, $f4, $f5, $f6, $f7) = explode(":", $line);
+          if($f1 == $user){
+            $user_attr["username"] = $f1;
+            $user_attr["x"] = $f2;
+            $user_attr["uid"] = $f3;
+            $user_attr["gid"] = $f4;
+            $user_attr["name"] = $f5;
+            $user_attr["homedir"] = $f6;
+            $user_attr["shell"] = $f7;
+            return $user_attr;
+          }
+        }
+      }
+    }
+  } else {
+    return false;
+  }
+}
+
+function chown($file, $owner, $group = ''){
+  $owner_change = @chown($file, $owner);
+  if($group != ""){
+    $group_change = @chgrp($file, $group);
+  } else {
+    $group_change = 1;
+  }
+  if($owner_change && $group_change){
+    return true;
+  } else {
+    return false;
+  }
+}
+
+function add_user_to_group($group, $user = 'admispconfig'){
+  global $app;
+  $group_file = $app->file->rf($this->server_conf["group_datei"]);
+  $group_file_lines = explode("\n", $group_file);
+  foreach($group_file_lines as $group_file_line){
+    list($group_name,$group_x,$group_id,$group_users) = explode(":",$group_file_line);
+    if($group_name == $group){
+      $group_users = explode(",", str_replace(" ", "", $group_users));
+      if(!in_array($user, $group_users)){
+        $group_users[] = $user;
+      }
+      $group_users = implode(",", $group_users);
+      if(substr($group_users,0,1) == ",") $group_users = substr($group_users,1);
+      $group_file_line = $group_name.":".$group_x.":".$group_id.":".$group_users;
+    }
+    $new_group_file[] = $group_file_line;
+  }
+  $new_group_file = implode("\n", $new_group_file);
+  $app->file->wf($this->server_conf["group_datei"], $new_group_file);
+  $app->file->remove_blank_lines($this->server_conf["group_datei"]);
+  if($this->server_conf["shadow_datei"] != "/etc/shadow"){
+    $app->log->caselog("pwd_mkdb ".$this->server_conf["shadow_datei"]." &> /dev/null", $this->FILE, __LINE__);
+  }
+}
+
+function usermod($user, $groups){
+  global $app;
+  if($this->is_user($user)){
+    $groups = explode(",", str_replace(" ", "", $groups));
+    $group_file = $app->file->rf($this->server_conf["group_datei"]);
+    $group_file_lines = explode("\n", $group_file);
+    foreach($group_file_lines as $group_file_line){
+      if(trim($group_file_line) != ""){
+        list($f1, $f2, $f3, $f4) = explode(":", $group_file_line);
+        $group_users = explode(",", str_replace(" ", "", $f4));
+        if(!in_array($f1, $groups)){
+          if(in_array($user, $group_users)){
+            $g_users = array();
+            foreach($group_users as $group_user){
+              if($group_user != $user) $g_users[] = $group_user;
+            }
+            $f4 = implode(",", $g_users);
+          }
+        } else {
+          if(!in_array($user, $group_users)){
+            if(trim($group_users[0]) == "") unset($group_users);
+            $group_users[] = $user;
+          }
+          $f4 = implode(",", $group_users);
+        }
+        $new_group_file[] = $f1.":".$f2.":".$f3.":".$f4;
+      }
+    }
+    $new_group_file = implode("\n", $new_group_file);
+    $app->file->wf($this->server_conf["group_datei"], $new_group_file);
+    $app->file->remove_blank_lines($this->server_conf["group_datei"]);
+    if($this->server_conf["shadow_datei"] != "/etc/shadow"){
+      $app->log->caselog("pwd_mkdb ".$this->server_conf["shadow_datei"]." &> /dev/null", $this->FILE, __LINE__);
+    }
+    return true;
+  } else {
+    return false;
+  }
+}
+
+function rc_edit($service, $rl, $action){
+  // $action = "on|off";
+  global $app;
+  $dist_init_scripts = $app->system->server_conf["dist_init_scripts"];
+  $dist_runlevel = $app->system->server_conf["dist_runlevel"];
+  $dist = $app->system->server_conf["dist"];
+  if(trim($dist_runlevel) == ""){ // falls es keine runlevel gibt (FreeBSD)
+    if($action == "on"){
+      @symlink($dist_init_scripts."/".$service, $dist_init_scripts."/".$service.".sh");
+    }
+    if($action == "off"){
+      if(is_link($dist_init_scripts."/".$service.".sh")){
+        unlink($dist_init_scripts."/".$service.".sh");
+      } else {
+        exec("mv -f ".$dist_init_scripts."/".$service.".sh ".$dist_init_scripts."/".$service." &> /dev/null");
+      }
+    }
+  } else { // Linux
+    if(substr($dist, 0,4) == 'suse'){
+      if($action == "on"){
+        exec("chkconfig --add $service &> /dev/null");
+      }
+      if($action == "off"){
+        exec("chkconfig --del $service &> /dev/null");
+      }
+    } else {
+      $runlevels = explode(",", $rl);
+      foreach($runlevels as $runlevel){
+        $runlevel = trim($runlevel);
+        if($runlevel != "" && is_dir($dist_runlevel."/rc".$runlevel.".d")){
+          $handle=opendir($dist_runlevel."/rc".$runlevel.".d");
+          while($file = readdir($handle)){
+            if($file != "." && $file != ".."){
+              $target = @readlink($dist_runlevel."/rc".$runlevel.".d/".$file);
+              if(strstr($file, $service) && strstr($target, $service) && substr($file,0,1) == "S") $ln_arr[$runlevel][] = $dist_runlevel."/rc".$runlevel.".d/".$file;
+            }
+          }
+          closedir($handle);
+        }
+        if($action == "on"){
+          if(!is_array($ln_arr[$runlevel])) @symlink($dist_init_scripts."/".$service, $dist_runlevel."/rc".$runlevel.".d/S99".$service);
+        }
+        if($action == "off"){
+          if(is_array($ln_arr[$runlevel])){
+            foreach($ln_arr[$runlevel] as $link){
+              unlink($link);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+function grep($content, $string, $params = ''){
+  global $app;
+  // params: i, v, w
+  $content = $app->file->unix_nl($content);
+  $lines = explode("\n", $content);
+  foreach($lines as $line){
+    if(!strstr($params, 'w')){
+      if(strstr($params, 'i')){
+        if(strstr($params, 'v')){
+          if(!stristr($line, $string)) $find[] = $line;
+        } else {
+          if(stristr($line, $string)) $find[] = $line;
+        }
+      } else {
+        if(strstr($params, 'v')){
+          if(!strstr($line, $string)) $find[] = $line;
+        } else {
+          if(strstr($line, $string)) $find[] = $line;
+        }
+      }
+    } else {
+      if(strstr($params, 'i')){
+        if(strstr($params, 'v')){
+          if(!$app->string->is_word($string, $line, 'i')) $find[] = $line;
+        } else {
+          if($app->string->is_word($string, $line, 'i')) $find[] = $line;
+        }
+      } else {
+        if(strstr($params, 'v')){
+          if(!$app->string->is_word($string, $line)) $find[] = $line;
+        } else {
+          if($app->string->is_word($string, $line)) $find[] = $line;
+        }
+      }
+    }
+  }
+  if(is_array($find)){
+    $ret_val = implode("\n", $find);
+    if(substr($ret_val,-1) != "\n") $ret_val .= "\n";
+    $find = NULL;
+    return $ret_val;
+  } else {
+    return false;
+  }
+}
+
+function cut($content, $field, $delimiter = ':'){
+  global $app;
+  $content = $app->file->unix_nl($content);
+  $lines = explode("\n", $content);
+  foreach($lines as $line){
+    $elms = explode($delimiter, $line);
+    $find[] = $elms[($field-1)];
+  }
+  if(is_array($find)){
+    $ret_val = implode("\n", $find);
+    if(substr($ret_val,-1) != "\n") $ret_val .= "\n";
+    $find = NULL;
+    return $ret_val;
+  } else {
+    return false;
+  }
+}
+
+function cat($file){
+  global $app;
+  return $app->file->rf($file);
+}
+
+function daemon_init($daemon, $action){
+  // $action = start|stop|restart|reload
+  global $app;
+  $dist = $this->server_conf["dist"];
+  $dist_init_scripts = $this->server_conf["dist_init_scripts"];
+  if(!strstr($dist, "freebsd")){
+    $app->log->caselog("$dist_init_scripts/$daemon $action &> /dev/null", $this->FILE, __LINE__);
+  } else {
+    if(is_file($dist_init_scripts."/".$daemon.".sh") || is_link($dist_init_scripts."/".$daemon.".sh")){
+      if($action == "start" || $action == "stop"){
+        $app->log->caselog($dist_init_scripts."/".$daemon.".sh ".$action." &> /dev/null", $this->FILE, __LINE__);
+      } else {
+        $app->log->caselog($dist_init_scripts."/".$daemon.".sh stop &> /dev/null", $this->FILE, __LINE__);
+        sleep(3);
+        $app->log->caselog($dist_init_scripts."/".$daemon.".sh start &> /dev/null", $this->FILE, __LINE__);
+      }
+    } else {
+      if(is_file($dist_init_scripts."/".$daemon) || is_link($dist_init_scripts."/".$daemon)){
+        if($action == "start" || $action == "stop"){
+          $app->log->caselog($dist_init_scripts."/".$daemon." ".$action." &> /dev/null", $this->FILE, __LINE__);
+        } else {
+          $app->log->caselog($dist_init_scripts."/".$daemon." stop &> /dev/null", $this->FILE, __LINE__);
+          sleep(3);
+          $app->log->caselog($dist_init_scripts."/".$daemon." start &> /dev/null", $this->FILE, __LINE__);
+        }
+      } else {
+        if(is_file("/etc/rc.d/".$daemon) || is_link("/etc/rc.d/".$daemon)){
+          if($action == "start" || $action == "stop"){
+            $app->log->caselog("/etc/rc.d/".$daemon." ".$action." &> /dev/null", $this->FILE, __LINE__);
+          } else {
+            $app->log->caselog("/etc/rc.d/".$daemon." stop &> /dev/null", $this->FILE, __LINE__);
+            sleep(3);
+            $app->log->caselog("/etc/rc.d/".$daemon." start &> /dev/null", $this->FILE, __LINE__);
+          }
+        }
+      }
+    }
+  }
+}
+
+function netmask($netmask){
+  list($f1,$f2,$f3,$f4) = explode(".", trim($netmask));
+  $bin = str_pad(decbin($f1),8,"0",STR_PAD_LEFT).str_pad(decbin($f2),8,"0",STR_PAD_LEFT).str_pad(decbin($f3),8,"0",STR_PAD_LEFT).str_pad(decbin($f4),8,"0",STR_PAD_LEFT);
+  $parts = explode("0", $bin);
+  $bin = str_pad($parts[0], 32, "0", STR_PAD_RIGHT);
+  $bin = wordwrap($bin, 8, ".", 1);
+  list($f1,$f2,$f3,$f4) = explode(".", trim($bin));
+  return bindec($f1).".".bindec($f2).".".bindec($f3).".".bindec($f4);
+}
+
+function binary_netmask($netmask){
+  list($f1,$f2,$f3,$f4) = explode(".", trim($netmask));
+  $bin = str_pad(decbin($f1),8,"0",STR_PAD_LEFT).str_pad(decbin($f2),8,"0",STR_PAD_LEFT).str_pad(decbin($f3),8,"0",STR_PAD_LEFT).str_pad(decbin($f4),8,"0",STR_PAD_LEFT);
+  $parts = explode("0", $bin);
+  return substr_count($parts[0], "1");
+}
+
+function network($ip, $netmask){
+  $netmask = $this->netmask($netmask);
+  list($f1,$f2,$f3,$f4) = explode(".", $netmask);
+  $netmask_bin = str_pad(decbin($f1),8,"0",STR_PAD_LEFT).str_pad(decbin($f2),8,"0",STR_PAD_LEFT).str_pad(decbin($f3),8,"0",STR_PAD_LEFT).str_pad(decbin($f4),8,"0",STR_PAD_LEFT);
+  list($f1,$f2,$f3,$f4) = explode(".", $ip);
+  $ip_bin = str_pad(decbin($f1),8,"0",STR_PAD_LEFT).str_pad(decbin($f2),8,"0",STR_PAD_LEFT).str_pad(decbin($f3),8,"0",STR_PAD_LEFT).str_pad(decbin($f4),8,"0",STR_PAD_LEFT);
+  for($i=0;$i<32;$i++){
+    $network_bin .= substr($netmask_bin,$i,1) * substr($ip_bin,$i,1);
+  }
+  $network_bin = wordwrap($network_bin, 8, ".", 1);
+  list($f1,$f2,$f3,$f4) = explode(".", trim($network_bin));
+  return bindec($f1).".".bindec($f2).".".bindec($f3).".".bindec($f4);
+}
+
+function broadcast($ip, $netmask){
+  $netmask = $this->netmask($netmask);
+  $binary_netmask = $this->binary_netmask($netmask);
+  list($f1,$f2,$f3,$f4) = explode(".", $ip);
+  $ip_bin = str_pad(decbin($f1),8,"0",STR_PAD_LEFT).str_pad(decbin($f2),8,"0",STR_PAD_LEFT).str_pad(decbin($f3),8,"0",STR_PAD_LEFT).str_pad(decbin($f4),8,"0",STR_PAD_LEFT);
+  $broadcast_bin = str_pad(substr($ip_bin, 0, $binary_netmask),32,"1",STR_PAD_RIGHT);
+  $broadcast_bin = wordwrap($broadcast_bin, 8, ".", 1);
+  list($f1,$f2,$f3,$f4) = explode(".", trim($broadcast_bin));
+  return bindec($f1).".".bindec($f2).".".bindec($f3).".".bindec($f4);
+}
+
+function network_info(){
+  $dist = $this->server_conf["dist"];
+  ob_start();
+  passthru("ifconfig");
+  $output = ob_get_contents();
+  ob_end_clean();
+  $lines = explode("\n", $output);
+  foreach($lines as $line){
+    $elms = explode(" ", $line);
+    if(trim($elms[0]) != "" && substr($elms[0],0,1) != "\t"){
+      $elms[0] = trim($elms[0]);
+      if(strstr($dist, "freebsd")) $elms[0] = substr($elms[0],0,-1);
+      $interfaces[] = $elms[0];
+    }
+  }
+  if(!empty($interfaces)){
+    foreach($interfaces as $interface){
+      ob_start();
+      if(!strstr($dist, "freebsd")){
+        passthru("ifconfig ".$interface." | grep -iw 'inet' | cut -f2 -d: | cut -f1 -d' '");
+      }else {
+        passthru("ifconfig ".$interface." | grep -iw 'inet' | grep -iv 'inet6' | cut -f2 -d' '");
+      }
+      $output = trim(ob_get_contents());
+      ob_end_clean();
+      if($output != ""){
+        $ifconfig["INTERFACE"][$interface] = $output;
+        $ifconfig["IP"][$output] = $interface;
+      }
+    }
+    if(!empty($ifconfig)){
+      return $ifconfig;
+    } else {
+      return false;
+    }
+  } else {
+    return false;
+  }
+}
+
+function network_config(){
+  $ifconfig = $this->network_info();
+  if($ifconfig){
+    $main_interface = $ifconfig["IP"][$this->server_conf["server_ip"]];
+    if(strstr($main_interface, ":")){
+      $parts = explode(":", $main_interface);
+      $main_interface = trim($parts[0]);
+    }
+    if($main_interface != ""){
+      $ips = $this->data["isp_server_ip"];
+      if(!empty($ips)){
+        foreach($ips as $ip){
+          if(!isset($ifconfig["IP"][$ip["server_ip"]])){
+            $to_set[] = $ip["server_ip"];
+          } else {
+            unset($ifconfig["IP"][$ip["server_ip"]]);
+          }
+        }
+        if(!empty($ifconfig["IP"])){
+          foreach($ifconfig["IP"] as $key => $val){
+            if(!strstr($val, "lo") && !strstr($val, "lp") && strstr($val, $main_interface)){
+              exec("ifconfig ".$val." down &> /dev/null");
+              unset($ifconfig["INTERFACE"][$val]);
+            }
+          }
+        }
+        if(!empty($to_set)){
+         foreach($to_set as $to){
+           $i = 0;
+           while($i >= 0){
+             if(isset($ifconfig["INTERFACE"][$main_interface.":".$i])){
+               $i++;
+             } else {
+               $new_interface = $main_interface.":".$i;
+               $i = -1;
+             }
+           }
+           exec("ifconfig ".$new_interface." ".$to." netmask ".$this->server_conf["server_netzmaske"]." up &> /dev/null");
+           $ifconfig["INTERFACE"][$new_interface] = $to;
+          }
+        }
+      }
+    }
+  }
+}
+
+function quota_dirs(){
+  global $app;
+  $content = $app->file->unix_nl($app->file->no_comments("/etc/fstab"));
+  $lines = explode("\n", $content);
+  foreach($lines as $line){
+    $line = trim($line);
+    if($line != ""){
+      $elms = explode("\t", $line);
+      foreach($elms as $elm){
+        if(trim($elm) != "") $f[] = $elm;
+      }
+      if(!empty($f) && stristr($f[3], "userquota") && stristr($f[3], "groupquota")){
+        $q_dirs[] = trim($f[1]);
+      }
+      unset($f);
+    }
+  }
+  if(!empty($q_dirs)){
+    return $q_dirs;
+  } else {
+    return false;
+  }
+}
+
+function make_trashscan(){
+  global $app;
+  //trashscan erstellen
+  // Template Öffnen
+  $app->tpl->clear_all();
+  $app->tpl->define( array(table    => "trashscan.master"));
+
+  if(!isset($this->server_conf["virusadmin"]) || trim($this->server_conf["virusadmin"]) == "") $this->server_conf["virusadmin"] = "admispconfig@localhost";
+  if(substr($this->server_conf["virusadmin"],0,1) == "#"){
+    $notify = "no";
+  } else {
+    $notify = "yes";
+  }
+
+  // Variablen zuweisen
+  $app->tpl->assign( array(VIRUSADMIN => $this->server_conf["virusadmin"],
+                           NOTIFICATION => $notify));
+
+  $app->tpl->parse(TABLE, table);
+
+  $trashscan_text = $app->tpl->fetch();
+
+  $datei = "/home/admispconfig/ispconfig/tools/clamav/bin/trashscan";
+  $app->file->wf($datei, $trashscan_text);
+
+  exec("chown admispconfig:admispconfig $datei &> /dev/null");
+  exec("chmod 755 $datei");
+}
+
+function get_time(){
+  $addr = "http://www.ispconfig.org/";
+  $timeout = 1;
+  $url_parts = parse_url($addr);
+  $path = $url_parts["path"];
+  $port = 80;
+  $urlHandle = @fsockopen($url_parts["host"], $port, $errno, $errstr, $timeout);
+  if ($urlHandle){
+    socket_set_timeout($urlHandle, $timeout);
+
+    $urlString = "GET $path HTTP/1.0\r\nHost: ".$url_parts["host"]."\r\nConnection: Keep-Alive\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n";
+    if ($user) $urlString .= "Authorization: Basic ".base64_encode("$user:$pass")."\r\n";
+    $urlString .= "\r\n";
+    fputs($urlHandle, $urlString);
+
+    $month["Jan"] = "01";
+    $month["Feb"] = "02";
+    $month["Mar"] = "03";
+    $month["Apr"] = "04";
+    $month["May"] = "05";
+    $month["Jun"] = "06";
+    $month["Jul"] = "07";
+    $month["Aug"] = "08";
+    $month["Sep"] = "09";
+    $month["Oct"] = "10";
+    $month["Nov"] = "11";
+    $month["Dec"] = "12";
+    $c = 0;
+    $l = 0;
+    $startzeit = time();
+    while(!feof($urlHandle) && $c < 2 && $l == 0){
+      $line = trim(fgets($urlHandle,128));
+      $response .= $line;
+      $c = time() - $startzeit;
+      if($line == "" || substr($line, 0, 5) == "Date:") $l += 1; // nur den Header auslesen
+      if(substr($line, 0, 5) == "Date:"){
+        $parts = explode(" ", $line);
+        $tag = $parts[2];
+        $monat = $month[$parts[3]];
+        $jahr = $parts[4];
+        list($stunde, $minute, $sekunde) = explode(":", $parts[5]);
+        $timestamp = mktime($stunde,$minute,$sekunde,$monat,$tag,$jahr);
+      }
+    }
+
+    @fclose($urlHandle);
+
+    return $timestamp;
+  } else {
+    @fclose($urlHandle);
+    return false;
+  }
+}
+
+}
+?>
\ No newline at end of file
diff --git a/server/lib/classes/tpl.inc.php b/server/lib/classes/tpl.inc.php
new file mode 100644
index 000000000..e16f0dcb1
--- /dev/null
+++ b/server/lib/classes/tpl.inc.php
@@ -0,0 +1,1499 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0                                                      |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2002 Active Fish Group                                 |
+// +----------------------------------------------------------------------+
+// | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk>                     |
+// +----------------------------------------------------------------------+
+//
+// $Id: class.tpl.inc.php,v 1.1 2003/07/08 12:31:10 platinum Exp $
+
+// check to avoid multiple including of class
+if (!defined('vlibTemplateClassLoaded')) {
+    define('vlibTemplateClassLoaded', 1);
+
+   	include_once ($conf['classpath'].'/tpl_error.inc.php');
+   	include_once ($conf['classpath'].'/tpl_ini.inc.php');
+
+    /**
+     * vlibTemplate is a class used to seperate PHP and HTML.
+     * For instructions on how to use vlibTemplate, see the
+     * vlibTemplate.html file, located in the 'docs' directory.
+     *
+     * @since 07/03/2002
+     * @author Kelvin Jones <kelvin@kelvinjones.co.uk>
+     * @package vLIB
+     * @access public
+     * @see vlibTemplate.html
+     */
+
+    class tpl {
+
+    /*-----------------------------------------------------------------------------\
+    |                                 ATTENTION                                    |
+    |  Do not touch the following variables. vlibTemplate will not work otherwise. |
+    \-----------------------------------------------------------------------------*/
+
+        var $OPTIONS = array(
+                        'MAX_INCLUDES'          =>   10,
+                        'TEMPLATE_DIR'          => null,
+                        'GLOBAL_VARS'           => null,
+                        'GLOBAL_CONTEXT_VARS'   => null,
+                        'LOOP_CONTEXT_VARS'     => null,
+                        'SET_LOOP_VAR'          => null,
+                        'DEFAULT_ESCAPE'        => null,
+                        'STRICT'                => null,
+                        'CASELESS'              => null,
+                        'UNKNOWNS'              => null,
+                        'TIME_PARSE'            => null,
+                        'ENABLE_PHPINCLUDE'     => null,
+                        'INCLUDE_PATHS'         => array(),
+                        'CACHE_DIRECTORY'       => null,
+                        'CACHE_LIFETIME'        => null,
+                        'CACHE_EXTENSION'       => null
+                             );
+
+        /** open and close tags used for escaping */
+        var $ESCAPE_TAGS = array(
+                            'html' => array('open' => 'htmlspecialchars('
+                                            ,'close'=> ', ENT_QUOTES)'),
+                            'url' => array('open' => 'urlencode('
+                                            ,'close'=> ')'),
+                            'rawurl'=>array('open' => 'rawurlencode('
+                                            ,'close'=> ')'),
+                            'sq' => array('open' => 'addcslashes('
+                                            ,'close'=> ", \"'\")"),
+                            'dq' => array('open' => 'addcslashes('
+                                            ,'close'=> ", '\"')"),
+                            '1' => array('open' => 'htmlspecialchars('
+                                            ,'close'=> ', ENT_QUOTES)'),
+                            '0' => array('open' => ''
+                                            ,'close'=> ''),
+                            'none' => array('open' => ''
+                                            ,'close'=> ''),
+                            'hex'        => array('open' => '$this->_escape_hex(',
+                                            'close'=> ', false)'),
+                            'hexentity'  => array('open' => '$this->_escape_hex(',
+                                            'close'=> ', true)')
+                                );
+
+        /** open and close tags used for formatting */
+        var $FORMAT_TAGS = array(
+                            'strtoupper' => array('open' => 'strtoupper(',
+                                            'close'=> ')'),
+                            'uc'         => array('open' => 'strtoupper(',
+                                            'close'=> ')'),
+                            'strtolower' => array('open' => 'strtolower(',
+                                            'close'=> ')'),
+                            'lc'         => array('open' => 'strtolower(',
+                                            'close'=> ')'),
+                            'ucfirst'    => array('open' => 'ucfirst(',
+                                            'close'=> ')'),
+                            'lcucfirst'  => array('open' => 'ucfirst(strtolower(',
+                                            'close'=> '))'),
+                            'ucwords'    => array('open' => 'ucwords(',
+                                            'close'=> ')'),
+                            'lcucwords'  => array('open' => 'ucwords(strtolower(',
+                                            'close'=> '))')
+                                );
+
+        /** operators allowed when using extended TMPL_IF syntax */
+        var $allowed_if_ops = array('==','!=','<>','<','>','<=','>=');
+
+        /** dbs allowed by vlibTemplate::setDbLoop(). */
+        var $allowed_loop_dbs = array('MYSQL','POSTGRESQL','INFORMIX','INTERBASE','INGRES',
+                                      'MSSQL','MSQL','OCI8','ORACLE','OVRIMOS','SYBASE');
+
+        /** root directory of vlibTemplate automagically filled in */
+        var $VLIBTEMPLATE_ROOT = null;
+
+        /** contains current directory used when doing recursive include */
+        var $_currentincludedir = array();
+
+        /** current depth of includes */
+        var $_includedepth = 0;
+
+        /** full path to tmpl file */
+        var $_tmplfilename = null;
+
+        /** file data before it's parsed */
+        var $_tmplfile = null;
+
+        /** parsed version of file, ready for eval()ing */
+        var $_tmplfilep = null;
+
+        /** eval()ed version ready for printing or whatever */
+        var $_tmploutput = null;
+
+        /** array for variables to be kept */
+        var $_vars = array();
+
+        /** array where loop variables are kept */
+        var $_arrvars = array();
+
+        /** array which holds the current namespace during parse */
+        var $_namespace = array();
+
+        /** variable is set to true once the template is parsed, to save re-parsing everything */
+        var $_parsed = false;
+
+        /** array holds all unknowns vars */
+        var $_unknowns = array();
+
+        /** microtime when template parsing began */
+        var $_firstparsetime = null;
+
+        /** total time taken to parse template */
+        var $_totalparsetime = null;
+
+        /** name of current loop being passed in */
+        var $_currloopname = null;
+
+        /** rows with the above loop */
+        var $_currloop = array();
+
+        /** define vars to avoid warnings */
+        var $_debug = null;
+        var $_cache = null;
+        
+        /** array which holds the dynamic Includes */
+        var $_dyninclude = array();
+    /*-----------------------------------------------------------------------------\
+    |                           public functions                                   |
+    \-----------------------------------------------------------------------------*/
+		
+		
+        /**
+         * FUNCTION: newTemplate
+         *
+         * Usually called by the class constructor.
+         * Stores the filename in $this->_tmplfilename.
+         * Raises an error if the template file is not found.
+         *
+         * @param string $tmplfile full path to template file
+         * @return boolean true
+         * @access public
+         */
+        function newTemplate ($tmplfile) {
+            if (!$tfile = $this->_fileSearch($tmplfile)) vlibTemplateError::raiseError('VT_ERROR_NOFILE',KILL,$tmplfile);
+
+            // make sure that any parsing vars are cleared for the new template
+            $this->_tmplfile = null;
+            $this->_tmplfilep = null;
+            $this->_tmploutput = null;
+            $this->_parsed = false;
+            $this->_unknowns = array();
+            $this->_firstparsetime = null;
+            $this->_totalparsetime = null;
+
+            // reset debug module
+            if ($this->_debug) $this->_debugReset();
+
+            $this->_tmplfilename = $tfile;
+            return true;
+        }
+
+        /**
+         * FUNCTION: setVar
+         *
+         * Sets variables to be used by the template
+         * If $k is an array, then it will treat it as an associative array
+         * using the keys as variable names and the values as variable values.
+         *
+         * @param mixed $k key to define variable name
+         * @param mixed $v variable to assign to $k
+         * @return boolean true/false
+         * @access public
+         */
+        function setVar ($k,$v=null) {
+            if (is_array($k)) {
+                foreach($k as $key => $value){
+                    $key = ($this->OPTIONS['CASELESS']) ? strtolower(trim($key)) : trim($key);
+                    if (preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $key) && $value !== null ) {
+                        $this->_vars[$key] = $value;
+                    }
+                }
+            }
+            else {
+                if (preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $k) && $v !== null) {
+                    if ($this->OPTIONS['CASELESS']) $k = strtolower($k);
+                    $this->_vars[trim($k)] = $v;
+                }
+                else {
+                    return false;
+                }
+            }
+            return true;
+        }
+        
+        /**
+         * FUNCTION: setInclude
+         *
+         * Sets dynamic includes to be used by the template
+         * If $k is an array, then it will treat it as an associative array
+         * using the keys as variable names and the values as variable values.
+         *
+         * @param mixed $k key to define variable name
+         * @param mixed $v variable to assign to $k
+         * @return boolean true/false
+         * @access public
+         */
+        
+        function setInclude($k, $v = null) {
+        	if(is_array($k)) {
+				foreach($k as $key => $val) {
+					$this->_dyninclude[$key] = $val;
+				}
+			} else {
+				$this->_dyninclude[$k] = $v;
+			}
+			
+			return true;
+		}
+
+        /**
+         * FUNCTION: unsetVar
+         *
+         * Unsets a variable which has already been set
+         * Parse in all vars wanted for deletion in seperate parametres
+         *
+         * @param string var name to remove use: vlibTemplate::unsetVar(var[, var..])
+         * @return boolean true/false returns true unless called with 0 params
+         * @access public
+         */
+        function unsetVar () {
+            $num_args = func_num_args();
+            if ($num_args < 1)  return false;
+
+            for ($i = 0; $i < $num_args; $i++) {
+                $var = func_get_arg($i);
+                if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
+                if (!preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $var)) continue;
+                unset($this->_vars[$var]);
+            }
+            return true;
+        }
+
+        /**
+         * FUNCTION: getVars
+         *
+         * Gets all vars currently set in global namespace.
+         *
+         * @return array
+         * @access public
+         */
+        function getVars () {
+            if (empty($this->_vars)) return false;
+            return $this->_vars;
+        }
+
+        /**
+         * FUNCTION: getVar
+         *
+         * Gets a single var from the global namespace
+         *
+         * @return var
+         * @access public
+         */
+        function getVar ($var) {
+            if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
+            if (empty($var) || !isset($this->_vars[$var])) return false;
+            return $this->_vars[$var];
+        }
+
+        /**
+         * FUNCTION: setContextVars
+         *
+         * sets the GLOBAL_CONTEXT_VARS
+         *
+         * @return true
+         * @access public
+         */
+        function setContextVars () {
+            $_phpself = @$GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'];
+            $_pathinfo = @$GLOBALS['HTTP_SERVER_VARS']['PATH_INFO'];
+            $_request_uri = @$GLOBALS['HTTP_SERVER_VARS']['REQUEST_URI'];
+            $_qs   = @$GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING'];
+
+            // the following fixes bug of $PHP_SELF on Win32 CGI and IIS.
+            $_self = (!empty($_pathinfo)) ? $_pathinfo : $_phpself;
+            $_uri  = (!empty($_request_uri)) ? $_request_uri : $_self.'?'.$_qs;
+
+            $this->setvar('__SELF__', $_self);
+            $this->setvar('__REQUEST_URI__', $_uri);
+            return true;
+        }
+
+        /**
+         * FUNCTION: setLoop
+         *
+         * Builds the loop construct for use with <TMPL_LOOP>.
+         *
+         * @param string $k string to define loop name
+         * @param array $v array to assign to $k
+         * @return boolean true/false
+         * @access public
+         */
+        function setLoop ($k,$v) {
+            if (is_array($v) && preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $k)) {
+                $k = ($this->OPTIONS['CASELESS']) ? strtolower(trim($k)) : trim($k);
+                $this->_arrvars[$k] = array();
+                if ($this->OPTIONS['SET_LOOP_VAR'] && !empty($v)) $this->setvar($k, 1);
+                if (($this->_arrvars[$k] = $this->_arrayBuild($v)) == false) {
+                    vlibTemplateError::raiseError('VT_WARNING_INVALID_ARR',WARNING,$k);
+                }
+            }
+            return true;
+        }
+
+        /**
+         * FUNCTION: setDbLoop [** EXPERIMENTAL **]
+         *
+         * Function to create a loop from a Db result resource link.
+         *
+         * @param string $loopname to commit loop. If not set, will use last loopname set using newLoop()
+         * @param string $result link to a Db result resource
+         * @param string $db_type, type of db that the result resource belongs to.
+         * @return boolean true/false
+         * @access public
+         */
+        function setDbLoop ($loopname, $result, $db_type='MYSQL') {
+            $db_type = strtoupper($db_type);
+            if (!in_array($db_type, $this->allowed_loop_dbs)) {
+                vlibTemplateError::raiseError('VT_WARNING_INVALID_LOOP_DB',WARNING, $db_type);
+                return false;
+            }
+
+            $loop_arr = array();
+            switch ($db_type) {
+
+                case 'MYSQL':
+                    if (get_resource_type($result) != 'mysql result') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while($r = mysql_fetch_assoc($result)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'POSTGRESQL':
+                    if (get_resource_type($result) != 'pgsql result') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+
+                    $nr = (function_exists('pg_num_rows')) ? pg_num_rows($result) : pg_numrows($result);
+
+                    for ($i=0; $i < $nr; $i++) {
+                        $loop_arr[] = pg_fetch_array($result, $i, PGSQL_ASSOC);
+                    }
+                break;
+
+                case 'INFORMIX':
+                    if (!$result) {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while($r = ifx_fetch_row($result, 'NEXT')) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'INTERBASE':
+                    if (get_resource_type($result) != 'interbase result') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while($r = ibase_fetch_row($result)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'INGRES':
+                    if (!$result) {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while($r = ingres_fetch_array(INGRES_ASSOC, $result)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'MSSQL':
+                    if (get_resource_type($result) != 'mssql result') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while($r = mssql_fetch_array($result)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'MSQL':
+                    if (get_resource_type($result) != 'msql result') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while($r = msql_fetch_array($result, MSQL_ASSOC)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'OCI8':
+                    if (get_resource_type($result) != 'oci8 statement') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while(OCIFetchInto($result, &$r, OCI_ASSOC+OCI_RETURN_LOBS)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'ORACLE':
+                    if (get_resource_type($result) != 'oracle Cursor') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while(ora_fetch_into($result, &$r, ORA_FETCHINTO_ASSOC)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'OVRIMOS':
+                    if (!$result) {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+                    while(ovrimos_fetch_into($result, &$r, 'NEXT')) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+
+                case 'SYBASE':
+                    if (get_resource_type($result) != 'sybase-db result') {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
+                        return false;
+                    }
+
+                    while($r = sybase_fetch_array($result)) {
+                        $loop_arr[] = $r;
+                    }
+                break;
+            }
+            $this->setLoop($loopname, $loop_arr);
+            return true;
+        }
+
+        /**
+         * FUNCTION: newLoop
+         *
+         * Sets the name for the curent loop in the 3 step loop process.
+         *
+         * @param string $name string to define loop name
+         * @return boolean true/false
+         * @access public
+         */
+        function newLoop ($loopname) {
+            if (preg_match('/^[a-z_]+[a-z0-9_]*$/i', $loopname)) {
+                $this->_currloopname[$loopname] = $loopname;
+                $this->_currloop[$loopname] = array();
+                return true;
+            }
+            else {
+                return false;
+            }
+        }
+
+        /**
+         * FUNCTION: addRow
+         *
+         * Adds a row to the current loop in the 3 step loop process.
+         *
+         * @param array $row loop row to add to current loop
+         * @param string $loopname loop to which you want to add row, if not set will use last loop set using newLoop().
+         * @return boolean true/false
+         * @access public
+         */
+        function addRow ($row, $loopname=null) {
+            if (!$loopname) $loopname = $this->_currloopname[(count($this->_currloopname)-1)];
+
+            if (!isset($this->_currloop[$loopname]) || empty($this->_currloopname)) {
+                vlibTemplateError::raiseError('VT_WARNING_LOOP_NOT_SET',WARNING);
+                return false;
+            }
+            if (is_array($row)) {
+                $this->_currloop[$loopname][] = $row;
+                return true;
+            }
+            else {
+                return false;
+            }
+        }
+
+        /**
+         * FUNCTION: addLoop
+         *
+         * Completes the 3 step loop process. This assigns the rows and resets
+         * the variables used.
+         *
+         * @param string $loopname to commit loop. If not set, will use last loopname set using newLoop()
+         * @return boolean true/false
+         * @access public
+         */
+        function addLoop ($loopname=null) {
+            if ($loopname == null) { // add last loop used
+                if (!empty($this->_currloop)) {
+                    foreach ($this->_currloop as $k => $v) {
+                        $this->setLoop($k, $v);
+                        unset($this->_currloop[$k]);
+                    }
+                    $this->_currloopname = array();
+                    return true;
+                }
+                else {
+                    return false;
+                }
+            }
+            elseif (!isset($this->_currloop[$loopname]) || empty($this->_currloopname)) { // newLoop not yet envoked
+                    vlibTemplateError::raiseError('VT_WARNING_LOOP_NOT_SET',WARNING);
+                    return false;
+            }
+            else { // add a specific loop
+                $this->setLoop($loopname, $this->_currloop[$loopname]);
+                unset($this->_currloopname[$loopname], $this->_currloop[$loopname]);
+            }
+            return true;
+        }
+
+        /**
+         * FUNCTION: unsetLoop
+         *
+         * Unsets a loop which has already been set.
+         * Can only unset top level loops.
+         *
+         * @param string loop to remove use: vlibTemplate::unsetLoop(loop[, loop..])
+         * @return boolean true/false returns true unless called with 0 params
+         * @access public
+         */
+        function unsetLoop () {
+            $num_args = func_num_args();
+            if ($num_args < 1) return false;
+
+            for ($i = 0; $i < $num_args; $i++) {
+                $var = func_get_arg($i);
+                if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
+                if (!preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $var)) continue;
+                unset($this->_arrvars[$var]);
+            }
+            return true;
+        }
+
+
+        /**
+         * FUNCTION: reset
+         *
+         * Resets the vlibTemplate object. After using vlibTemplate::reset() you must
+         * use vlibTemplate::newTemplate(tmpl) to reuse, not passing in the options array.
+         *
+         * @return boolean true
+         * @access public
+         */
+        function reset () {
+            $this->clearVars();
+            $this->clearLoops();
+            $this->_tmplfilename = null;
+            $this->_tmplfile = null;
+            $this->_tmplfilep = null;
+            $this->_tmploutput = null;
+            $this->_parsed = false;
+            $this->_unknowns = array();
+            $this->_firstparsetime = null;
+            $this->_totalparsetime = null;
+            $this->_currloopname = null;
+            $this->_currloop = array();
+            return true;
+        }
+
+        /**
+         * FUNCTION: clearVars
+         *
+         * Unsets all variables in the template
+         *
+         * @return boolean true
+         * @access public
+         */
+        function clearVars () {
+            $this->_vars = array();
+            return true;
+        }
+
+        /**
+         * FUNCTION: clearLoops
+         *
+         * Unsets all loops in the template
+         *
+         * @return boolean true
+         * @access public
+         */
+        function clearLoops () {
+            $this->_arrvars = array();
+            $this->_currloopname = null;
+            $this->_currloop = array();
+            return true;
+        }
+
+        /**
+         * FUNCTION: clearAll
+         *
+         * Unsets all variables and loops set using setVar/Loop()
+         *
+         * @return boolean true
+         * @access public
+         */
+        function clearAll () {
+            $this->clearVars();
+            $this->clearLoops();
+            return true;
+        }
+
+        /**
+         * FUNCTION: unknownsExist
+         *
+         * Returns true if unknowns were found after parsing.
+         * Function MUST be called AFTER one of the parsing functions to have any relevance.
+         *
+         * @return boolean true/false
+         * @access public
+         */
+        function unknownsExist () {
+            return (!empty($this->_unknowns));
+        }
+
+        /**
+         * FUNCTION: unknowns
+         *
+         * Alias for unknownsExist.
+         *
+         * @access public
+         */
+        function unknowns () {
+            return $this->unknownsExist();
+        }
+
+        /**
+         * FUNCTION: getUnknowns
+         *
+         * Returns an array of all unknown vars found when parsing.
+         * This function is only relevant after parsing a document.
+         *
+         * @return array
+         * @access public
+         */
+        function getUnknowns () {
+            return $this->_unknowns;
+        }
+
+        /**
+         * FUNCTION: setUnknowns
+         *
+         * Sets how you want to handle variables that were found in the
+         * template but not set in vlibTemplate using vlibTemplate::setVar().
+         *
+         * @param  string $arg ignore, remove, print, leave or comment
+         * @return boolean
+         * @access public
+         */
+        function setUnknowns ($arg) {
+            $arg = strtolower(trim($arg));
+            if (preg_match('/^ignore|remove|print|leave|comment$/', $arg)) {
+                $this->OPTIONS['UNKNOWNS'] = $arg;
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * FUNCTION: setPath
+         *
+         * function sets the paths to use when including files.
+         * Use of this function: vlibTemplate::setPath(string path [, string path, ..]);
+         * i.e. if $tmpl is your template object do: $tmpl->setPath('/web/htdocs/templates','/web/htdocs/www');
+         * with as many paths as you like.
+         * if this function is called without any arguments, it will just delete any previously set paths.
+         *
+         * @param string path (mulitple)
+         * @return bool success
+         * @access public
+         */
+        function setPath () {
+            $num_args = func_num_args();
+            if ($num_args < 1) {
+                $this->OPTIONS['INCLUDE_PATHS'] = array();
+                return true;
+            }
+            for ($i = 0; $i < $num_args; $i++) {
+                $thispath = func_get_arg($i);
+                array_push($this->OPTIONS['INCLUDE_PATHS'], realpath($thispath));
+            }
+            return true;
+        }
+
+        /**
+         * FUNCTION: getParseTime
+         *
+         * After using one of the parse functions, this will allow you
+         * access the time taken to parse the template.
+         * see OPTION 'TIME_PARSE'.
+         *
+         * @return float time taken to parse template
+         * @access public
+         */
+        function getParseTime () {
+            if ($this->OPTIONS['TIME_PARSE'] && $this->_parsed) {
+                return $this->_totalparsetime;
+            }
+            return false;
+        }
+
+
+        /**
+         * FUNCTION: fastPrint
+         *
+         * Identical to pparse() except that it uses output buffering w/ gz compression thus
+         * printing the output directly and compressed if poss.
+         * Will possibly if parsing a huge template.
+         *
+         * @access public
+         * @return boolean true/false
+         */
+        function fastPrint () {
+            $ret = $this->_parse('ob_gzhandler');
+            print($this->_tmploutput);
+            return $ret;
+        }
+
+
+        /**
+         * FUNCTION: pparse
+         *
+         * Calls parse, and then prints out $this->_tmploutput
+         *
+         * @access public
+         * @return boolean true/false
+         */
+        function pparse () {
+            if (!$this->_parsed) $this->_parse();
+            print($this->_tmploutput);
+            return true;
+        }
+
+        /**
+         * FUNCTION: pprint
+         *
+         * Alias for pparse()
+         *
+         * @access public
+         */
+        function pprint () {
+            return $this->pparse();
+        }
+
+
+        /**
+         * FUNCTION: grab
+         *
+         * Returns the parsed output, ready for printing, passing to mail() ...etc.
+         * Invokes $this->_parse() if template has not yet been parsed.
+         *
+         * @access public
+         * @return boolean true/false
+         */
+        function grab () {
+            if (!$this->_parsed) $this->_parse();
+            return $this->_tmploutput;
+        }
+
+    /*-----------------------------------------------------------------------------\
+    |                           private functions                                  |
+    \-----------------------------------------------------------------------------*/
+
+        /**
+         * FUNCTION: vlibTemplate
+         *
+         * vlibTemplate constructor.
+         * if $tmplfile has been passed to it, it will send to $this->newTemplate()
+         *
+         * @param string $tmplfile full path to template file
+         * @param array $options see above
+         * @return boolean true/false
+         * @access private
+         */
+        function tpl ($tmplfile=null, $options=null) {
+            if (is_array($tmplfile) && $options == null) {
+                $options = $tmplfile;
+                unset($tmplfile);
+            }
+
+            $this->VLIBTEMPLATE_ROOT = dirname(realpath(__FILE__));
+
+            if (is_array(vlibIni::vlibTemplate())) {
+                foreach (vlibIni::vlibTemplate() as $name => $val) {
+                    $this->OPTIONS[$name] = $val;
+                }
+            }
+
+            if (is_array($options)) {
+                foreach($options as $key => $val) {
+                    $key = strtoupper($key);
+                    if ($key == 'PATH') {
+                        $this->setPath($val);
+                    }
+                    else {
+                        $this->_setOption($key, strtolower($val));
+                    }
+                }
+            }
+            if($tmplfile) $this->newTemplate($tmplfile);
+            if ($this->OPTIONS['GLOBAL_CONTEXT_VARS']) $this->setContextVars();
+            return true;
+        }
+
+        /** FUNCTION: _getData
+         *
+         * function returns the text from the file, or if we're using cache, the text
+         * from the cache file. MUST RETURN DATA.
+         * @param string tmplfile contains path to template file
+         * @param do_eval used for included files. If set then this function must do the eval()'ing.
+         * @access private
+         * @return mixed data/string or boolean
+         */
+        function _getData ($tmplfile, $do_eval=false) {
+            //check the current file depth
+            if ($this->_includedepth > $this->OPTIONS['MAX_INCLUDES'] || $tmplfile == false) {
+                return;
+            }
+            else {
+                if ($this->_debug) array_push ($this->_debugIncludedfiles, $tmplfile);
+                if ($do_eval) {
+                    array_push($this->_currentincludedir, dirname($tmplfile));
+                    $this->_includedepth++;
+                }
+            }
+
+
+            if($this->_cache && $this->_checkCache($tmplfile)) { // cache exists so lets use it
+                $data = fread($fp = fopen($this->_cachefile, 'r'), filesize($this->_cachefile));
+                fclose($fp);
+            }
+            else { // no cache lets parse the file
+                $data = fread($fp = fopen($tmplfile, 'r'), filesize($tmplfile));
+                fclose($fp);
+
+                $regex = '/(<|<\/|{|{\/|<!--|<!--\/){1}\s*';
+                $regex.= 'tmpl_([\w]+)\s*';
+                $regex.= '(?:';
+                $regex.=    '(?:';
+                $regex.=        '(name|format|escape|op|value|file)';
+                $regex.=        '\s*=\s*';
+                $regex.=    ')?';
+                $regex.=    '(?:[\"\'])?';
+                $regex.=    '((?<=[\"\'])';
+                $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
+                $regex.=    '[\"\']?';
+                $regex.= ')?\s*';
+                $regex.= '(?:';
+                $regex.=    '(?:';
+                $regex.=        '(name|format|escape|op|value)';
+                $regex.=        '\s*=\s*';
+                $regex.=    ')';
+                $regex.=    '(?:[\"\'])?';
+                $regex.=    '((?<=[\"\'])';
+                $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
+                $regex.=    '[\"\']?';
+                $regex.= ')?\s*';
+                $regex.= '(?:';
+                $regex.=    '(?:';
+                $regex.=        '(name|format|escape|op|value)';
+                $regex.=        '\s*=\s*';
+                $regex.=    ')';
+                $regex.=    '(?:[\"\'])?';
+                $regex.=    '((?<=[\"\'])';
+                $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
+                $regex.=    '[\"\']?';
+                $regex.= ')?\s*';
+                $regex.= '(?:>|\/>|}|-->){1}';
+				$regex.= '/ie';
+                //$regex.= '([\r\n|\n|\r])?/ie';
+                $data = preg_replace($regex,"\$this->_parseTag(array('\\0','\\1','\\2','\\3','\\4','\\5','\\6','\\7','\\8','\\9'));",$data);
+
+                if ($this->_cache) { // add cache if need be
+                    $this->_createCache($data);
+                }
+            }
+
+            // now we must parse the $data and check for any <tmpl_include>'s
+            if ($this->_debug) $this->doDebugWarnings(file($tmplfile), $tmplfile);
+
+            if ($do_eval) {
+                $success = @eval('?>'.$data.'<?php return 1;');
+                $this->_includedepth--;
+                array_pop($this->_currentincludedir);
+                return $success;
+            }
+            else {
+                return $data;
+            }
+
+        }
+
+        /**
+         * FUNCTION: _fileSearch
+         *
+         * Searches for all possible instances of file { $file }
+         *
+         * @param string $file path of file we're looking for
+         * @access private
+         * @return mixed fullpath to file or boolean false
+         */
+        function _fileSearch ($file) {
+            $filename = basename($file);
+            $filepath = dirname($file);
+
+            // check fullpath first..
+            $fullpath = $filepath.'/'.$filename;
+            if (is_file($fullpath)) return $fullpath;
+
+            // ..then check for relative path for current directory..
+            if (!empty($this->_currentincludedir)) {
+                $currdir = $this->_currentincludedir[(count($this->_currentincludedir) -1)];
+                $relativepath = realpath($currdir.'/'.$filepath.'/'.$filename);
+                if (is_file($relativepath)) {
+                    array_push ($this->_currentincludedir, dirname($relativepath));
+                    return $relativepath;
+                }
+            }
+
+            // ..then check for relative path for all additional given paths..
+            if (!empty($this->OPTIONS['INCLUDE_PATHS'])) {
+                foreach ($this->OPTIONS['INCLUDE_PATHS'] as $currdir) {
+                    $relativepath = realpath($currdir.'/'.$filepath.'/'.$filename);
+                    if (is_file($relativepath)) {
+                        return $relativepath;
+                    }
+                }
+            }
+
+            // ..then check path from TEMPLATE_DIR..
+            if (!empty($this->OPTIONS['TEMPLATE_DIR'])) {
+                $fullpath = realpath($this->OPTIONS['TEMPLATE_DIR'].'/'.$filepath.'/'.$filename);
+                if (is_file($fullpath)) return $fullpath;
+            }
+
+            // ..then check relative path from executing php script..
+            $fullpath = realpath($filepath.'/'.$filename);
+            if (is_file($fullpath)) return $fullpath;
+
+            // ..then check path from template file.
+            if (!empty($this->VLIBTEMPLATE_ROOT)) {
+                $fullpath = realpath($this->VLIBTEMPLATE_ROOT.'/'.$filepath.'/'.$filename);
+                if (is_file($fullpath)) return $fullpath;
+            }
+
+            return false; // uh oh, file not found
+        }
+
+        /**
+         * FUNCTION: _arrayBuild
+         *
+         * Modifies the array $arr to add Template variables, __FIRST__, __LAST__ ..etc
+         * if $this->OPTIONS['LOOP_CONTEXT_VARS'] is true.
+         * Used by $this->setloop().
+         *
+         * @param array $arr
+         * @return array new look array
+         * @access private
+         */
+        function _arrayBuild ($arr) {
+            if (is_array($arr) && !empty($arr)) {
+                $arr = array_values($arr); // to prevent problems w/ non sequential arrays
+                for ($i = 0; $i < count($arr); $i++) {
+                    if(!is_array($arr[$i]))  return false;
+                    foreach ($arr[$i] as $k => $v) {
+                        unset($arr[$i][$k]);
+                        if ($this->OPTIONS['CASELESS']) $k = strtolower($k);
+                        if (preg_match('/^[0-9]+$/', $k)) $k = '_'.$k;
+
+                        if (is_array($v)) {
+                            if (($arr[$i][$k] = $this->_arrayBuild($v)) == false) return false;
+                        }
+                        else { // reinsert the var
+                            $arr[$i][$k] = $v;
+                        }
+                    }
+                    if ($this->OPTIONS['LOOP_CONTEXT_VARS']) {
+                        if ($i == 0) $arr[$i]['__FIRST__'] = true;
+                        if (($i + 1) == count($arr)) $arr[$i]['__LAST__'] = true;
+                        if ($i != 0 && (($i + 1) < count($arr))) $arr[$i]['__INNER__'] = true;
+                        if (is_int(($i+1) / 2))  $arr[$i]['__EVEN__'] = true;
+                        if (!is_int(($i+1) / 2))  $arr[$i]['__ODD__'] = true;
+                        $arr[$i]['__ROWNUM__'] = ($i + 1);
+                    }
+                }
+                return $arr;
+            }
+            elseif (empty($arr)) {
+                return true;
+            }
+        }
+
+        /**
+         * FUNCTION: _parseIf
+         * returns a string used for parsing in tmpl_if statements.
+         *
+         * @param string $varname
+         * @param string $value
+         * @param string $op
+         * @param string $namespace current namespace
+         * @access private
+         * @return string used for eval'ing
+         */
+        function _parseIf ($varname, $value=null, $op=null, $namespace=null) {
+            if (isset($namespace)) $namespace = substr($namespace, 0, -1);
+            $comp_str = ''; // used for extended if statements
+
+            // work out what to put on the end id value="whatever" is used
+            if (isset($value)) {
+
+                // add the correct operator depending on whether it's been specified or not
+                if (!empty($op)) {
+                    if (in_array($op, $this->allowed_if_ops)) {
+                        $comp_str .= $op;
+                    }
+                    else {
+                        vlibTemplateError::raiseError('VT_WARNING_INVALID_IF_OP', WARNING, $op);
+                    }
+                }
+                else {
+                    $comp_str .= '==';
+                }
+
+                // now we add the value, if it's numeric, then we leave the quotes off
+                if (is_numeric($value)) {
+                    $comp_str .= $value;
+                }
+                else {
+                    $comp_str .= '\''.$value.'\'';
+                }
+            }
+
+            if (count($this->_namespace) == 0 || $namespace == 'global') return '$this->_vars[\''.$varname.'\']'.$comp_str;
+            $retstr = '$this->_arrvars';
+            $numnamespaces = count($this->_namespace);
+            for ($i=0; $i < $numnamespaces; $i++) {
+                if ($this->_namespace[$i] == $namespace || (($i + 1) == $numnamespaces && !empty($namespace))) {
+                    $retstr .= "['".$namespace."'][\$_".$i."]";
+                    break 1;
+                }
+                else {
+                    $retstr .= "['".$this->_namespace[$i]."'][\$_".$i."]";
+                }
+            }
+            if ($this->OPTIONS['GLOBAL_VARS'] && empty($namespace)) {
+                return '(('.$retstr.'[\''.$varname.'\'] !== null) ? '.$retstr.'[\''.$varname.'\'] : $this->_vars[\''.$varname.'\'])'.$comp_str;
+            }
+            else {
+                return $retstr."['".$varname."']".$comp_str;
+            }
+        }
+
+
+        /**
+         * FUNCTION: _parseLoop
+         * returns a string used for parsing in tmpl_loop statements.
+         *
+         * @param string $varname
+         * @access private
+         * @return string used for eval'ing
+         */
+        function _parseLoop ($varname) {
+            array_push($this->_namespace, $varname);
+            $tempvar = count($this->_namespace) - 1;
+            $retstr = "for (\$_".$tempvar."=0 ; \$_".$tempvar." < count(\$this->_arrvars";
+            for ($i=0; $i < count($this->_namespace); $i++) {
+                $retstr .= "['".$this->_namespace[$i]."']";
+                if ($this->_namespace[$i] != $varname) $retstr .= "[\$_".$i."]";
+            }
+            return $retstr."); \$_".$tempvar."++) {";
+        }
+
+        /**
+         * FUNCTION: _parseVar
+         *
+         * returns a string used for parsing in tmpl_var statements.
+         *
+         * @param string $wholetag
+         * @param string $tag
+         * @param string $varname
+         * @param string $escape
+         * @param string $format
+         * @param string $namespace
+         * @access private
+         * @return string used for eval'ing
+         */
+        function _parseVar ($wholetag, $tag, $varname, $escape, $format, $namespace) {
+            if (!empty($namespace)) $namespace = substr($namespace, 0, -1);
+            $wholetag = stripslashes($wholetag);
+
+            if (count($this->_namespace) == 0 || $namespace == 'global') {
+                $var1 = '$this->_vars[\''.$varname.'\']';
+            }
+            else {
+                $var1build = "\$this->_arrvars";
+                $numnamespaces = count($this->_namespace);
+                for ($i=0; $i < $numnamespaces; $i++) {
+                    if ($this->_namespace[$i] == $namespace || (($i + 1) == $numnamespaces && !empty($namespace))) {
+                        $var1build .= "['".$namespace."'][\$_".$i."]";
+                        break 1;
+                    }
+                    else {
+                        $var1build .= "['".$this->_namespace[$i]."'][\$_".$i."]";
+                    }
+                }
+                $var1 = $var1build . "['$varname']";
+                if ($this->OPTIONS['GLOBAL_VARS'] && empty($namespace)) {
+                    $var2 = '$this->_vars[\''.$varname.'\']';
+                }
+            }
+
+            $beforevar = '';
+            $aftervar  = '';
+            if (!empty($escape)&& isset($this->ESCAPE_TAGS[$escape])) {
+                $beforevar .= $this->ESCAPE_TAGS[$escape]['open'];
+                $aftervar   = $this->ESCAPE_TAGS[$escape]['close'] . $aftervar;
+            }
+
+            if (!empty($format)&& isset($this->FORMAT_TAGS[$format])) {
+                $beforevar .= $this->FORMAT_TAGS[$format]['open'];
+                $aftervar   = $this->FORMAT_TAGS[$format]['close'] . $aftervar;
+            }
+
+            // build return values
+            $retstr  = 'if ('.$var1.' !== null) { ';
+            $retstr .= 'print('.$beforevar.$var1.$aftervar.'); ';
+            $retstr .= '}';
+
+            if (@$var2) {
+                $retstr .= ' elseif ('.$var2.' !== null) { ';
+                $retstr .= 'print('.$beforevar.$var2.$aftervar.'); ';
+                $retstr .= '}';
+            }
+
+            switch (strtolower($this->OPTIONS['UNKNOWNS'])) {
+                case 'comment':
+                    $comment = addcslashes('<!-- unknown variable '.ereg_replace('<!--|-->', '', $wholetag).'//-->', '"');
+                    $retstr .= ' else { print("'.$comment.'"); $this->_setUnknown("'.$varname.'"); }';
+                    return $retstr;
+                break;
+                case 'leave':
+                    $retstr .= ' else { print("'.addcslashes($wholetag, '"').'"); $this->_setUnknown("'.$varname.'"); }';
+                    return $retstr;
+                break;
+                case 'print':
+                    $retstr .= ' else { print("'.htmlspecialchars($wholetag, ENT_QUOTES).'"); $this->_setUnknown("'.$varname.'"); }';
+                    return $retstr;
+                break;
+
+                case 'ignore':
+                    return $retstr;
+                break;
+                case 'remove':
+                default:
+                    $retstr .= ' else { $this->_setUnknown("'.$varname.'"); }';
+                    return $retstr;
+                break;
+            }
+        }
+
+        /**
+         * FUNCTION: _parseTag
+         * takes values from preg_replace in $this->_intparse() and determines
+         * the replace string.
+         *
+         * @param array $args array of all matches found by preg_replace
+         * @access private
+         * @return string replace values
+         */
+        function _parseTag ($args) {
+            $wholetag = $args[0];
+            $openclose = $args[1];
+            $tag = strtolower($args[2]);
+            $newline = $args[9];
+			//echo "1#$newline#2";
+
+            if ($tag == 'else') return '<?php } else { ?>'.$newline;
+            if ($tag == 'tmpl_include') return $wholetag; // ignore tmpl_include tags
+
+            if (preg_match("/^<\/|{\/|<!--\/$/s", $openclose) || preg_match("/^end[if|loop|unless|comment]$/", $tag)) {
+                if ($tag == 'loop' || $tag == 'endloop') array_pop($this->_namespace);
+                if ($tag == 'comment' || $tag == 'endcomment') {
+                    return '<?php */ ?>'.$newline;
+                }
+                else {
+                    return '<?php } ?>'.$newline;
+                }
+            }
+
+            // arrange attributes
+            for ($i=3; $i < 8; $i=($i+2)) {
+                if (empty($args[$i]) && empty($args[($i+1)])) break;
+                $key = (empty($args[$i])) ? 'name' : strtolower($args[$i]);
+                if ($key == 'name' && preg_match('/^(php)?include$/', $tag)) $key = 'file';
+                $$key = $args[($i+1)];
+            }
+
+            $var = ($this->OPTIONS['CASELESS']) ? strtolower($name) : $name;
+
+            if ($this->_debug && !empty($var)) {
+                if (preg_match("/^global\.([A-Za-z_]+[_A-Za-z0-9]*)$/", $var, $matches)) $var2 = $matches[1];
+                if (empty($this->_debugTemplatevars[$tag])) $this->_debugTemplatevars[$tag] = array();
+                if (!isset($var2)) $var2 = $var;
+                if (!in_array($var2, $this->_debugTemplatevars[$tag])) array_push($this->_debugTemplatevars[$tag], $var2);
+            }
+
+            if (preg_match("/^([A-Za-z_]+[_A-Za-z0-9]*(\.)+)?([A-Za-z_]+[_A-Za-z0-9]*)$/", $var, $matches)) {
+                $var = $matches[3];
+                $namespace = $matches[1];
+            }
+
+
+            // return correct string (tag dependent)
+            switch ($tag) {
+                case 'var':
+                    if (empty($escape) && (!empty($this->OPTIONS['DEFAULT_ESCAPE']) && strtolower($this->OPTIONS['DEFAULT_ESCAPE']) != 'none')) {
+                        $escape = strtolower($this->OPTIONS['DEFAULT_ESCAPE']);
+                    }
+                    return '<?php '.$this->_parseVar ($wholetag, $tag, $var, @$escape, @$format, @$namespace).' ?>'.$newline."\n";
+                break;
+
+                case 'if':
+                    return '<?php if ('. $this->_parseIf($var, @$value, @$op, @$namespace) .') { ?>'.$newline;
+                break;
+
+                case 'unless':
+                      return '<?php if (!'. $this->_parseIf($var, @$value, @$op, @$namespace) .') { ?>'.$newline;
+                break;
+
+                case 'elseif':
+                      return '<?php } elseif ('. $this->_parseIf($var, @$value, @$op, @$namespace) .') { ?>'.$newline;
+                break;
+
+                case 'loop':
+                      return '<?php '. $this->_parseLoop($var) .'?>'.$newline;
+                break;
+
+                case 'comment':
+                    if (empty($var)) { // full open/close style comment
+                        return '<?php /* ?>'.$newline;
+                    }
+                    else { // just ignore tag if it was a one line comment
+                        return;
+                    }
+                break;
+
+                case 'phpinclude':
+                	if ($this->OPTIONS['ENABLE_PHPINCLUDE']) {
+                    	return '<?php include(\''.$file.'\'); ?>'.$newline;
+                    }
+                break;
+
+                case 'include':
+                    return '<?php $this->_getData($this->_fileSearch(\''.$file.'\'), 1); ?>';
+                break;
+                
+                case 'dyninclude':
+                    return '<?php $this->_getData($this->_fileSearch($this->_dyninclude[\''.$name.'\']), 1); ?>';
+                break;
+
+                default:
+                    if ($this->OPTIONS['STRICT']) vlibTemplateError::raiseError('VT_ERROR_INVALID_TAG', KILL, htmlspecialchars($wholetag, ENT_QUOTES));
+                break;
+            }
+
+        }
+
+        /**
+         * FUNCTION: _intParse
+         *
+         * Parses $this->_tmplfile into correct format for eval() to work
+         * Called by $this->_parse(), or $this->fastPrint, this replaces all <tmpl_*> references
+         * with their correct php representation, i.e. <tmpl_var title> becomes $this->vars['title']
+         * Sets final parsed file to $this->_tmplfilep.
+         *
+         * @access private
+         * @return boolean true/false
+         */
+        function _intParse () {
+            $mqrt = get_magic_quotes_runtime();
+            set_magic_quotes_runtime(0);
+            $this->_tmplfilep = '?>'.$this->_getData($this->_tmplfilename).'<?php return true;';
+            set_magic_quotes_runtime($mqrt);
+            return true;
+        }
+
+        /**
+         * FUNCTION: _parse
+         *
+         * Calls _intParse, and eval()s $this->tmplfilep
+         * and outputs the results to $this->tmploutput
+         *
+         * @param bool compress whether to compress contents
+         * @access private
+         * @return boolean true/false
+         */
+        function _parse ($compress='') {
+            if (!$this->_parsed) {
+                if ($this->OPTIONS['TIME_PARSE']) $this->_firstparsetime = $this->_getMicroTime();
+
+                $this->_intParse();
+                $this->_parsed = true;
+
+                if ($this->OPTIONS['TIME_PARSE']) $this->_totalparsetime = ($this->_getMicroTime() - $this->_firstparsetime);
+                if ($this->OPTIONS['TIME_PARSE'] && $this->OPTIONS['GLOBAL_CONTEXT_VARS']) $this->setVar('__PARSE_TIME__', $this->getParseTime());
+            }
+			
+			// ob_start($compress);
+            ob_start();
+
+                array_push($this->_currentincludedir, dirname($this->_tmplfilename));
+                $this->_includedepth++;
+                $success = @eval($this->_tmplfilep);
+                $this->_includedepth--;
+                array_pop($this->_currentincludedir);
+
+                if ($this->_debug) $this->doDebug();
+                if (!$success) vlibTemplateError::raiseError('VT_ERROR_PARSE', FATAL);
+                $this->_tmploutput .= ob_get_contents();
+            ob_end_clean();
+			
+			
+			
+            return true;
+        }
+
+        /**
+         * FUNCTION: _setOption
+         *
+         * Sets one or more of the boolean options 1/0, that control certain actions in the template.
+         * Use of this function:
+         * either: vlibTemplate::_setOptions(string option_name, bool option_val [, string option_name, bool option_val ..]);
+         * or      vlibTemplate::_setOptions(array);
+         *          with an associative array where the key is the option_name
+         *          and the value is the option_value.
+         *
+         * @param mixed (mulitple)
+         * @return bool true/false
+         * @access private
+         */
+        function _setOption () {
+            $numargs = func_num_args();
+            if ($numargs < 1) {
+                vlibTemplateError::raiseError('VT_ERROR_WRONG_NO_PARAMS', null, '_setOption()');
+                return false;
+            }
+
+            if ($numargs == 1) {
+                $options = func_get_arg(1);
+                if (is_array($options)) {
+                    foreach ($options as $k => $v) {
+                        if ($v != null) {
+                            if(in_array($k, array_keys($this->OPTIONS))) $this->OPTIONS[$k] = $v;
+                        }
+                        else {
+                            continue;
+                        }
+                    }
+                }
+                else {
+                    vlibTemplateError::raiseError('VT_ERROR_WRONG_NO_PARAMS', null, '_setOption()');
+                    return false;
+                }
+            }
+            elseif (is_int($numargs / 2)) {
+                for ($i = 0; $i < $numargs; $i=($i+2)) {
+                    $k  = func_get_arg($i);
+                    $v = func_get_arg(($i+1));
+                    if ($v != null) {
+                        if(in_array($k, array_keys($this->OPTIONS))) $this->OPTIONS[$k] = $v;
+                    }
+                }
+            }
+            else {
+                vlibTemplateError::raiseError('VT_ERROR_WRONG_NO_PARAMS', null, '_setOption()');
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * FUNCTION: _setUnknown
+         *
+         * Used during parsing, this function sets an unknown var checking to see if it
+         * has been previously set.
+         *
+         * @param string var
+         * @access private
+         */
+        function _setUnknown ($var) {
+            if (!in_array($var, $this->_unknowns)) array_push($this->_unknowns, $var);
+        }
+
+        /**
+         * FUNCTION: _getMicrotime
+         * Returns microtime as a float number
+         *
+         * @return float microtime
+         * @access private
+         */
+        function _getMicrotime () {
+            list($msec, $sec) = explode(" ",microtime());
+            return ((float)$msec + (float)$sec);
+        }
+
+        /**
+         * FUNCTION: _escape_hex
+         * Returns str encoded to hex code.
+         *
+         * @param string str to be encoded
+         * @param bool true/false specify whether to use hex_entity
+         * @return string encoded in hex
+         * @access private
+         */
+        function _escape_hex($str="", $entity=false) {
+            $prestr = $entity ? '&#x' : '%';
+            $poststr= $entity ? ';' : '';
+            for ($i=0; $i < strlen($str); $i++) {
+                $return .= $prestr.bin2hex($str[$i]).$poststr;
+            }
+            return $return;
+        }
+
+    /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    The following functions have no use and are included just so that if the user
+    is making use of vlibTemplateCache functions, this doesn't crash when changed to
+    vlibTemplate if the user is quickly bypassing the vlibTemplateCache class.
+    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+        function clearCache()        {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'clearCache()');}
+        function recache()           {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'recache()');}
+        function setCacheLifeTime()  {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'setCacheLifeTime()');}
+        function setCacheExtension() {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'setCacheExtension()');}
+    }
+
+    //include_once ($conf['classpath'].'/vlibTemplate/debug.php');
+    include_once ($conf['classpath'].'/tpl_cache.inc.php');
+
+} // << end if(!defined())..
+?>
\ No newline at end of file
diff --git a/server/lib/classes/tpl_cache.inc.php b/server/lib/classes/tpl_cache.inc.php
new file mode 100644
index 000000000..a4c6eecf7
--- /dev/null
+++ b/server/lib/classes/tpl_cache.inc.php
@@ -0,0 +1,186 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0                                                      |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2002 Active Fish Group                                 |
+// +----------------------------------------------------------------------+
+// | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk>                     |
+// +----------------------------------------------------------------------+
+//
+// $Id: cache.php,v 1.1 2003/07/08 12:32:06 platinum Exp $
+
+/**
+ * Class uses all of vlibTemplate's functionality but caches the template files.
+ * It creates an identical tree structure to your filesystem but with cached files.
+ *
+ * @author Kelvin Jones <kelvin@kelvinjones.co.uk>
+ * @since 22/02/2002
+ * @package vLIB
+ * @access public
+ */
+
+class tplc extends tpl {
+
+/*-----------------------------------------------------------------------------\
+|     DO NOT TOUCH ANYTHING IN THIS CLASS, IT MAY NOT WORK OTHERWISE           |
+\-----------------------------------------------------------------------------*/
+
+    var $_cache = 1;     // tells vlibTemplate that we're caching
+    var $_cachefile;     // full path to current cache file (even if it doesn't yet exist)
+    var $_cacheexists;   // has this file been cached before
+    var $_cachefilelocked; // is this file currently locked whilst writing
+    var $_cachefiledir;  // dir of current cache file
+    var $_clearcache = 0;
+
+
+    /**
+     * FUNCTION: clearCache
+     * will unset a file, and set $this->_cacheexists to 0.
+     *
+     * @access public
+     * @return boolean
+     */
+    function clearCache() {
+        $this->_clearcache = 1;
+        return true;
+    }
+
+    /**
+     * FUNCTION: recache
+     * alias for clearCache().
+     *
+     * @access public
+     * @return boolean
+     */
+    function recache() {
+        return $this->clearCache();
+    }
+
+    /**
+     * FUNCTION: setCacheLifeTime
+     * sets the lifetime of the cached file
+     *
+     * @param int $int number of seconds to set lifetime to
+     * @access public
+     * @return boolean
+     */
+    function setCacheLifeTime($int = null) {
+        if ($int == null || !is_int($int)) return false;
+        if ($int == 0) $int = 60;
+        if ($int == -1) $int = 157680000; // set to 5 yrs time
+        $this->OPTIONS['CACHE_LIFETIME'] = $int;
+        return true;
+    }
+
+    /**
+     * FUNCTION: setCacheExtension
+     * sets the extention of the cache file
+     *
+     * @param str $str name of new cache extention
+     * @access public
+     * @return boolean
+     */
+    function setCacheExtension($str = null) {
+        if ($str == null || !ereg('^[a-z0-9]+$', strtolower($str))) return false;
+        $this->OPTIONS['CACHE_EXTENSION'] = strtolower($str);
+        return true;
+    }
+
+
+/*----------------------------------------\
+          Private Functions
+-----------------------------------------*/
+
+    /**
+     * FUNCTION: _checkCache
+     * checks if there's a cache, if there is then it will read the cache file as the template.
+     */
+    function _checkCache ($tmplfile) {
+        $this->_cachefile = $this->_getFilename($tmplfile);
+        if ($this->_clearcache) {
+            if (file_exists($this->_cachefile)) unlink($this->_cachefile);
+            return false;
+        }
+
+        if (file_exists($this->_cachefile)) {
+            $this->_cacheexists = 1;
+
+            // if it's expired
+            if ((filemtime($this->_cachefile) + $this->OPTIONS['CACHE_LIFETIME']) < date ('U')
+                  || filectime($this->_cachefile) < filemtime($tmplfile)) {
+                $this->_cacheexists = 0;
+                return false; // so that we know to recache
+            }
+            else {
+                return true;
+            }
+
+        } else {
+            $this->_cacheexists = 0;
+            return false;
+        }
+    }
+
+
+    /**
+     * FUNCTION: _getFilename
+     * gets the full pathname for the cached file
+     *
+     */
+    function _getFilename($tmplfile) {
+        return $this->OPTIONS['CACHE_DIRECTORY'].'/'.md5('vlibCachestaR'.realpath($tmplfile)).'.'.$this->OPTIONS['CACHE_EXTENSION'];
+    }
+
+    /**
+     * FUNCTION: _createCache
+     * creates the cached file
+     *
+     */
+    function _createCache($data) {
+        $cache_file = $this->_cachefile;
+        if(!$this->_prepareDirs($cache_file)) return false; // prepare all of the directories
+
+        $f = fopen ($cache_file, "w");
+        flock($f, 2); // set an EXclusive lock
+        if (!$f) vlibTemplateError::raiseError('VT_ERROR_NO_CACHE_WRITE',KILL,$cache_file);
+        fputs ($f, $data); // write the parsed string from vlibTemplate
+        flock($f, 3); // UNlock file
+        fclose ($f);
+        touch ($cache_file);
+        return true;
+    }
+
+    /**
+     * FUNCTION: _prepareDirs
+     * prepares the directory structure
+     *
+     */
+    function _prepareDirs($file) {
+        if (empty($file)) die('no filename'); //do error in future
+        $filepath = dirname($file);
+        if (is_dir($filepath)) return true;
+
+        $dirs = split('[\\/]', $filepath);
+        $currpath;
+        foreach ($dirs as $dir) {
+            $currpath .= $dir .'/';
+            $type = @filetype($currpath);
+
+            ($type=='link') and $type = 'dir';
+            if ($type != 'dir' && $type != false && !empty($type)) {
+                vlibTemplateError::raiseError('VT_ERROR_WRONG_CACHE_TYPE',KILL,'directory: '.$currpath.', type: '.$type);
+            }
+            if ($type == 'dir') {
+                continue;
+            }
+            else {
+                $s = @mkdir($currpath, 0775);
+                if (!$s) vlibTemplateError::raiseError('VT_ERROR_CACHE_MKDIR_FAILURE',KILL,'directory: '.$currpath);
+            }
+        }
+        return true;
+    }
+
+} // -- end vlibTemplateCache class
+?>
\ No newline at end of file
diff --git a/server/lib/classes/tpl_error.inc.php b/server/lib/classes/tpl_error.inc.php
new file mode 100644
index 000000000..2f139882b
--- /dev/null
+++ b/server/lib/classes/tpl_error.inc.php
@@ -0,0 +1,92 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0                                                      |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2002 Active Fish Group                                 |
+// +----------------------------------------------------------------------+
+// | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk>                     |
+// +----------------------------------------------------------------------+
+//
+// $Id: error.php,v 1.1 2003/07/08 12:32:06 platinum Exp $
+
+define('FATAL', E_USER_ERROR);
+define('WARNING', E_USER_WARNING);
+define('NOTICE', E_USER_NOTICE);
+define('KILL',   -1); // used for killing inside parsing.
+
+/**
+ * Class is used by vlibTemplate.
+ * It handles all of the error reporting for vlibTemplate.
+ *
+ * @author Kelvin Jones <kelvin@kelvinjones.co.uk>
+ * @since 06/03/2002
+ * @package vLIB
+ * @access private
+ */
+
+class vlibTemplateError {
+
+/*-----------------------------------------------------------------------------\
+|     DO NOT TOUCH ANYTHING IN THIS CLASS IT MAY NOT WORK OTHERWISE            |
+\-----------------------------------------------------------------------------*/
+
+    function raiseError ($code, $level = null, $extra=null) {
+        if (!($level & error_reporting())&& $level != KILL) return; // binary AND checks for reporting level
+
+        $error_codes = array(
+                        'VT_ERROR_NOFILE'               => 'vlibTemplate Error: Template ('.$extra.') file not found.',
+                        'VT_ERROR_PARSE'                => 'vlibTemplate Error: Parse error!<br>To debug this file, use vlibTemplateDebug instead of vlibTemplate in the class instantiation(i.e. new vlibTemplateDebug).',
+                        'VT_NOTICE_INVALID_TAG'         => 'vlibTemplate Notice: Invalid tag ('.$extra.').',
+                        'VT_ERROR_INVALID_TAG'          => 'vlibTemplate Error: Invalid tag ('.$extra.'). To disable this you must turn of the STRICT option.',
+                        'VT_NOTICE_INVALID_ATT'         => 'vlibTemplate Notice: Invalid attribute ('.$extra.').',
+                        'VT_WARNING_INVALID_ARR'        => 'vlibTemplate Warning: Invalid loop structure passed to vlibTemplate::setLoop() (loop name: '.$extra.').',
+                        'VT_ERROR_INVALID_ERROR_CODE'   => 'vlibTemplate Error: Invalid error raised.',
+                        'VT_ERROR_WRONG_NO_PARAMS'      => 'vlibTemplate Warning: Wrond parameter count passed to '.$extra.'.',
+                        'VT_ERROR_UNKNOWN_VAR'          => 'vlibTemplate Error: template var not found.',
+                        'VT_ERROR_NO_CACHE_WRITE'       => 'vlibTemplate Error: unable to write to cache file ('.$extra.').',
+                        'VT_ERROR_WRONG_CACHE_TYPE'     => 'vlibTemplate Error: non-directory file found in cache root with same name as directory ('.$extra.').',
+                        'VT_ERROR_CACHE_MKDIR_FAILURE'  => 'vlibTemplate Error: failed to create directory in cache root ('.$extra.').',
+                        'VT_WARNING_NOT_CACHE_OBJ'      => 'vlibTemplate Warning: called a vlibTemplateCache function ('.$extra.') without instantiating the vlibTemplateCache class.',
+                        'VT_WARNING_LOOP_NOT_SET'       => 'vlibTemplate Warning: called vlibTemplate::addRow() or vlibTemplate::addLoop() with an invalid loop name.',
+                        'VT_WARNING_INVALID_RESOURCE'   => 'vlibTemplate Warning: Invalid resource type passed to vlibTemplate::setDbLoop() for Db "'.$extra.'".',
+                        'VT_WARNING_INVALID_LOOP_DB'    => 'vlibTemplate Warning: Invalid Db type passed to vlibTemplate::setDbLoop(), "'.$extra.'" not currently available.',
+                        'VT_WARNING_INVALID_IF_OP'      => 'vlibTemplate Warning: The Operator "'.$extra.'" is not supported by vlibTemplate.'
+                            );
+
+        $error_levels = array(
+                        'VT_ERROR_NOFILE'               => FATAL,
+                        'VT_ERROR_PARSE'                => FATAL,
+                        'VT_NOTICE_INVALID_TAG'         => NOTICE,
+                        'VT_ERROR_INVALID_TAG'          => FATAL,
+                        'VT_NOTICE_INVALID_ATT'         => NOTICE,
+                        'VT_WARNING_INVALID_ARR'        => WARNING,
+                        'VT_ERROR_INVALID_ERROR_CODE'   => FATAL,
+                        'VT_ERROR_WRONG_NO_PARAMS'      => WARNING,
+                        'VT_ERROR_UNKNOWN_VAR'          => WARNING,
+                        'VT_ERROR_NO_CACHE_WRITE'       => KILL,
+                        'VT_ERROR_WRONG_CACHE_TYPE'     => KILL,
+                        'VT_ERROR_CACHE_MKDIR_FAILURE'  => KILL,
+                        'VT_WARNING_NOT_CACHE_OBJ'      => WARNING,
+                        'VT_WARNING_LOOP_NOT_SET'       => WARNING,
+                        'VT_WARNING_INVALID_RESOURCE'   => WARNING,
+                        'VT_WARNING_INVALID_LOOP_DB'    => WARNING,
+                        'VT_WARNING_INVALID_IF_OP'      => WARNING
+                            );
+
+        ($level === null) and $level = $error_levels[$code];
+        if ($level == KILL) {
+            die ($error_codes[$code]);
+        }
+
+        if ($msg = $error_codes[$code]) {
+            trigger_error($msg, $level);
+        } else {
+            $level = $error_levels['VT_ERROR_INVALID_ERROR_CODE'];
+            $msg = $error_codes['VT_ERROR_INVALID_ERROR_CODE'];
+            trigger_error($msg, $level);
+        }
+        return;
+    }
+}
+?>
\ No newline at end of file
diff --git a/server/lib/classes/tpl_ini.inc.php b/server/lib/classes/tpl_ini.inc.php
new file mode 100644
index 000000000..84ae02b1a
--- /dev/null
+++ b/server/lib/classes/tpl_ini.inc.php
@@ -0,0 +1,116 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0                                                      |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2002 Active Fish Group                                 |
+// +----------------------------------------------------------------------+
+// | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk>                     |
+// +----------------------------------------------------------------------+
+//
+// $Id: vlibIni.php,v 1.1 2003/07/08 12:31:10 platinum Exp $
+
+/*
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; This file contains configuration parametres for use  ;
+; with the vLIB library. [ NOW A CLASS!! ]             ;
+;                                                      ;
+; vLIB uses this file so that for future releases, you ;
+; will not have to delve through all the php script    ;
+; again to set your specific variable/properties ..etc ;
+;                                                      ;
+; ---------------------------------------------------- ;
+; ATTENTION: Do NOT remove any variable given in the   ;
+; configurations below as they will probably still be  ;
+; needed by vLIB. If you do not need a variable simply ;
+; let it be.                                           ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+*/
+
+if (!defined('vlibIniClassLoaded')) {
+    define('vlibIniClassLoaded', 1);
+
+    /**
+     * vlibIni is a class used to store configuration parameters
+     * for the vLIB library.
+     *
+     * @since 21/07/2002
+     * @author Kelvin Jones <kelvin@kelvinjones.co.uk>
+     * @package vLIB
+     * @access private
+     */
+
+    class vlibIni {
+
+        /** config vars for vlibTemplate */
+        function vlibTemplate () {
+        	
+        	global $conf;
+			
+			$tpl_dir = $conf["rootpath"]."/conf";
+			
+            return array(
+
+                        'TEMPLATE_DIR' => $tpl_dir,   // Default directory for your template files (full path)
+                                                                   // leave the '/' or '\' off the end of the directory.
+
+                        'MAX_INCLUDES' => 10,                      // Drill depth for tmpl_include's
+
+                        'GLOBAL_VARS' => 1,                        // if set to 1, any variables not found in a
+                                                                   // loop will search for a global var as well
+
+                        'GLOBAL_CONTEXT_VARS' => 1,                // if set to 1, vlibTemplate will add global vars
+                                                                   // reflecting the environment.
+
+                        'LOOP_CONTEXT_VARS' => 1,                  // if set to 1, vlibTemplate will add loop specific vars
+                                                                   // on each row of the loop.
+
+                        'SET_LOOP_VAR' => 1,                       // Sets a global variable for each top level loops
+
+                        'DEFAULT_ESCAPE' => 'none',                // 1 of the following: html, url, sq, dq, none
+
+                        'STRICT' => 0,                             // Dies when encountering an incorrect tmpl_*
+                                                                   // style tags i.e. tmpl_vae
+
+                        'CASELESS' => 0,                           // Removes case sensitivity on all variables
+
+                        'UNKNOWNS' => 'ignore',                    // How to handle unknown variables.
+                                                                   // 1 of the following: ignore, remove, leave,print, comment
+                                                                   // 1 of the following: ignore, remove, leave, print, comment
+
+                        'TIME_PARSE' => '0',                       // Will enable you to time how long vlibTemplate takes to parse
+                                                                   // your template. You then use the function: getParseTime().
+
+                        'ENABLE_PHPINCLUDE' => '1',                // Will allow template to include a php file using <TMPL_PHPINCLUDE>
+
+
+                        /* the following are only used by the vlibTemplateCache class. */
+
+                        'CACHE_DIRECTORY' => $conf["template"]["cache_dir"],
+                                                                   // Directory where the cached filesystem
+                                                                   // will be set up (full path, and must be writable)
+                                                                   // '/' or '\' off the end of the directory.
+
+                        'CACHE_LIFETIME' => 604800,                // Duration until file is re-cached in seconds (604800 = 1 week)
+
+                        'CACHE_EXTENSION' => 'vtc'                  // extention to be used by the cached file i.e. index.php will become
+                                                                   // index.vtc (vlibTemplate Compiled)
+                    );
+
+        } // << end function vlibTemplate
+
+
+
+        /** config vars for vlibDate */
+        function vlibDate () {
+
+            return array(
+                        'DEFAULT_LANG' => 'de'                     // default language for the date displays
+                    );
+
+        }// << end function vlibDate
+
+
+    }// << end class vlibIni
+}
+?>
\ No newline at end of file
diff --git a/server/mods-enabled/web_module.inc.php b/server/mods-enabled/web_module.inc.php
index a32b8b5e4..537791107 100644
--- a/server/mods-enabled/web_module.inc.php
+++ b/server/mods-enabled/web_module.inc.php
@@ -60,7 +60,10 @@ class web_module {
  		class that contains the function functionname.
 		*/
 		
-		$app->modules->registerTableHook('web_domain','mail_module','process');
+		$app->modules->registerTableHook('web_domain','web_module','process');
+		
+		// Register service
+		$app->services->registerService('httpd','web_module','restartHttpd');
 		
 	}
 	
@@ -80,6 +83,18 @@ class web_module {
 			break;
 		} // end switch
 	} // end function
+	
+	
+	// This function is used
+	function restartHttpd($action = 'restart') {
+		global $app;
+		if($action == 'restart') {
+			exec('/etc/init.d/apache2 restart');
+		} else {
+			exec('/etc/init.d/apache2 reload');
+		}
+		
+	}
 
 } // end class
 
diff --git a/server/plugins-enabled/apache2_plugin.inc.php b/server/plugins-enabled/apache2_plugin.inc.php
index f8aafe0bc..3d01fd621 100644
--- a/server/plugins-enabled/apache2_plugin.inc.php
+++ b/server/plugins-enabled/apache2_plugin.inc.php
@@ -31,7 +31,7 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 class apache2_plugin {
 	
 	var $plugin_name = 'apache2_plugin';
-	var $class_name = $this->plugin_name;
+	var $class_name = 'apache2_plugin';
 	
 		
 	/*
@@ -54,7 +54,8 @@ class apache2_plugin {
 	function insert($event_name,$data) {
 		global $app, $conf;
 		
-		
+		// just run the update function
+		$this->update($event_name,$data);
 		
 		
 	}
@@ -67,12 +68,100 @@ class apache2_plugin {
 		$app->uses("getconf");
 		$web_config = $app->getconf->get_server_config($conf["server_id"], 'web');
 		
+		if($data["new"]["document_root"] == '') {
+			$app->log("document_root not set",LOGLEVEL_WARN);
+			return 0;
+		}
+		
+		//print_r($data);
+		
+		// Check if the directories are there and create them if nescessary.
+		if(!is_dir($data["new"]["document_root"]."/web")) exec("mkdir -p ".$data["new"]["document_root"]."/web");
+		if(!is_dir($data["new"]["document_root"]."/log")) exec("mkdir -p ".$data["new"]["document_root"]."/log");
+		if(!is_dir($data["new"]["document_root"]."/ssl")) exec("mkdir -p ".$data["new"]["document_root"]."/ssl");
+		if(!is_dir($data["new"]["document_root"]."/cgi-bin")) exec("mkdir -p ".$data["new"]["document_root"]."/cgi-bin");
+		
+		// TODO: Create the symlinks
+		
+		
+		// Create group and user, if not exist
+		$app->uses("system");
+		
+		$groupname = escapeshellcmd($data["new"]["system_group"]);
+		if($data["new"]["system_group"] != '' && !$app->system->is_group($data["new"]["system_group"])) {
+			exec("groupadd $groupname");
+			$app->log("Adding the group: $groupname",LOGLEVEL_DEBUG);
+		}
+		
+		$username = escapeshellcmd($data["new"]["system_user"]);
+		if($data["new"]["system_user"] != '' && !$app->system->is_user($data["new"]["system_user"])) {
+			exec("useradd -d ".escapeshellcmd($data["new"]["document_root"])." -g $groupname $username");
+			$app->log("Adding the user: $username",LOGLEVEL_DEBUG);
+		}
+		
+		// Chown and chmod the directories
+		exec("chown -R $username:$groupname ".escapeshellcmd($data["new"]["document_root"]));
+		
+		// Create the vhost config file
+		$app->load('tpl');
+		
+		$tpl = new tpl();
+		$tpl->newTemplate("vhost.conf.master");
+		
+		$vhost_data = $data["new"];
+		$vhost_data["document_root"] = $data["new"]["document_root"]."/web";
+		$tpl->setVar($vhost_data);
+		
+		// get alias domains
+		$aliases = $app->db->queryAllRecords("SELECT * FROM web_domain WHERE parent_domain_id = ".$data["new"]["domain_id"]);
+		$server_alias = '';
+		foreach($aliases as $alias) {
+			$server_alias .= $alias["domain"].' ';
+		}
+		$tpl->setVar('alias',trim($server_alias));
+		
+		$vhost_file = escapeshellcmd($web_config["vhost_conf_dir"].'/'.$data["new"]["domain"].'.vhost');
+		file_put_contents($vhost_file,$tpl->grab());
+		$app->log("Writing the vhost file: $vhost_file",LOGLEVEL_DEBUG);
+		unset($tpl);
+		
+		// Set the symlink to enable the vhost
+		$vhost_symlink = escapeshellcmd($web_config["vhost_conf_enabled_dir"].'/'.$data["new"]["domain"].'.vhost');
+		if($data["new"]["active"] == 'y' && !is_link($vhost_symlink)) {
+			symlink($vhost_file,$vhost_symlink);
+			$app->log("Creating the symlink: $vhost_symlink => $vhost_file",LOGLEVEL_DEBUG);
+		}
+		
+		// Remove the symlink, if site is inactive
+		if($data["new"]["active"] == 'n' && is_link($vhost_symlink)) {
+			unlink($vhost_symlink);
+			$app->log("Removing symlink: $vhost_symlink => $vhost_file",LOGLEVEL_DEBUG);
+		}
+		
+		// request a httpd reload when all records have been processed
+		$app->services->restartServiceDelayed('httpd','reload');
 		
 	}
 	
 	function delete($event_name,$data) {
 		global $app, $conf;
 		
+		// load the server configuration options
+		$app->uses("getconf");
+		$web_config = $app->getconf->get_server_config($conf["server_id"], 'web');
+		
+		// Deleting the vhost file, symlink and the data directory
+		$vhost_symlink = escapeshellcmd($web_config["vhost_conf_enabled_dir"].'/'.$data["old"]["domain"].'.vhost');
+		unlink($vhost_symlink);
+		$app->log("Removing symlink: $vhost_symlink => $vhost_file",LOGLEVEL_DEBUG);
+		
+		$vhost_file = escapeshellcmd($web_config["vhost_conf_dir"].'/'.$data["old"]["domain"].'.vhost');
+		unlink($vhost_file);
+		$app->log("Removing vhost file: $vhost_file",LOGLEVEL_DEBUG);
+		
+		$docroot = escapeshellcmd($data["old"]["document_root"]);
+		if($docroot != '' && !stristr($docroot,'..')) exec("rm -rf $docroot");
+		$app->log("Removing website: $docroot",LOGLEVEL_DEBUG);
 		
 	}
 	
diff --git a/server/server.php b/server/server.php
index 89341eb8d..b87ea4569 100644
--- a/server/server.php
+++ b/server/server.php
@@ -76,7 +76,7 @@ if($tmp_num_records > 0) {
 	$app->log("Found $tmp_num_records changes, starting update process.",LOGLEVEL_DEBUG);
 	
 	// Load required base-classes
-	$app->uses('ini_parser,modules,plugins');
+	$app->uses('ini_parser,modules,plugins,file,services');
 	
 	
 	// Get server configuration
@@ -99,8 +99,15 @@ if($tmp_num_records > 0) {
 	 in the modules that are hooked on to the table actions
 	*/
 	$app->modules->processDatalog();
+	
+	/*
+	 Restart services that need to be restarted after configuration
+	*/
+	$app->services->processDelayedActions();
+	
+	
 } else {
-	$app->log('Nothing to Update.',LOGLEVEL_DEBUG);
+	$app->log('No Updated records found.',LOGLEVEL_DEBUG);
 }
 
 /*
-- 
GitLab