diff --git a/TODO.txt b/TODO.txt
index 5b409f9552cad3ce6f9f8abaee8a8bc98fca205b..20071babc2c1195665b0762e933cb50bdab74e81 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -46,9 +46,6 @@ Administration module
 Clients module
 --------------------------------------
 
-- Add the ability that Clients can add clients (Reseller functionality)
-  Task assigned to: Till
-
 
 Sites (web) module
 --------------------------------------
diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql
index 0f7008c18b6da448221f3abcd6d6de2b9bcaf899..c8188f013a1a7d5b38a59dfc739bb0beda10269b 100644
--- a/install/sql/ispconfig3.sql
+++ b/install/sql/ispconfig3.sql
@@ -60,6 +60,8 @@ CREATE TABLE `client` (
   `default_dnsserver` int(10) unsigned NOT NULL default '1',
   `limit_dns_zone` int(11) NOT NULL default '-1',
   `limit_dns_record` int(11) NOT NULL default '-1',
+  `limit_client` int(11) NOT NULL default '0',
+  `parent_client_id` int(10) unsigned NOT NULL default '0',
   `username` varchar(255) default NULL,
   `password` varchar(255) default NULL,
   `language` varchar(255) NOT NULL default 'en',
@@ -847,15 +849,15 @@ CREATE TABLE `web_domain` (
   `redirect_type` varchar(255) default NULL,
   `redirect_path` varchar(255) default NULL,
   `ssl` enum('n','y') NOT NULL default 'n',
-  `ssl_state` varchar(255) NOT NULL,
-  `ssl_locality` varchar(255) NOT NULL,
-  `ssl_organisation` varchar(255) NOT NULL,
-  `ssl_organisation_unit` varchar(255) NOT NULL,
-  `ssl_country` varchar(255) NOT NULL,
-  `ssl_request` mediumtext NOT NULL,
-  `ssl_cert` mediumtext NOT NULL,
-  `ssl_bundle` mediumtext NOT NULL,
-  `ssl_action` varchar(10) NOT NULL,
+  `ssl_state` varchar(255) NULL,
+  `ssl_locality` varchar(255) NULL,
+  `ssl_organisation` varchar(255) NULL,
+  `ssl_organisation_unit` varchar(255) NULL,
+  `ssl_country` varchar(255) NULL,
+  `ssl_request` mediumtext NULL,
+  `ssl_cert` mediumtext NULL,
+  `ssl_bundle` mediumtext NULL,
+  `ssl_action` varchar(10) NULL,
   `active` varchar(255) NOT NULL default 'y',
   PRIMARY KEY  (`domain_id`)
 ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
diff --git a/install/tpl/config.inc.php.master b/install/tpl/config.inc.php.master
index 313e6b3298dc4a849421219cb389f8dfc854cfbd..a1b65b56657b0e737b4d1bc72b93289f5189284f 100644
--- a/install/tpl/config.inc.php.master
+++ b/install/tpl/config.inc.php.master
@@ -58,6 +58,9 @@ define('ISPC_THEMES_PATH', ISPC_ROOT_PATH.'/web/themes');
 define('ISPC_TEMP_PATH',   ISPC_ROOT_PATH.'/temp');
 define('ISPC_CACHE_PATH',  ISPC_ROOT_PATH.'/cache');
 
+//** Interface settings
+define('ISPC_INTERFACE_MODULES_ENABLED', 'mail,sites,dns');
+
 
 /*
 	Server variables
diff --git a/interface/lib/app.inc.php b/interface/lib/app.inc.php
index 926b4ebdedb1ab5f36759009fb20f677af3cb608..dff1bd7ed4a6c23d8c728b956597162008164e3e 100644
--- a/interface/lib/app.inc.php
+++ b/interface/lib/app.inc.php
@@ -63,6 +63,8 @@ class app {
 			if(empty($_SESSION['s']['theme'])) $_SESSION['s']['theme'] = $conf['theme'];
 			if(empty($_SESSION['s']['language'])) $_SESSION['s']['language'] = $conf['language'];
 		}
+		
+		$this->uses('auth');
 	}
 
 	public function uses($classes)
@@ -176,6 +178,9 @@ class app {
 		if(isset($_SESSION['s']['user']) && $_SESSION['s']['user']['typ'] == 'admin') {
 			$this->tpl->setVar('is_admin', 1);
 		}
+		if(isset($_SESSION['s']['user']) && $this->auth->has_clients($_SESSION['s']['user']['userid'])) {
+			$this->tpl->setVar('is_reseller', 1);
+		}
     }
     
 } // end class
diff --git a/interface/lib/classes/auth.inc.php b/interface/lib/classes/auth.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..59822490c8e6e4df15ef091331b02a4678a03c3a
--- /dev/null
+++ b/interface/lib/classes/auth.inc.php
@@ -0,0 +1,87 @@
+<?php
+
+/*
+Copyright (c) 2008, 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.
+*/
+
+class auth {
+	
+	public function has_clients($userid) {
+		global $app, $conf;
+		
+		$userid = intval($userid);
+		$client = $app->db->queryOneRecord("SELECT client.limit_client FROM sys_user, client WHERE sys_user.userid = $userid AND sys_user.client_id = client.client_id");
+		if($client['limit_client'] > 0) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+	
+	//** This function adds a given group id to a given user.
+	public function add_group_to_user($userid,$groupid) {
+		global $app;
+		
+		$userid = intval($userid);
+		$groupid = intval($groupid);
+		
+		if($userid > 0 && $groupid > 0) {
+			$user = $app->db->queryOneRecord("SELECT * FROM sys_user WHERE userid = $userid");
+			$groups = explode(',',$user['groups']);
+			if(!in_array($groupid,$groups)) $groups[] = $groupid;
+			$groups_string = implode(',',$groups);
+			$sql = "UPDATE sys_user SET groups = '$groups_string' WHERE userid = $userid";
+			$app->db->query($sql);
+			return true;
+		} else {
+			return false;
+		}
+	}
+	
+	//** This function removes a given group id from a given user.
+	public function remove_group_from_user($userid,$groupid) {
+		global $app;
+		
+		$userid = intval($userid);
+		$groupid = intval($groupid);
+		
+		if($userid > 0 && $groupid > 0) {
+			$user = $app->db->queryOneRecord("SELECT * FROM sys_user WHERE userid = $userid");
+			$groups = explode(',',$user['groups']);
+			$key = array_search($groupid,$groups);
+			unset($groups[$key]);
+			$groups_string = implode(',',$groups);
+			$sql = "UPDATE sys_user SET groups = '$groups_string' WHERE userid = $userid";
+			$app->db->query($sql);
+			return true;
+		} else {
+			return false;
+		}
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/interface/lib/classes/tform.inc.php b/interface/lib/classes/tform.inc.php
index c190512cffd78d65ff1756a39057eb17550be8af..e5644c321fb07c16c8c2af98a4ca8b8f45931afd 100644
--- a/interface/lib/classes/tform.inc.php
+++ b/interface/lib/classes/tform.inc.php
@@ -718,7 +718,7 @@ class tform {
                                                 }
                                         } else {
                                                 if($field['formtype'] == 'PASSWORD') {
-														if($field['encryption'] == 'CRYPT') {
+														if(isset($field['encryption']) && $field['encryption'] == 'CRYPT') {
                                                                 $salt="$1$";
 																for ($n=0;$n<8;$n++) {
 																	$salt.=chr(mt_rand(64,126));
@@ -999,8 +999,8 @@ class tform {
                         }
                 } else {
                         $result = false;
-                        if($this->formDef["auth_preset"]["userid"] == $_SESSION["s"]["user"]["userid"] && stristr($perm,$this->formDef["auth_preset"]["perm_user"])) $result = true;
-                        if($this->formDef["auth_preset"]["groupid"] == $_SESSION["s"]["user"]["groupid"] && stristr($perm,$this->formDef["auth_preset"]["perm_group"])) $result = true;
+                        if(@$this->formDef["auth_preset"]["userid"] == $_SESSION["s"]["user"]["userid"] && stristr($perm,$this->formDef["auth_preset"]["perm_user"])) $result = true;
+                        if(@$this->formDef["auth_preset"]["groupid"] == $_SESSION["s"]["user"]["groupid"] && stristr($perm,$this->formDef["auth_preset"]["perm_group"])) $result = true;
                         if(@stristr($this->formDef["auth_preset"]["perm_other"],$perm)) $result = true;
 
                         // if preset == 0, everyone can insert a record of this type
diff --git a/interface/lib/config.inc.php b/interface/lib/config.inc.php
index 94f0ea4a88f198a17aba5ea07b88d2832a41ab0e..9ed79dfe448f4a4449aba98fbd302fd13c9d3d81 100644
--- a/interface/lib/config.inc.php
+++ b/interface/lib/config.inc.php
@@ -51,6 +51,7 @@ define('ISPC_THEMES_PATH', ISPC_ROOT_PATH.'/web/themes');
 define('ISPC_TEMP_PATH',   ISPC_ROOT_PATH.'/temp');
 define('ISPC_CACHE_PATH',  ISPC_ROOT_PATH.'/cache');
 
+define('ISPC_INTERFACE_MODULES_ENABLED', 'mail,sites,dns');
 
 //********************************************************************************
 //** Future Code idea  - pedro - rfc
diff --git a/interface/web/client/client_del.php b/interface/web/client/client_del.php
index 5378464ce4701b0137d420314b56eab63310cb90..38bcd67fb80f1b1114a4dbe3ca3e822013922452 100644
--- a/interface/web/client/client_del.php
+++ b/interface/web/client/client_del.php
@@ -48,7 +48,35 @@ if(!stristr($_SESSION["s"]["user"]["modules"],$_SESSION["s"]["module"]["name"]))
 	exit;
 }
 
-$app->uses("tform_actions");
-$app->tform_actions->onDelete();
+$app->uses('tpl,tform');
+$app->load('tform_actions');
+
+class page_action extends tform_actions {
+	function onAfterDelete() {
+		global $app, $conf;
+		
+		$client_id = intval($this->dataRecord['client_id']);
+		
+		if($client_id > 0) {
+			// TODO: Delete all records (sub-clients, mail, web, etc....)  of this client.
+			
+			// remove the group of the client from the resellers group
+			$parent_client_id = intval($this->dataRecord['parent_client_id']);
+			$parent_user = $app->db->queryOneRecord("SELECT userid FROM sys_user WHERE client_id = $parent_client_id");
+			$client_group = $app->db->queryOneRecord("SELECT groupid FROM sys_group WHERE client_id = $client_id");
+			$app->auth->remove_group_from_user($parent_user['userid'],$client_group['groupid']);
+			
+			// delete the group of the client
+			$app->db->query("DELETE FROM sys_group WHERE client_id = $client_id");
+			
+			// delete the sys user(s) of the client
+			$app->db->query("DELETE FROM sys_user WHERE client_id = $client_id");
+		}
+		
+	}
+}
+
+$page = new page_action;
+$page->onDelete()
 
 ?>
\ No newline at end of file
diff --git a/interface/web/client/client_edit.php b/interface/web/client/client_edit.php
index d2068d8b44f099aeba67168d475bd9e3e24f510e..4852047d97544b743ff0642ae55556f41c574251 100644
--- a/interface/web/client/client_edit.php
+++ b/interface/web/client/client_edit.php
@@ -63,10 +63,12 @@ class page_action extends tform_actions {
 		$sql = "INSERT INTO sys_group (name,description,client_id) VALUES ('".addslashes($this->dataRecord["username"])."','',".$this->id.")";
 		$app->db->query($sql);
 		$groupid = $app->db->insertID();
+		$groups = $groupid;
 		
 		$username = addslashes($this->dataRecord["username"]);
 		$password = addslashes($this->dataRecord["password"]);
-		$modules = 'mail,sites,dns';
+		$modules = ISPC_INTERFACE_MODULES_ENABLED;
+		if($this->dataRecord["limit_client"] > 0) $modules .= ',client';
 		$startmodule = 'mail';
 		$usertheme = addslashes($this->dataRecord["usertheme"]);
 		$type = 'user';
@@ -75,8 +77,17 @@ class page_action extends tform_actions {
 		
 		// Create the controlpaneluser for the client
 		$sql = "INSERT INTO sys_user (username,passwort,modules,startmodule,app_theme,typ,active,language,groups,default_group,client_id)
-		VALUES ('$username',md5('$password'),'$modules','$startmodule','$usertheme','$type','$active','$language',$groupid,$groupid,".$this->id.")";
+		VALUES ('$username',md5('$password'),'$modules','$startmodule','$usertheme','$type','$active','$language',$groups,$groupid,".$this->id.")";
 		$app->db->query($sql);
+		
+		//* If the user who inserted the client is a reseller (not admin), we will have to add this new client group 
+		//* to his groups, so he can administrate the records of this client.
+		if($_SESSION['s']['user']['typ'] == 'user') {
+			$app->auth->add_group_to_user($_SESSION['s']['user']['userid'],$groupid);
+			$app->db->query("UPDATE client SET parent_client_id = ".intval($_SESSION['s']['user']['client_id'])." WHERE client_id = ".$this->id);
+		}
+		
+		
 	}
 	
 	
@@ -105,8 +116,15 @@ class page_action extends tform_actions {
 			$app->db->query($sql);
 		}
 		
-		
-		
+		// reseller status changed
+		if(isset($this->dataRecord["limit_client"])) {
+			$modules = ISPC_INTERFACE_MODULES_ENABLED;
+			if($this->dataRecord["limit_client"] > 0) $modules .= ',client';
+			$modules = addslashes($modules);
+			$client_id = $this->id;
+			$sql = "UPDATE sys_user SET modules = '$modules' WHERE client_id = $client_id";
+			$app->db->query($sql);
+		}
 	}
 	
 	
diff --git a/interface/web/client/form/client.tform.php b/interface/web/client/form/client.tform.php
index 14aeab1ed9f44d8994d616fe73c9364b444745d6..fe7e284193ae44ea801f0c40c6b357567f45833e 100644
--- a/interface/web/client/form/client.tform.php
+++ b/interface/web/client/form/client.tform.php
@@ -104,6 +104,7 @@ $form["tabs"]['address'] = array (
 		'password' => array (
 			'datatype'	=> 'VARCHAR',
 			'formtype'	=> 'PASSWORD',
+			'encryption'=> 'MD5',
 			'default'	=> '',
 			'value'		=> '',
 			'separator'	=> '',
@@ -579,6 +580,20 @@ $form["tabs"]['limits'] = array (
 			'rows'		=> '',
 			'cols'		=> ''
 		),
+		'limit_client' => array (
+			'datatype'	=> 'INTEGER',
+			'formtype'	=> 'TEXT',
+			'validators'	=> array ( 	0 => array (	'type'	=> 'ISINT',
+														'errmsg'=> 'limit_client_error_notint'),
+									),
+			'default'	=> '0',
+			'value'		=> '',
+			'separator'	=> '',
+			'width'		=> '10',
+			'maxlength'	=> '10',
+			'rows'		=> '',
+			'cols'		=> ''
+		),
 	##################################
 	# END Datatable fields
 	##################################
diff --git a/interface/web/client/lib/lang/en_client.lng b/interface/web/client/lib/lang/en_client.lng
index fbc1fbf77741b62605ff203225bf5c84462360e8..58f24f12030002a19087c57eeb7b61b5da08389e 100644
--- a/interface/web/client/lib/lang/en_client.lng
+++ b/interface/web/client/lib/lang/en_client.lng
@@ -54,5 +54,6 @@ $wb["default_dnsserver_txt"] = 'Default DNS Server';
 $wb["limit_dns_zone_txt"] = 'Max. number of DNS zones';
 $wb["limit_dns_record_txt"] = 'Max. number DNS records';
 $wb["limit_shell_user_txt"] = 'Max. number of Shell users';
+$wb["limit_client_txt"] = 'Max. number of Clients';
 
 ?>
\ No newline at end of file
diff --git a/interface/web/client/lib/module.conf.php b/interface/web/client/lib/module.conf.php
index c544555c64639fdeb483546d4988571b5d5104d7..23c4a18b8e1a68cfef32cd89e9e7866d9188d809 100644
--- a/interface/web/client/lib/module.conf.php
+++ b/interface/web/client/lib/module.conf.php
@@ -1,57 +1,26 @@
 <?php
-$module = array (
-  'name' => 'client',
-  'title' => 'Client',
-  'template' => 'module.tpl.htm',
-  'navframe_page' => '',
-  'startpage' => 'client/client_list.php',
-  'tab_width' => '',
-  'nav' => 
-  array (
-    0 => 
-    array (
-      'title' => 'Clients',
-      'open' => 1,
-      'items' => 
-      array (
-        0 => 
-        array (
-          'title' => 'Add Client',
-          'target' => 'content',
-          'link' => 'client/client_edit.php',
-        ),
-        1 => 
-        array (
-          'title' => 'Edit Client',
-          'target' => 'content',
-          'link' => 'client/client_list.php',
-        ),
-      ),
-    ),
-    1 => 
-    array (
-      'title' => 'Statistics',
-      'open' => 1,
-      'items' => 
-      array (
-      ),
-    ),
-    2 => 
-    array (
-      'title' => 'Invoices',
-      'open' => 1,
-      'items' => 
-      array (
-      ),
-    ),
-    3 => 
-    array (
-      'title' => 'Mailings',
-      'open' => 1,
-      'items' => 
-      array (
-      ),
-    ),
-  ),
-)
+
+$module["name"] 		= "client";
+$module["title"] 		= "Client";
+$module["template"] 	= "module.tpl.htm";
+$module["startpage"] 	= "client/client_list.php";
+$module["tab_width"]    = '';
+
+/*
+	Email accounts menu
+*/
+
+
+$items[] = array( 'title' 	=> "Add Client",
+				  'target' 	=> 'content',
+				  'link'	=> 'client/client_edit.php');
+
+$items[] = array( 'title' 	=> "Edit Client",
+				  'target' 	=> 'content',
+				  'link'	=> 'client/client_list.php');
+
+
+$module["nav"][] = array(	'title'	=> 'Clients',
+							'open' 	=> 1,
+							'items'	=> $items);
 ?>
\ No newline at end of file
diff --git a/interface/web/client/templates/client_edit_limits.htm b/interface/web/client/templates/client_edit_limits.htm
index f130d8951fd66538bba97403e8e74d4596c234c1..27115ddea5e2d49560cd741d14d1b2cf56806e29 100644
--- a/interface/web/client/templates/client_edit_limits.htm
+++ b/interface/web/client/templates/client_edit_limits.htm
@@ -113,6 +113,13 @@
     <td class="frmText11" width="280">{tmpl_var name='limit_dns_record_txt'}:</td>
     <td class="frmText11" width="220"><input name="limit_dns_record" type="text" class="text" value="{tmpl_var name='limit_dns_record'}" size="10" maxlength="10"></td>
   </tr>
+  <tr>
+    <td><h2>Clients</h2></td>
+  </tr>
+  <tr>
+    <td class="frmText11" width="280">{tmpl_var name='limit_client_txt'}:</td>
+    <td class="frmText11" width="220"><input name="limit_client" type="text" class="text" value="{tmpl_var name='limit_client'}" size="10" maxlength="10"></td>
+  </tr>
   <tr>
     <td class="frmText11">&nbsp;</td>
     <td class="frmText11">&nbsp;</td>
diff --git a/interface/web/dns/dns_soa_edit.php b/interface/web/dns/dns_soa_edit.php
index de3cb6d550bf2be1933210b8684e186a5254dffd..48e9a7c04fa34dd4174ceeed3120aec25482b9af 100644
--- a/interface/web/dns/dns_soa_edit.php
+++ b/interface/web/dns/dns_soa_edit.php
@@ -79,11 +79,12 @@ class page_action extends tform_actions {
 		global $app, $conf;
 		
 		// If user is admin, we will allow him to select to whom this record belongs
-		if($_SESSION["s"]["user"]["typ"] == 'admin') {
+		if($_SESSION["s"]["user"]["typ"] == 'admin' || $app->auth->has_clients($_SESSION['s']['user']['userid'])) {
 			// Getting Domains of the user
 			$sql = "SELECT groupid, name FROM sys_group WHERE client_id > 0";
 			$clients = $app->db->queryAllRecords($sql);
-			$client_select = "<option value='0'></option>";
+			$client_select = '';
+			if($_SESSION["s"]["user"]["typ"] == 'admin') $client_select .= "<option value='0'></option>";
 			if(is_array($clients)) {
 				foreach( $clients as $client) {
 					$selected = ($client["groupid"] == $this->dataRecord["sys_groupid"])?'SELECTED':'';
@@ -134,25 +135,37 @@ class page_action extends tform_actions {
 	function onAfterInsert() {
 		global $app, $conf;
 		
-		// make sure that the record belongs to the clinet group and not the admin group when a dmin inserts it
+		// make sure that the record belongs to the client group and not the admin group when a dmin inserts it
 		if($_SESSION["s"]["user"]["typ"] == 'admin' && isset($this->dataRecord["client_group_id"])) {
 			$client_group_id = intval($this->dataRecord["client_group_id"]);
 			$app->db->query("UPDATE dns_soa SET sys_groupid = $client_group_id WHERE id = ".$this->id);
 			// And we want to update all rr records too, that belong to this record
 			$app->db->query("UPDATE dns_rr SET sys_groupid = $client_group_id WHERE zone = ".$this->id);
 		}
+		if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) {
+			$client_group_id = intval($this->dataRecord["client_group_id"]);
+			$app->db->query("UPDATE dns_soa SET sys_groupid = $client_group_id WHERE id = ".$this->id);
+			// And we want to update all rr records too, that belong to this record
+			$app->db->query("UPDATE dns_rr SET sys_groupid = $client_group_id WHERE zone = ".$this->id);
+		}
 	}
 	
 	function onAfterUpdate() {
 		global $app, $conf;
 		
-		// make sure that the record belongs to the clinet group and not the admin group when a dmin inserts it
+		// make sure that the record belongs to the client group and not the admin group when a dmin inserts it
 		if($_SESSION["s"]["user"]["typ"] == 'admin' && isset($this->dataRecord["client_group_id"])) {
 			$client_group_id = intval($this->dataRecord["client_group_id"]);
 			$app->db->query("UPDATE dns_soa SET sys_groupid = $client_group_id WHERE id = ".$this->id);
 			// And we want to update all rr records too, that belong to this record
 			$app->db->query("UPDATE dns_rr SET sys_groupid = $client_group_id WHERE zone = ".$this->id);
 		}
+		if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) {
+			$client_group_id = intval($this->dataRecord["client_group_id"]);
+			$app->db->query("UPDATE dns_soa SET sys_groupid = $client_group_id WHERE id = ".$this->id);
+			// And we want to update all rr records too, that belong to this record
+			$app->db->query("UPDATE dns_rr SET sys_groupid = $client_group_id WHERE zone = ".$this->id);
+		}
 	}
 	
 }
diff --git a/interface/web/dns/form/dns_soa.tform.php b/interface/web/dns/form/dns_soa.tform.php
index 89ff4f9220de85b8abe85e86e5eedc6d754057fe..7888c04ee91e91034f2055c2fb7ab539b31bf291 100644
--- a/interface/web/dns/form/dns_soa.tform.php
+++ b/interface/web/dns/form/dns_soa.tform.php
@@ -202,7 +202,7 @@ $form["tabs"]['dns_records'] = array (
          	'class'   => 'plugin_listview',
      		'options' => array(
 				'listdef' => 'list/dns_a.list.php',
-				'sqlextwhere' => "zone = ".intval($_REQUEST['id']),
+				'sqlextwhere' => "zone = ".intval(@$_REQUEST['id']),
 				'sql_order_by' => "ORDER BY type, name"
 			)
         )
diff --git a/interface/web/dns/templates/dns_soa_edit.htm b/interface/web/dns/templates/dns_soa_edit.htm
index 03d3ab06054cf5af6c7521a04051e5ca9c85bac2..a5d3811c922048578c74f0775014d51fab78d8fe 100644
--- a/interface/web/dns/templates/dns_soa_edit.htm
+++ b/interface/web/dns/templates/dns_soa_edit.htm
@@ -17,6 +17,16 @@
 	</td>
   </tr>
   </tmpl_if>
+  <tmpl_if name="is_reseller">
+  <tr>
+    <td class="frmText11">{tmpl_var name='client_txt'}:</td>
+    <td class="frmText11">
+		<select name="client_group_id" class="text">
+			{tmpl_var name='client_group_id'}
+		</select>
+	</td>
+  </tr>
+  </tmpl_if>
   <tr>
     <td class="frmText11">{tmpl_var name='origin_txt'}:</td>
     <td class="frmText11"><input name="origin" type="text" class="text" value="{tmpl_var name='origin'}" size="30" maxlength="255"> e.g. mydomain.com.</td>
diff --git a/interface/web/mail/mail_domain_edit.php b/interface/web/mail/mail_domain_edit.php
index b8ca43bc3a6a8c58ae7ada751a50de5eee704f21..a32190d160cdda43f5d4f2a4a48c04def937f2ae 100644
--- a/interface/web/mail/mail_domain_edit.php
+++ b/interface/web/mail/mail_domain_edit.php
@@ -78,11 +78,12 @@ class page_action extends tform_actions {
 	function onShowEnd() {
 		global $app, $conf;
 		
-		if($_SESSION["s"]["user"]["typ"] == 'admin') {
+		if($_SESSION["s"]["user"]["typ"] == 'admin' || $app->auth->has_clients($_SESSION['s']['user']['userid'])) {
 			// Getting Domains of the user
 			$sql = "SELECT groupid, name FROM sys_group WHERE client_id > 0";
 			$clients = $app->db->queryAllRecords($sql);
-			$client_select = "<option value='0'></option>";
+			$client_select = '';
+			if($_SESSION["s"]["user"]["typ"] == 'admin') $client_select .= "<option value='0'></option>";
 			if(is_array($clients)) {
 				foreach( $clients as $client) {
 					$selected = ($client["groupid"] == $this->dataRecord["sys_groupid"])?'SELECTED':'';
@@ -140,7 +141,7 @@ class page_action extends tform_actions {
 			}
 			
 			// Clients may not set the client_group_id, so we unset them if user is not a admin
-			unset($this->dataRecord["client_group_id"]);
+			if(!$app->auth->has_clients($_SESSION['s']['user']['userid'])) unset($this->dataRecord["client_group_id"]);
 		}
 		parent::onSubmit();
 	}
@@ -148,12 +149,16 @@ class page_action extends tform_actions {
 	function onAfterInsert() {
 		global $app, $conf;
 		
-		// make sure that the record belongs to the clinet group and not the admin group when a dmin inserts it
+		// make sure that the record belongs to the client group and not the admin group when a dmin inserts it
 		// also make sure that the user can not delete domain created by a admin
 		if($_SESSION["s"]["user"]["typ"] == 'admin' && isset($this->dataRecord["client_group_id"])) {
 			$client_group_id = intval($this->dataRecord["client_group_id"]);
 			$app->db->query("UPDATE mail_domain SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE domain_id = ".$this->id);
 		}
+		if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) {
+			$client_group_id = intval($this->dataRecord["client_group_id"]);
+			$app->db->query("UPDATE mail_domain SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE domain_id = ".$this->id);
+		}
 		
 		// Spamfilter policy
 		$policy_id = intval($this->dataRecord["policy"]);
@@ -177,12 +182,16 @@ class page_action extends tform_actions {
 	function onAfterUpdate() {
 		global $app, $conf;
 		
-		// make sure that the record belongs to the clinet group and not the admin group when a dmin inserts it
+		// make sure that the record belongs to the clinet group and not the admin group when admin inserts it
 		// also make sure that the user can not delete domain created by a admin
 		if($_SESSION["s"]["user"]["typ"] == 'admin' && isset($this->dataRecord["client_group_id"])) {
 			$client_group_id = intval($this->dataRecord["client_group_id"]);
 			$app->db->query("UPDATE mail_domain SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE domain_id = ".$this->id);
 		}
+		if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) {
+			$client_group_id = intval($this->dataRecord["client_group_id"]);
+			$app->db->query("UPDATE mail_domain SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE domain_id = ".$this->id);
+		}
 		
 		// Spamfilter policy
 		$policy_id = intval($this->dataRecord["policy"]);
diff --git a/interface/web/mail/mail_user_edit.php b/interface/web/mail/mail_user_edit.php
index 88986e95bf850866ea75acf6de419d15f5fb6976..e18b3901847b381d6125e315070af41a0f4f1539 100644
--- a/interface/web/mail/mail_user_edit.php
+++ b/interface/web/mail/mail_user_edit.php
@@ -89,7 +89,7 @@ class page_action extends tform_actions {
 		$domain_select = '';
 		if(is_array($domains)) {
 			foreach( $domains as $domain) {
-				$selected = ($domain["domain"] == $email_parts[1])?'SELECTED':'';
+				$selected = ($domain["domain"] == @$email_parts[1])?'SELECTED':'';
 				$domain_select .= "<option value='$domain[domain]' $selected>$domain[domain]</option>\r\n";
 			}
 		}
diff --git a/interface/web/mail/templates/mail_domain_edit.htm b/interface/web/mail/templates/mail_domain_edit.htm
index 6889b8a37dcfc98209f0104641720b34504c3dd8..2cb315d9e9da4e240e4a1fd53e44a18846d58d4c 100644
--- a/interface/web/mail/templates/mail_domain_edit.htm
+++ b/interface/web/mail/templates/mail_domain_edit.htm
@@ -17,6 +17,16 @@
 	</td>
   </tr>
   </tmpl_if>
+  <tmpl_if name="is_reseller">
+  <tr>
+    <td class="frmText11">{tmpl_var name='client_txt'}:</td>
+    <td class="frmText11">
+		<select name="client_group_id" class="text">
+			{tmpl_var name='client_group_id'}
+		</select>
+	</td>
+  </tr>
+  </tmpl_if>
   <tr>
     <td class="frmText11">{tmpl_var name='domain_txt'}:</td>
     <td class="frmText11"><input name="domain" type="text" class="text" value="{tmpl_var name='domain'}" size="30" maxlength="255"></td>
diff --git a/interface/web/sites/templates/web_domain_edit.htm b/interface/web/sites/templates/web_domain_edit.htm
index 36b4fd851fcbc14460f78f3de595cacdaaeb5ea0..59d725691cda0204ade12151e18c4a0e61f7cd24 100644
--- a/interface/web/sites/templates/web_domain_edit.htm
+++ b/interface/web/sites/templates/web_domain_edit.htm
@@ -17,6 +17,16 @@
 	</td>
   </tr>
   </tmpl_if>
+  <tmpl_if name="is_reseller">
+  <tr>
+    <td class="frmText11">{tmpl_var name='client_txt'}:</td>
+    <td class="frmText11">
+		<select name="client_group_id" class="text">
+			{tmpl_var name='client_group_id'}
+		</select>
+	</td>
+  </tr>
+  </tmpl_if>
   <tr>
     <td class="frmText11">{tmpl_var name='ip_address_txt'}:</td>
     <td class="frmText11">
diff --git a/interface/web/sites/web_domain_edit.php b/interface/web/sites/web_domain_edit.php
index 6790f3ccbe71d147441735b4ef075e1ae39a9f25..5c571c20af75d09a1787ef2dd23afe1d3b6c5caa 100644
--- a/interface/web/sites/web_domain_edit.php
+++ b/interface/web/sites/web_domain_edit.php
@@ -78,11 +78,11 @@ class page_action extends tform_actions {
 	function onShowEnd() {
 		global $app, $conf;
 		
-		if($_SESSION["s"]["user"]["typ"] != 'admin') {
+		if($_SESSION["s"]["user"]["typ"] != 'admin' && !$app->auth->has_clients($_SESSION['s']['user']['userid'])) {
 		
 			// Get the limits of the client
 			$client_group_id = $_SESSION["s"]["user"]["default_group"];
-			$client = $app->db->queryOneRecord("SELECT limit_maildomain, default_mailserver FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
+			$client = $app->db->queryOneRecord("SELECT limit_web_domain, default_webserver FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
 			
 			// Set the webserver to the default server of the client
 			$tmp = $app->db->queryOneRecord("SELECT server_name FROM server WHERE server_id = $client[default_webserver]");
@@ -90,6 +90,35 @@ class page_action extends tform_actions {
 			unset($tmp);
 			
 			// Fill the IP select field with the IP addresses that are allowed for this client
+			$ip_select = "<option value='*'>*</option>";
+			$app->tpl->setVar("ip_address",$ip_select);
+			
+		} elseif ($_SESSION["s"]["user"]["typ"] != 'admin' && $app->auth->has_clients($_SESSION['s']['user']['userid'])) {
+			
+			// Get the limits of the client
+			$client_group_id = $_SESSION["s"]["user"]["default_group"];
+			$client = $app->db->queryOneRecord("SELECT limit_web_domain, default_webserver FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
+			
+			// Set the webserver to the default server of the client
+			$tmp = $app->db->queryOneRecord("SELECT server_name FROM server WHERE server_id = $client[default_webserver]");
+			$app->tpl->setVar("server_id","<option value='$client[default_webserver]'>$tmp[server_name]</option>");
+			unset($tmp);
+			
+			// Fill the client select field
+			$sql = "SELECT groupid, name FROM sys_group WHERE client_id > 0";
+			$clients = $app->db->queryAllRecords($sql);
+			$client_select = '';
+			if(is_array($clients)) {
+				foreach( $clients as $client) {
+					$selected = @($client["groupid"] == $this->dataRecord["sys_groupid"])?'SELECTED':'';
+					$client_select .= "<option value='$client[groupid]' $selected>$client[name]</option>\r\n";
+				}
+			}
+			$app->tpl->setVar("client_group_id",$client_select);
+			
+			// Fill the IP select field with the IP addresses that are allowed for this client
+			$ip_select = "<option value='*'>*</option>";
+			$app->tpl->setVar("ip_address",$ip_select);
 			
 		} else {
 			
@@ -169,8 +198,8 @@ class page_action extends tform_actions {
 				
 			}
 			
-			// Clients may not set the client_group_id, so we unset them if user is not a admin
-			unset($this->dataRecord["client_group_id"]);
+			// Clients may not set the client_group_id, so we unset them if user is not a admin and the client is not a reseller
+			if(!$app->auth->has_clients($_SESSION['s']['user']['userid'])) unset($this->dataRecord["client_group_id"]);
 		}
 		
 		
@@ -186,6 +215,10 @@ class page_action extends tform_actions {
 			$client_group_id = intval($this->dataRecord["client_group_id"]);
 			$app->db->query("UPDATE web_domain SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE domain_id = ".$this->id);
 		}
+		if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) {
+			$client_group_id = intval($this->dataRecord["client_group_id"]);
+			$app->db->query("UPDATE web_domain SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE domain_id = ".$this->id);
+		}
 		
 		// Get configuration for the web system
 		$app->uses("getconf");
@@ -222,6 +255,10 @@ class page_action extends tform_actions {
 			$client_group_id = intval($this->dataRecord["client_group_id"]);
 			$app->db->query("UPDATE web_domain SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE domain_id = ".$this->id);
 		}
+		if($app->auth->has_clients($_SESSION['s']['user']['userid']) && isset($this->dataRecord["client_group_id"])) {
+			$client_group_id = intval($this->dataRecord["client_group_id"]);
+			$app->db->query("UPDATE web_domain SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE domain_id = ".$this->id);
+		}
 		
 		// Get configuration for the web system
 		$app->uses("getconf");
@@ -230,7 +267,7 @@ class page_action extends tform_actions {
 		$document_root = str_replace("[website_id]",$this->id,$web_config["website_path"]);
 		
 		// get the ID of the client
-		if($_SESSION["s"]["user"]["typ"] != 'admin') {
+		if($_SESSION["s"]["user"]["typ"] != 'admin' && !$app->auth->has_clients($_SESSION['s']['user']['userid'])) {
 			$client_group_id = $_SESSION["s"]["user"]["default_group"];
 			$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"]);
diff --git a/server/mods-enabled/client_module.inc.php b/server/mods-enabled/client_module.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..d7de2f5544736f73b791795a5cf6ccd2ceb29ae0
--- /dev/null
+++ b/server/mods-enabled/client_module.inc.php
@@ -0,0 +1,87 @@
+<?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.
+*/
+
+class web_module {
+	
+	var $module_name = 'client_module';
+	var $class_name = 'client_module';
+	var $actions_available = array(	'client_insert',
+									'client_update',
+									'client_delete');
+	
+	/*
+	 	This function is called when the module is loaded
+	*/
+	
+	function onLoad() {
+		global $app;
+		
+		/*
+		Annonce the actions that where provided by this module, so plugins 
+		can register on them.
+		*/
+		
+		$app->plugins->announceEvents($this->module_name,$this->actions_available);
+		
+		/*
+		As we want to get notified of any changes on several database tables,
+		we register for them.
+		
+		The following function registers the function "functionname"
+ 		to be executed when a record for the table "dbtable" is 
+ 		processed in the sys_datalog. "classname" is the name of the
+ 		class that contains the function functionname.
+		*/
+		
+		$app->modules->registerTableHook('client',$this->module_name,'process');
+		
+	}
+	
+	/*
+	 This function is called when a change in one of the registered tables is detected.
+	 The function then raises the events for the plugins.
+	*/
+
+	function process($tablename,$action,$data) {
+		global $app;
+		
+		switch ($tablename) {
+			case 'client':
+				if($action == 'i') $app->plugins->raiseEvent('client_insert',$data);
+				if($action == 'u') $app->plugins->raiseEvent('client_update',$data);
+				if($action == 'd') $app->plugins->raiseEvent('client_delete',$data);
+			break;
+		} // end switch
+	} // end function
+	
+
+} // end class
+
+?>
\ No newline at end of file
diff --git a/server/plugins-enabled/mail_plugin.inc.php b/server/plugins-enabled/mail_plugin.inc.php
new file mode 100644
index 0000000000000000000000000000000000000000..6e1fe30588bfed8eb5377583f04f43554c8e4b06
--- /dev/null
+++ b/server/plugins-enabled/mail_plugin.inc.php
@@ -0,0 +1,109 @@
+<?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.
+*/
+
+class mail_plugin {
+	
+	var $plugin_name = 'mail_plugin';
+	var $class_name  = 'mail_plugin';
+	
+		
+	/*
+	 	This function is called when the plugin is loaded
+	*/
+	
+	function onLoad() {
+		global $app;
+		
+		/*
+		Register for the events
+		*/
+		
+		$app->plugins->registerEvent('mail_user_insert',$this->plugin_name,'user_insert');
+		$app->plugins->registerEvent('mail_user_update',$this->plugin_name,'user_update');
+		$app->plugins->registerEvent('mail_user_delete',$this->plugin_name,'user_delete');
+
+		
+	}
+	
+	
+	function user_insert($event_name,$data) {
+		global $app, $conf;
+		
+		// Create the maildir, if it does not exist
+		if(!is_dir($data['new']['maildir']) {
+			mkdir($data['new']['maildir']);
+			exec('chown '.$mail_config['mailuser_name'].':'.$mail_config['mailuser_group'].' '.escapeshellcmd($data['new']['maildir']));
+			$app->log('Created Maildir: '.$data['new']['maildir'],LOGLEVEL_DEBUG);
+		}
+		
+	}
+	
+	function user_update($event_name,$data) {
+		global $app, $conf;
+		
+		// get the config
+		$app->uses("getconf");
+		$mail_config = $app->getconf->get_server_config($conf["server_id"], 'mail');
+		
+		// Create the maildir, if it does not exist
+		if(!is_dir($data['new']['maildir']) {
+			mkdir($data['new']['maildir']);
+			exec('chown '.$mail_config['mailuser_name'].':'.$mail_config['mailuser_group'].' '.escapeshellcmd($data['new']['maildir']));
+			$app->log('Created Maildir: '.$data['new']['maildir'],LOGLEVEL_DEBUG);
+		}
+		
+		// Move mailbox, if domain has changed and delete old mailbox
+		if($data['new']['maildir'] != $data['old']['maildir'] && is_dir($data['old']['maildir'])) {
+			exec('mv -f'.escapeshellcmd($data['old']['maildir']).'* '.escapeshellcmd($data['new']['maildir']));
+			unlink($data['old']['maildir']);
+			$app->log('Moved Maildir from: '.$data['old']['maildir'].' to '.$data['new']['maildir'],LOGLEVEL_DEBUG);
+		}
+		
+	}
+	
+	function user_delete($event_name,$data) {
+		global $app, $conf;
+		
+		$old_maildir_path = escapeshellcmd($data['old']['maildir']);
+		if(!stristr($old_maildir_path,'..') && !stristr($old_maildir_path,'*') && strlen($old_maildir_path) >= 10) {
+			exec('rm -rf '.$old_maildir_path);
+			$app->log('Deleted the Maildir: '.$data['old']['maildir'],LOGLEVEL_DEBUG);
+		} else {
+			$app->log('Possible security violation when deleting the maildir: '.$data['old']['maildir'],LOGLEVEL_ERROR);
+		}
+		
+	}
+	
+	
+	
+
+} // end class
+
+?>
\ No newline at end of file