From 5a43e7a2ea0cf7af35c100cb67e4a53566cbc496 Mon Sep 17 00:00:00 2001
From: tbrehm <t.brehm@ispconfig.org>
Date: Fri, 03 Feb 2012 05:58:01 -0500
Subject: [PATCH] - Implemented new backup and restore functions for websites and databases (see also FS#1389) - Added "actions" framework in server to replace the functions provided by the core modules - Moved system update function from remoteactins core module to software update plugin.

---
 interface/web/sites/templates/database_edit.htm             |   18 
 server/plugins-available/openvz_plugin.inc.php              |   54 +++
 interface/web/mail/mail_user_edit.php                       |    8 
 interface/web/sites/database_edit.php                       |   93 +---
 install/tpl/server.ini.master                               |    2 
 server/plugins-available/software_update_plugin.inc.php     |   34 +
 interface/web/admin/form/server_config.tform.php            |   10 
 interface/web/admin/templates/server_config_server_edit.htm |   10 
 interface/web/sites/lib/lang/en_web_backup_list.lng         |   16 
 server/plugins-available/mysql_clientdb_plugin.inc.php      |   12 
 install/sql/ispconfig3.sql                                  |   20 +
 server/lib/classes/modules.inc.php                          |   38 ++
 server/mods-available/remoteaction_core_module.inc.php      |    7 
 interface/lib/classes/plugin_backuplist.inc.php             |  138 ++++++++
 interface/web/sites/templates/web_domain_backup.htm         |    2 
 interface/web/sites/form/database.tform.php                 |   11 
 interface/web/themes/default/css/screen/content_ispc.css    |    2 
 interface/web/sites/form/web_domain.tform.php               |   11 
 server/cron_daily.php                                       |  162 +++++++-
 interface/web/js/scrigo.js.php                              |    6 
 server/plugins-available/backup_plugin.inc.php              |  134 +++++++
 interface/web/sites/templates/web_backup_list.htm           |   40 ++
 interface/web/sites/lib/lang/en_database.lng                |    5 
 server/lib/classes/db_mysql.inc.php                         |    7 
 server/lib/classes/plugins.inc.php                          |   42 ++
 interface/web/admin/lib/lang/en_server_config.lng           |    5 
 interface/web/sites/web_domain_edit.php                     |   23 +
 interface/web/sites/tools.inc.php                           |    2 
 server/server.php                                           |   33 -
 server/mods-available/web_module.inc.php                    |   11 
 install/sql/incremental/upd_0028.sql                        |   12 
 interface/web/sites/web_domain_del.php                      |    6 
 32 files changed, 806 insertions(+), 168 deletions(-)

diff --git a/install/sql/incremental/upd_0028.sql b/install/sql/incremental/upd_0028.sql
new file mode 100644
index 0000000..0020cdd
--- /dev/null
+++ b/install/sql/incremental/upd_0028.sql
@@ -0,0 +1,12 @@
+ALTER TABLE  `web_database` ADD  `parent_domain_id` int(11) unsigned NOT NULL DEFAULT  '0' AFTER  `server_id`;
+ALTER TABLE  `web_database` ADD  `backup_interval` VARCHAR( 255 ) NOT NULL DEFAULT 'none', ADD `backup_copies` INT NOT NULL DEFAULT '1' AFTER  `remote_ips`;
+CREATE TABLE `web_backup` (
+  `backup_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `server_id` int(10) unsigned NOT NULL,
+  `parent_domain_id` int(10) unsigned NOT NULL,
+  `backup_type` enum('web','mysql') NOT NULL DEFAULT 'web',
+  `backup_mode` varchar(64) NOT NULL DEFAULT  '',
+  `tstamp` int(10) unsigned NOT NULL,
+  `filename` varchar(255) NOT NULL,
+  PRIMARY KEY (`backup_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql
index 9c2d5cf..fdd31a8 100644
--- a/install/sql/ispconfig3.sql
+++ b/install/sql/ispconfig3.sql
@@ -1438,6 +1438,23 @@
 -- --------------------------------------------------------
 
 --
+-- Table structure for table `web_backup`
+--
+
+CREATE TABLE `web_backup` (
+  `backup_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `server_id` int(10) unsigned NOT NULL,
+  `parent_domain_id` int(10) unsigned NOT NULL,
+  `backup_type` enum('web','mysql') NOT NULL DEFAULT 'web',
+  `backup_mode` varchar(64) NOT NULL DEFAULT  '',
+  `tstamp` int(10) unsigned NOT NULL,
+  `filename` varchar(255) NOT NULL,
+  PRIMARY KEY (`backup_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
+
+-- --------------------------------------------------------
+
+--
 -- Table structure for table `web_database`
 --
 
@@ -1449,6 +1466,7 @@
   `sys_perm_group` varchar(5) DEFAULT NULL,
   `sys_perm_other` varchar(5) DEFAULT NULL,
   `server_id` int(11) unsigned NOT NULL DEFAULT '0',
+  `parent_domain_id` int(11) unsigned NOT NULL DEFAULT  '0',
   `type` varchar(16) NOT NULL DEFAULT 'y',
   `database_name` varchar(64) DEFAULT NULL,
   `database_user` varchar(64) DEFAULT NULL,
@@ -1456,6 +1474,8 @@
   `database_charset` varchar(64) DEFAULT NULL,
   `remote_access` enum('n','y') NOT NULL DEFAULT 'y',
   `remote_ips` text NOT NULL,
+  `backup_interval` VARCHAR( 255 ) NOT NULL DEFAULT 'none',
+  `backup_copies` INT NOT NULL DEFAULT '1',
   `active` enum('n','y') NOT NULL DEFAULT 'y',
   PRIMARY KEY (`database_id`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
diff --git a/install/tpl/server.ini.master b/install/tpl/server.ini.master
index 71e61e6..275fa7a 100644
--- a/install/tpl/server.ini.master
+++ b/install/tpl/server.ini.master
@@ -13,7 +13,7 @@
 nameservers=192.168.0.1,192.168.0.2
 loglevel=2
 backup_dir=/var/backup
-backup_dir_ftpread=n
+backup_mode=rootgz
 
 [mail]
 module=postfix_mysql
diff --git a/interface/lib/classes/plugin_backuplist.inc.php b/interface/lib/classes/plugin_backuplist.inc.php
new file mode 100644
index 0000000..ac0396b
--- /dev/null
+++ b/interface/lib/classes/plugin_backuplist.inc.php
@@ -0,0 +1,138 @@
+<?php
+
+/*
+Copyright (c) 2012, Till Brehm, ISPConfig UG
+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 plugin_backuplist extends plugin_base {
+
+        var $module;
+        var $form;
+        var $tab;
+        var $record_id;
+        var $formdef;
+        var $options;
+
+        function onShow() {
+
+                global $app;
+				
+				$listTpl = new tpl;
+                $listTpl->newTemplate('templates/web_backup_list.htm');
+				
+				//* Loading language file
+                $lng_file = "lib/lang/".$_SESSION["s"]["language"]."_web_backup_list.lng";
+                include($lng_file);
+                $listTpl->setVar($wb);
+				
+				$message = '';
+				$error = '';
+				
+				if(isset($_GET['backup_action'])) {
+					$backup_id = intval($_GET['backup_id']);
+					
+					if($_GET['backup_action'] == 'download' && $backup_id > 0) {
+						$sql = "SELECT count(action_id) as number FROM sys_remoteaction WHERE action_state = 'pending' AND action_type = 'backup_download' AND action_param = '$backup_id'";
+						$tmp = $app->db->queryOneRecord($sql);
+						if($tmp['number'] == 0) {
+							$message .= $wb['download_info_txt'];
+							$sql = 	"INSERT INTO sys_remoteaction (server_id, tstamp, action_type, action_param, action_state, response) " .
+								"VALUES (".
+								(int)$this->form->dataRecord['server_id'] . ", " .
+								time() . ", " .
+								"'backup_download', " .
+								"'".$backup_id."', " .
+								"'pending', " .
+								"''" .
+								")";
+							$app->db->query($sql);
+						} else {
+							$error .= $wb['download_pending_txt'];
+						}
+					}
+					if($_GET['backup_action'] == 'restore' && $backup_id > 0) {
+						$sql = "SELECT count(action_id) as number FROM sys_remoteaction WHERE action_state = 'pending' AND action_type = 'backup_restore' AND action_param = '$backup_id'";
+						$tmp = $app->db->queryOneRecord($sql);
+						if($tmp['number'] == 0) {
+							$message .= $wb['restore_info_txt'];
+							$sql = 	"INSERT INTO sys_remoteaction (server_id, tstamp, action_type, action_param, action_state, response) " .
+								"VALUES (".
+								(int)$this->form->dataRecord['server_id'] . ", " .
+								time() . ", " .
+								"'backup_restore', " .
+								"'".$backup_id."', " .
+								"'pending', " .
+								"''" .
+								")";
+						$app->db->query($sql);
+						} else {
+							$error .= $wb['restore_pending_txt'];
+						}
+					}
+					
+				}
+				
+				//* Get the data
+				$sql = "SELECT * FROM web_backup WHERE parent_domain_id = ".$this->form->id." ORDER BY tstamp DESC, backup_type ASC";
+                $records = $app->db->queryAllRecords($sql);
+
+                $bgcolor = "#FFFFFF";
+                if(is_array($records)) {
+                        foreach($records as $rec) {
+
+                                // Change of color
+                                $bgcolor = ($bgcolor == "#FFFFFF")?"#EEEEEE":"#FFFFFF";
+                                $rec["bgcolor"] = $bgcolor;
+								
+								$rec['date'] = date($app->lng('conf_format_datetime'),$rec['tstamp']);
+								$rec['backup_type'] = $wb[('backup_type_'.$rec['backup_type'])];
+
+                                $records_new[] = $rec;
+                        }
+                }
+
+                $listTpl->setLoop('records',@$records_new);
+				
+				$listTpl->setVar('parent_id',$this->form->id);
+				$listTpl->setVar('msg',$message);
+				$listTpl->setVar('error',$error);
+				
+				// Setting Returnto information in the session
+                $list_name = 'backup_list';
+                // $_SESSION["s"]["list"][$list_name]["parent_id"] = $app->tform_actions->id;
+				$_SESSION["s"]["list"][$list_name]["parent_id"] = $this->form->id;
+				$_SESSION["s"]["list"][$list_name]["parent_name"] = $app->tform->formDef["name"];
+                $_SESSION["s"]["list"][$list_name]["parent_tab"] = $_SESSION["s"]["form"]["tab"];
+                $_SESSION["s"]["list"][$list_name]["parent_script"] = $app->tform->formDef["action"];
+                $_SESSION["s"]["form"]["return_to"] = $list_name;
+				
+				return $listTpl->grab();
+        }
+		
+}
+
+?>
\ No newline at end of file
diff --git a/interface/web/admin/form/server_config.tform.php b/interface/web/admin/form/server_config.tform.php
index 027328d..24f3f18 100644
--- a/interface/web/admin/form/server_config.tform.php
+++ b/interface/web/admin/form/server_config.tform.php
@@ -138,11 +138,13 @@
 			'width' => '40',
 			'maxlength' => '255'
 		),
-		'backup_dir_ftpread' => array(
+		'backup_mode' => array(
 			'datatype' => 'VARCHAR',
-			'formtype' => 'CHECKBOX',
-			'default' => 'y',
-			'value' => array(0 => 'n', 1 => 'y')
+			'formtype' => 'SELECT',
+			'default' => 'userzip',
+			'value' => array('userzip' => 'backup_mode_userzip', 'rootgz' => 'backup_mode_rootgz'),
+			'width' => '40',
+			'maxlength' => '255'
 		),
 	##################################
 	# ENDE Datatable fields
diff --git a/interface/web/admin/lib/lang/en_server_config.lng b/interface/web/admin/lib/lang/en_server_config.lng
index ec2e053..4cce275 100644
--- a/interface/web/admin/lib/lang/en_server_config.lng
+++ b/interface/web/admin/lib/lang/en_server_config.lng
@@ -87,7 +87,9 @@
 $wb["CA_path_txt"] = 'CA Path';
 $wb["CA_pass_txt"] = 'CA passphrase';
 $wb["fastcgi_config_syntax_txt"] = 'FastCGI config syntax';
-$wb["backup_dir_ftpread_txt"] = 'Backup dir. readable for website FTP users.';
+$wb["backup_mode_txt"] = 'Backup mode';
+$wb["backup_mode_userzip"] = 'Backup web files owned by web user as zip';
+$wb["backup_mode_rootgz"] = 'Backup all files in web directory as root user';
 $wb["server_type_txt"] = 'Server Type';
 $wb["nginx_vhost_conf_dir_txt"] = 'Nginx Vhost config dir';
 $wb["nginx_vhost_conf_enabled_dir_txt"] = 'Nginx Vhost config enabled dir';
@@ -153,7 +155,6 @@
 $wb["add_web_users_to_sshusers_group_txt"] = 'Add web users to -sshusers- group';
 $wb["connect_userid_to_webid_txt"] = 'Connect Linux userid to webid';
 $wb["connect_userid_to_webid_start_txt"] = 'Start ID for userid/webid connect';
-
 $wb["realtime_blackhole_list_txt"] = 'Real-time Blackhole List';
 $wb["realtime_blackhole_list_note_txt"] = '(Separate RBL\'s with commas)';
 ?>
\ No newline at end of file
diff --git a/interface/web/admin/templates/server_config_server_edit.htm b/interface/web/admin/templates/server_config_server_edit.htm
index 8f42d8c..7b777bf 100644
--- a/interface/web/admin/templates/server_config_server_edit.htm
+++ b/interface/web/admin/templates/server_config_server_edit.htm
@@ -43,11 +43,11 @@
         <input name="backup_dir" id="backup_dir" value="{tmpl_var name='backup_dir'}" size="40" maxlength="255" type="text" class="textInput" />
 	  </div>
 	  <div class="ctrlHolder">
-		<p class="label">{tmpl_var name='backup_dir_ftpread_txt'}</p>
-		  <div class="multiField">
-			{tmpl_var name='backup_dir_ftpread'}
-		  </div>
-	  </div>
+      	<label for="backup_mode">{tmpl_var name='backup_mode_txt'}</label>
+        <select name="backup_mode" id="backup_mode" class="selectInput">
+					{tmpl_var name='backup_mode'}
+				</select>
+      </div>
     </fieldset>
 
     <input type="hidden" name="id" value="{tmpl_var name='id'}">
diff --git a/interface/web/js/scrigo.js.php b/interface/web/js/scrigo.js.php
index 94b7075..f8da538 100644
--- a/interface/web/js/scrigo.js.php
+++ b/interface/web/js/scrigo.js.php
@@ -269,6 +269,12 @@
   }
 }
 
+function confirm_action(link,confirmation) {
+  if(window.confirm(confirmation)) {
+          loadContent(link);
+  }
+}
+
 function loadContentInto(elementid,pagename) {
   var pageContentObject2 = jQuery.ajax({	type: "GET", 
 											url: pagename,
diff --git a/interface/web/mail/mail_user_edit.php b/interface/web/mail/mail_user_edit.php
index f57a4e8..398bf12 100644
--- a/interface/web/mail/mail_user_edit.php
+++ b/interface/web/mail/mail_user_edit.php
@@ -231,14 +231,14 @@
 		// Spamfilter policy
 		$policy_id = intval($this->dataRecord["policy"]);
 		if($policy_id > 0) {
-			$tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".mysql_real_escape_string($this->dataRecord["email"])."'");
+			$tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".$app->db->quote($this->dataRecord["email"])."'");
 			if($tmp_user["id"] > 0) {
 				// There is already a record that we will update
 				$app->db->datalogUpdate('spamfilter_users', "policy_id = $policy_id", 'id', $tmp_user["id"]);
 			} else {
 				// We create a new record
 				$insert_data = "(`sys_userid`, `sys_groupid`, `sys_perm_user`, `sys_perm_group`, `sys_perm_other`, `server_id`, `priority`, `policy_id`, `email`, `fullname`, `local`) 
-				        VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".mysql_real_escape_string($this->dataRecord["email"])."', '".mysql_real_escape_string($this->dataRecord["email"])."', 'Y')";
+				        VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".$app->db->quote($this->dataRecord["email"])."', '".$app->db->quote($this->dataRecord["email"])."', 'Y')";
 				$app->db->datalogInsert('spamfilter_users', $insert_data, 'id');
 			}
 		}  // endif spamfilter policy
@@ -266,7 +266,7 @@
 		
 			// Spamfilter policy
 			$policy_id = intval($this->dataRecord["policy"]);
-			$tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".mysql_real_escape_string($this->dataRecord["email"])."'");
+			$tmp_user = $app->db->queryOneRecord("SELECT id FROM spamfilter_users WHERE email = '".$app->db->quote($this->dataRecord["email"])."'");
 			if($policy_id > 0) {
 				if($tmp_user["id"] > 0) {
 					// There is already a record that we will update
@@ -274,7 +274,7 @@
 				} else {
 					// We create a new record
 					$insert_data = "(`sys_userid`, `sys_groupid`, `sys_perm_user`, `sys_perm_group`, `sys_perm_other`, `server_id`, `priority`, `policy_id`, `email`, `fullname`, `local`) 
-				        	VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".mysql_real_escape_string($this->dataRecord["email"])."', '".mysql_real_escape_string($this->dataRecord["email"])."', 'Y')";
+				        	VALUES (".$_SESSION["s"]["user"]["userid"].", ".$domain["sys_groupid"].", 'riud', 'riud', '', ".$domain["server_id"].", 10, ".$policy_id.", '".$app->db->quote($this->dataRecord["email"])."', '".$app->db->quote($this->dataRecord["email"])."', 'Y')";
 					$app->db->datalogInsert('spamfilter_users', $insert_data, 'id');
 				}
 			}else {
diff --git a/interface/web/sites/database_edit.php b/interface/web/sites/database_edit.php
index e066943..501c650 100644
--- a/interface/web/sites/database_edit.php
+++ b/interface/web/sites/database_edit.php
@@ -91,20 +91,6 @@
 			$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, client WHERE sys_group.client_id = client.client_id AND client.parent_client_id = ".$client['client_id']." ORDER BY name";
-			$clients = $app->db->queryAllRecords($sql);
-			$tmp = $app->db->queryOneRecord("SELECT groupid FROM sys_group WHERE client_id = ".$client['client_id']);
-			$client_select = '<option value="'.$tmp['groupid'].'">'.$client['contact_name'].'</option>';
-			//$tmp_data_record = $app->tform->getDataRecord($this->id);
-			if(is_array($clients)) {
-				foreach( $clients as $client) {
-					$selected = @(is_array($this->dataRecord) && ($client["groupid"] == $this->dataRecord['client_group_id'] || $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);
 
 		} else {
 
@@ -116,33 +102,6 @@
 				$tmp = $app->db->queryOneRecord("SELECT server_id FROM server WHERE web_server = 1 ORDER BY server_name LIMIT 0,1");
 				$server_id = $tmp['server_id'];
 			}
-
-			$sql = "SELECT ip_address FROM server_ip WHERE server_id = $server_id";
-			$ips = $app->db->queryAllRecords($sql);
-			$ip_select = "<option value='*'>*</option>";
-			//$ip_select = "";
-			if(is_array($ips)) {
-				foreach( $ips as $ip) {
-					$selected = ($ip["ip_address"] == $this->dataRecord["ip_address"])?'SELECTED':'';
-					$ip_select .= "<option value='$ip[ip_address]' $selected>$ip[ip_address]</option>\r\n";
-				}
-			}
-			$app->tpl->setVar("ip_address",$ip_select);
-			unset($tmp);
-			unset($ips);
-
-			// Fill the client select field
-			$sql = "SELECT groupid, name FROM sys_group WHERE client_id > 0 ORDER BY name";
-			$clients = $app->db->queryAllRecords($sql);
-			$client_select = "<option value='0'></option>";
-			//$tmp_data_record = $app->tform->getDataRecord($this->id);
-			if(is_array($clients)) {
-				foreach( $clients as $client) {
-					$selected = @(is_array($this->dataRecord) && ($client["groupid"] == $this->dataRecord['client_group_id'] || $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);
 
 		}
 
@@ -212,9 +171,6 @@
 				}
 
 			}
-
-			// 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"]);
 		}
 
 
@@ -224,10 +180,8 @@
 	function onBeforeUpdate() {
 		global $app, $conf, $interfaceConf;
 
-		/*
-		* If the names should be restricted -> do it!
-		*/
-		
+		//* Site shell not be empty
+		if($this->dataRecord['parent_domain_id'] == 0) $app->tform->errorMessage .= $app->tform->lng("database_site_error_empty").'<br />';
 		
 		//* Get the database name and database user prefix
 		$app->uses('getconf');
@@ -290,6 +244,9 @@
 	function onBeforeInsert() {
 		global $app, $conf, $interfaceConf;
 		
+		//* Site shell not be empty
+		if($this->dataRecord['parent_domain_id'] == 0) $app->tform->errorMessage .= $app->tform->lng("database_site_error_empty").'<br />';
+		
 		//* Database username and database name shall not be empty
 		if($this->dataRecord['database_name'] == '') $app->tform->errorMessage .= $app->tform->wordbook["database_name_error_empty"].'<br />';
 		if($this->dataRecord['database_user'] == '') $app->tform->errorMessage .= $app->tform->wordbook["database_user_error_empty"].'<br />';
@@ -330,31 +287,33 @@
 
 	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
-		// 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 web_database SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE database_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_database SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE database_id = ".$this->id);
+		
+		if($this->dataRecord["parent_domain_id"] > 0) {
+			$web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".intval($this->dataRecord["parent_domain_id"]));
+		
+			//* The Database user shall be owned by the same group then the website
+			$sys_groupid = $web['sys_groupid'];
+			$backup_interval = $web['backup_interval'];
+			$backup_copies = $web['backup_copies'];
+		
+			$sql = "UPDATE web_database SET sys_groupid = '$sys_groupid', backup_interval = '$backup_interval', backup_copies = '$backup_copies' WHERE database_id = ".$this->id;
+			$app->db->query($sql);
 		}
 	}
 
 	function onAfterUpdate() {
 		global $app, $conf;
 
-		// make sure that the record belongs to the client group and not the admin group when a 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 web_database SET sys_groupid = $client_group_id, sys_perm_group = 'ru' WHERE database_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_database SET sys_groupid = $client_group_id, sys_perm_group = 'riud' WHERE database_id = ".$this->id);
+		if($this->dataRecord["parent_domain_id"] > 0) {
+			$web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".intval($this->dataRecord["parent_domain_id"]));
+		
+			//* The Database user shall be owned by the same group then the website
+			$sys_groupid = $web['sys_groupid'];
+			$backup_interval = $web['backup_interval'];
+			$backup_copies = $web['backup_copies'];
+		
+			$sql = "UPDATE web_database SET sys_groupid = '$sys_groupid', backup_interval = '$backup_interval', backup_copies = '$backup_copies' WHERE database_id = ".$this->id;
+			$app->db->query($sql);
 		}
 
 	}
diff --git a/interface/web/sites/form/database.tform.php b/interface/web/sites/form/database.tform.php
index ffb711f..9bef109 100644
--- a/interface/web/sites/form/database.tform.php
+++ b/interface/web/sites/form/database.tform.php
@@ -69,6 +69,17 @@
 									 ),
 			'value'		=> ''
 		),
+		'parent_domain_id' => array (
+			'datatype'	=> 'INTEGER',
+			'formtype'	=> 'SELECT',
+			'default'	=> '',
+			'datasource'	=> array ( 	'type'	=> 'SQL',
+										'querystring' => "SELECT domain_id,domain FROM web_domain WHERE type = 'vhost' AND {AUTHSQL} ORDER BY domain",
+										'keyfield'=> 'domain_id',
+										'valuefield'=> 'domain'
+									 ),
+			'value'		=> array('0' => $app->tform->lng('select_site_txt'))
+		),
 		'type' => array (
 			'datatype'	=> 'VARCHAR',
 			'formtype'	=> 'SELECT',
diff --git a/interface/web/sites/form/web_domain.tform.php b/interface/web/sites/form/web_domain.tform.php
index 34cbbc8..241e6ea 100644
--- a/interface/web/sites/form/web_domain.tform.php
+++ b/interface/web/sites/form/web_domain.tform.php
@@ -431,7 +431,7 @@
 	)
 );
 
-if($_SESSION["s"]["user"]["typ"] == 'admin') {
+// if($_SESSION["s"]["user"]["typ"] == 'admin') {
 
 //* Backup
 $form["tabs"]['backup'] = array (
@@ -458,10 +458,17 @@
 	##################################
 	# ENDE Datatable fields
 	##################################
+	),
+	'plugins' => array (
+     	'backup_records' => array (
+         	'class'   => 'plugin_backuplist',
+     		'options' => array(
+			)
+        )
 	)
 );
 
-}
+// }
 
 if($_SESSION["s"]["user"]["typ"] == 'admin') {
 
diff --git a/interface/web/sites/lib/lang/en_database.lng b/interface/web/sites/lib/lang/en_database.lng
index 48d7ea3..abf302e 100644
--- a/interface/web/sites/lib/lang/en_database.lng
+++ b/interface/web/sites/lib/lang/en_database.lng
@@ -22,4 +22,9 @@
 $wb["database_charset_change_txt"] = 'The database charset can not be changed';
 $wb["database_name_error_len"] = 'Database name - {db} - too long. The max. database name length incl. prefix is 64 chars.';
 $wb["database_user_error_len"] = 'Database username - {user}- too long. The max. database username length incl. prefix is 16 chars.';
+$wb["parent_domain_id_txt"] = 'Site';
+$wb["database_site_error_empty"] = 'Select the site to which the database belongs.';
+$wb["select_site_txt"] = '- Select Site -';
+$wb["btn_save_txt"] = 'Save';
+$wb["btn_cancel_txt"] = 'Cancel';
 ?>
diff --git a/interface/web/sites/lib/lang/en_web_backup_list.lng b/interface/web/sites/lib/lang/en_web_backup_list.lng
new file mode 100644
index 0000000..1ac6687
--- /dev/null
+++ b/interface/web/sites/lib/lang/en_web_backup_list.lng
@@ -0,0 +1,16 @@
+<?php
+$wb['list_head_txt'] = 'Existing backups';
+$wb['date_txt'] = 'Date';
+$wb['backup_type_txt'] = 'Type';
+$wb['filename_txt'] = 'Backup file';
+$wb['restore_backup_txt'] = 'Restore backup';
+$wb['download_backup_txt'] = 'Download backup';
+$wb['download_info_txt'] = 'The backup file will be available for download in the backup folder of the website in a few minutes.';
+$wb['restore_info_txt'] = 'Restore of the backup has been started. This action takes several minutes to be completed.';
+$wb['restore_confirm_txt'] = 'Restoring will overwrite existing files in your website. Do you really want to restore this backup?';
+$wb['download_pending_txt'] = 'There is already a pending backup download job.';
+$wb['restore_pending_txt'] = 'There is already a pending backup restore job.';
+$wb['backup_type_mysql'] = 'MySQL Database';
+$wb['backup_type_web'] = 'Website files';
+
+?>
\ No newline at end of file
diff --git a/interface/web/sites/templates/database_edit.htm b/interface/web/sites/templates/database_edit.htm
index 99e1d07..59b1bff 100644
--- a/interface/web/sites/templates/database_edit.htm
+++ b/interface/web/sites/templates/database_edit.htm
@@ -20,21 +20,13 @@
 		</select>
 		</tmpl_if>
       </div>
-      <div class="ctrlHolder">
-      	<label for="client_group_id">{tmpl_var name='client_txt'}</label>
-        <select name="client_group_id" id="client_group_id" class="selectInput">
-					{tmpl_var name='client_group_id'}
-		</select>
-      </div>
       </tmpl_if>
-      <tmpl_if name="is_reseller">
-      <div class="ctrlHolder">
-      	<label for="client_group_id">{tmpl_var name='client_txt'}</label>
-        <select name="client_group_id" id="client_group_id" class="selectInput">
-					{tmpl_var name='client_group_id'}
+	  <div class="ctrlHolder">
+		<label for="parent_domain_id">{tmpl_var name='parent_domain_id_txt'}</label>
+        <select name="parent_domain_id" id="parent_domain_id" class="selectInput">
+					{tmpl_var name='parent_domain_id'}
 		</select>
-      </div>
-      </tmpl_if>
+	  </div>
       <div class="ctrlHolder">
       	<label for="type">{tmpl_var name='type_txt'}</label>
         <select name="type" id="type" class="selectInput formLengthHalf">
diff --git a/interface/web/sites/templates/web_backup_list.htm b/interface/web/sites/templates/web_backup_list.htm
new file mode 100644
index 0000000..7d91c23
--- /dev/null
+++ b/interface/web/sites/templates/web_backup_list.htm
@@ -0,0 +1,40 @@
+<tmpl_if name="msg">
+	<div id="OKMsg"><p><tmpl_var name="msg"></p></div>
+</tmpl_if>
+<tmpl_if name="error">
+	<div id="errorMsg"><h3>ERROR</h3><ol><tmpl_var name="error"></ol></div>
+</tmpl_if>
+<h3><tmpl_var name="list_head_txt"></h3>
+  <div class="pnl_listarea">
+    <fieldset><legend><tmpl_var name="list_head_txt"></legend>
+      <table class="list">
+        <thead>
+          <tr>
+            <th class="tbl_col_date" scope="col"><tmpl_var name="date_txt"></th>
+			<th class="tbl_col_date" scope="col"><tmpl_var name="backup_type_txt"></th>
+			<th class="tbl_col_filename" scope="col"><tmpl_var name="filename_txt"></th>
+            <th class="tbl_col_buttons" scope="col">&nbsp;</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tmpl_loop name="records">
+          <tr class="tbl_row_<tmpl_if name='__EVEN__'}even<tmpl_else>uneven</tmpl_if>">
+            <td class="tbl_col_date">{tmpl_var name="date"}</td>
+			<td class="tbl_col_date">{tmpl_var name="backup_type"}</td>
+			<td class="tbl_col_filename">{tmpl_var name="filename"}</td>
+            <td class="tbl_col_buttons" style="width:300px;">
+              <div class="buttons" >
+                <button class="iconstxt icoRestore" type="button" onClick="confirm_action('sites/web_domain_edit.php?id={tmpl_var name='parent_id'}&next_tab=backup&backup_action=restore&backup_id={tmpl_var name='backup_id'}','{tmpl_var name='restore_confirm_txt'}');">
+					<span>{tmpl_var name="restore_backup_txt"}</span>
+				</button>
+				<button class="iconstxt icoDownload" type="button" onClick="loadContent('sites/web_domain_edit.php?id={tmpl_var name='parent_id'}&next_tab=backup&backup_action=download&backup_id={tmpl_var name='backup_id'}');">
+					<span>{tmpl_var name="download_backup_txt"}</span>
+				</button>
+              </div>
+            </td>
+          </tr>
+          </tmpl_loop>
+        </tbody>
+      </table>
+    </fieldset>
+  </div>
diff --git a/interface/web/sites/templates/web_domain_backup.htm b/interface/web/sites/templates/web_domain_backup.htm
index 6b8c627..14e3a8f 100644
--- a/interface/web/sites/templates/web_domain_backup.htm
+++ b/interface/web/sites/templates/web_domain_backup.htm
@@ -18,6 +18,8 @@
 				</select>
       </div>
     </fieldset>
+	
+	{tmpl_var name='backup_records'}
 
     <input type="hidden" name="id" value="{tmpl_var name='id'}">
 
diff --git a/interface/web/sites/tools.inc.php b/interface/web/sites/tools.inc.php
index 38e8804..316abe6 100644
--- a/interface/web/sites/tools.inc.php
+++ b/interface/web/sites/tools.inc.php
@@ -96,7 +96,7 @@
       	}
     }
     /* get the name of the client */
-    $tmp = $app->db->queryOneRecord("SELECT client_id FROM sys_group WHERE groupid = " . $client_group_id);
+    $tmp = $app->db->queryOneRecord("SELECT client_id FROM sys_group WHERE groupid = " . intval($client_group_id));
     $clientID = $tmp['client_id'];
     if ($clientID == '') $clientID = '0';
     return $clientID;
diff --git a/interface/web/sites/web_domain_del.php b/interface/web/sites/web_domain_del.php
index a69923f..f84f4f1 100644
--- a/interface/web/sites/web_domain_del.php
+++ b/interface/web/sites/web_domain_del.php
@@ -91,6 +91,12 @@
             $app->db->datalogDelete('webdav_user','webdav_user_id',$rec['webdav_user_id']);
         }
 		
+		//* Delete all records that belog to this web
+        $records = $app->db->queryAllRecords("SELECT backup_id FROM web_backup WHERE parent_domain_id = '".intval($this->id)."'");
+        foreach($records as $rec) {
+            $app->db->datalogDelete('web_backup','backup_id',$rec['backup_id']);
+        }
+		
 		//* Delete all web folders
         $records = $app->db->queryAllRecords("SELECT web_folder_id FROM web_folder WHERE parent_domain_id = '".intval($this->id)."'");
         foreach($records as $rec) {
diff --git a/interface/web/sites/web_domain_edit.php b/interface/web/sites/web_domain_edit.php
index b6236bd..91b965c 100644
--- a/interface/web/sites/web_domain_edit.php
+++ b/interface/web/sites/web_domain_edit.php
@@ -607,6 +607,14 @@
 			}
 			unset($records);
 			unset($rec);
+			
+			//* Update all databases
+			$records = $app->db->queryAllRecords("SELECT database_id FROM web_database WHERE parent_domain_id = ".$this->id);
+			foreach($records as $rec) {
+				$app->db->datalogUpdate('web_database', "sys_userid = '".$web_rec['sys_userid']."', sys_groupid = '".$web_rec['sys_groupid']."'", 'database_id', $rec['database_id']);
+			}
+			unset($records);
+			unset($rec);
 
 		}
 
@@ -638,6 +646,21 @@
 			$sql = "UPDATE web_domain SET php_open_basedir = '$php_open_basedir' WHERE domain_id = ".$this->id;
 			$app->db->query($sql);
 		}
+		
+		//* Change database backup options when web backup options have been changed
+		if(isset($this->dataRecord['backup_interval']) && ($this->dataRecord['backup_interval'] != $this->oldDataRecord['backup_interval'] || $this->dataRecord['backup_copies'] != $this->oldDataRecord['backup_copies'])) {
+			//* Update all databases
+			$backup_interval = $this->dataRecord['backup_interval'];
+			$backup_copies = $this->dataRecord['backup_copies'];
+			$records = $app->db->queryAllRecords("SELECT database_id FROM web_database WHERE parent_domain_id = ".$this->id);
+			foreach($records as $rec) {
+				$app->db->datalogUpdate('web_database', "backup_interval = '$backup_interval', backup_copies = '$backup_copies'", 'database_id', $rec['database_id']);
+			}
+			unset($records);
+			unset($rec);
+			unset($backup_copies);
+			unset($backup_interval);
+		}
 
 	}
 
diff --git a/interface/web/themes/default/css/screen/content_ispc.css b/interface/web/themes/default/css/screen/content_ispc.css
index 9f53502..9e65bd4 100644
--- a/interface/web/themes/default/css/screen/content_ispc.css
+++ b/interface/web/themes/default/css/screen/content_ispc.css
@@ -366,6 +366,8 @@
 	.iconstxt.icoAdd { background-image: url(../../icons/x16/plus_circle_frame.png); }
 	.iconstxt.icoKey { background-image: url("../../icons/x16/key.png"); }
 	.iconstxt.icoDelete { background-image: url("../../icons/x16/minus_circle_frame.png"); }
+	.iconstxt.icoDownload { background-image: url("../../icons/x16/arrow_270.png"); }
+	.iconstxt.icoRestore { background-image: url("../../icons/x16/arrow_circle_225.png"); }
 	
 	/* Button with icon and without text */
 	.icons16 span { display: none; }
diff --git a/server/cron_daily.php b/server/cron_daily.php
index 67d9945..87ba2ee 100644
--- a/server/cron_daily.php
+++ b/server/cron_daily.php
@@ -1,7 +1,7 @@
 <?php
 
 /*
-Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+Copyright (c) 2007-2012, Till Brehm, projektfarm Gmbh
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
@@ -480,6 +480,11 @@
 
 $server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
 $backup_dir = $server_config['backup_dir'];
+$backup_mode = $server_config['backup_mode'];
+if($backup_mode == '') $backup_mode = 'userzip';
+
+$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
+$http_server_user = $web_config['user'];
 
 if($backup_dir != '') {
 	
@@ -495,12 +500,12 @@
 		chmod(escapeshellcmd($backup_dir), $backup_dir_permissions);
 	}
 	
-	$sql = "SELECT * FROM web_domain WHERE type = 'vhost'";
+	$sql = "SELECT * FROM web_domain WHERE server_id = '".$conf['server_id']."' AND type = 'vhost' AND backup_interval != 'none'";
 	$records = $app->db->queryAllRecords($sql);
 	if(is_array($records)) {
 		foreach($records as $rec) {
 			
-			// Create a backup
+			//* Do the website backup
 			if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
 				
 				$web_path = $rec['document_root'];
@@ -510,46 +515,71 @@
 				$web_backup_dir = $backup_dir.'/web'.$web_id;
 				if(!is_dir($web_backup_dir)) mkdir($web_backup_dir, 0750);
 				chmod($web_backup_dir, 0750); 
-				if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') {
-					chown($web_backup_dir, $rec['system_user']); 
-					chgrp($web_backup_dir, $rec['system_group']);
-				} else {
+				//if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') {
+				chown($web_backup_dir, $rec['system_user']); 
+				chgrp($web_backup_dir, $rec['system_group']);
+				/*} else {
 					chown($web_backup_dir, 'root');
 					chgrp($web_backup_dir, 'root');
+				}*/
+				if($backup_mode == 'userzip') {
+					//* Create a .zip backup as web user and include also files owned by apache / nginx user
+					$web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.zip';
+					exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -group '.escapeshellarg($web_group).' -print 2> /dev/null | zip --exclude=backup\* --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@');
+					exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -user '.escapeshellarg($http_server_user).' -print 2> /dev/null | zip --exclude=backup\* --update --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@');
+				} else {
+					//* Create a tar.gz backup as root user
+					$web_backup_file = 'web'.$web_id.'_'.date('Y-m-d_H-i').'.tar.gz';
+					exec('tar pczf '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' --exclude=backup\* --directory '.escapeshellarg($web_path).' .');
 				}
-				exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -group '.escapeshellarg($web_group).' -print | zip -y '.escapeshellarg($web_backup_dir.'/web.zip').' -@');
-				chown($web_backup_dir.'/web.zip', $rec['system_user']); 
-				chgrp($web_backup_dir.'/web.zip', $rec['system_group']);
-				chmod($web_backup_dir.'/web.zip', 0750);
+				chown($web_backup_dir.'/'.$web_backup_file, 'root'); 
+				chgrp($web_backup_dir.'/'.$web_backup_file, 'root');
+				chmod($web_backup_dir.'/'.$web_backup_file, 0750);
 				
-				// Rename or remove old backups
+				//* Insert web backup record in database
+				$insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",".$web_id.",'web','".$backup_mode."',".time().",'".$app->db->quote($web_backup_file)."')";
+				$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
+				
+				//* Remove old backups
 				$backup_copies = intval($rec['backup_copies']);
 				
-				//* delete any older backup copies that previously existed
-				for ($n = $backup_copies; $n <= 10; $n++) {
-					if(is_file($web_backup_dir.'/web.'.$n.'.zip')) unlink($web_backup_dir.'/web.'.$n.'.zip');
-				}
-				
-				for($n = $backup_copies - 1; $n >= 1; $n--) {
-					if(is_file($web_backup_dir.'/web.'.$n.'.zip')) {
-						rename($web_backup_dir.'/web.'.$n.'.zip',$web_backup_dir.'/web.'.($n+1).'.zip');
+				$dir_handle = dir($web_backup_dir);
+				$files = array();
+				while (false !== ($entry = $dir_handle->read())) {
+					if($entry != '.' && $entry != '..' && substr($entry,0,3) == 'web' && is_file($web_backup_dir.'/'.$entry)) {
+						$files[] = $entry;
 					}
 				}
-			
-				if(is_file($web_backup_dir.'/web.zip')) rename($web_backup_dir.'/web.zip',$web_backup_dir.'/web.1.zip');
-			
-				// Create backupdir symlink
-				if(is_link($web_path.'/backup')) unlink($web_path.'/backup');
-				symlink($web_backup_dir,$web_path.'/backup');
-				// chmod($web_path.'/backup', 0755);
-				chown($web_path.'/backup', $rec['system_user']); 
-				chgrp($web_path.'/backup', $rec['system_group']);
+				$dir_handle->close();
+				
+				rsort($files);
+				
+				for ($n = $backup_copies; $n <= 10; $n++) {
+					if(isset($files[$n]) && is_file($web_backup_dir.'/'.$files[$n])) {
+						unlink($web_backup_dir.'/'.$files[$n]);
+						$sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
+						$tmp = $app->dbmaster->queryOneRecord($sql);
+						$app->dbmaster->datalogDelete('web_backup', 'backup_id', $tmp['backup_id']);
+					}
+				}
+				
+				unset($files);
+				unset($dir_handle);
+				
+				//* Remove backupdir symlink and create as directory instead
+				if(is_link($web_path.'/backup')) {
+					unlink($web_path.'/backup');
+				}
+				if(!is_dir($web_path.'/backup')) {
+					mkdir($web_path.'/backup');
+					chown($web_path.'/backup', $rec['system_user']); 
+					chgrp($web_path.'/backup', $rec['system_group']);
+				}
 				
 			}
 			
 			/* If backup_interval is set to none and we have a 
 			backup directory for the website, then remove the backups */
-			
 			if($rec['backup_interval'] == 'none') {
 				$web_id = $rec['domain_id'];
 				$web_user = $rec['system_user'];
@@ -560,6 +590,78 @@
 			}
 		}
 	}
+	
+	$sql = "SELECT * FROM web_database WHERE server_id = '".$conf['server_id']."' AND backup_interval != 'none'";
+	$records = $app->db->queryAllRecords($sql);
+	if(is_array($records)) {
+		
+		include('lib/mysql_clientdb.conf');
+		
+		foreach($records as $rec) {
+			
+			//* Do the database backup
+			if($rec['backup_interval'] == 'daily' or ($rec['backup_interval'] == 'weekly' && date('w') == 0) or ($rec['backup_interval'] == 'monthly' && date('d') == '01')) {
+				
+				$web_id = $rec['parent_domain_id'];
+				$db_backup_dir = $backup_dir.'/web'.$web_id;
+				if(!is_dir($web_backup_dir)) mkdir($web_backup_dir, 0750);
+				chmod($web_backup_dir, 0750); 
+				chown($web_backup_dir, 'root');
+				chgrp($web_backup_dir, 'root');
+
+				//* Do the mysql database backup with mysqldump
+				$db_id = $rec['database_id'];
+				$db_name = $rec['database_name'];
+				$db_backup_file = 'db_'.$db_name.'_'.date('Y-m-d_H-i').'.sql';
+				$command = "mysqldump -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' -c --add-drop-table --create-options --quick --result-file='".$db_backup_dir.'/'.$db_backup_file."' '".$db_name."'";
+				exec($command);
+				
+				//* Compress the backup with gzip
+				exec("gzip -c '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file)."' > '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file).".gz'");
+				chmod($db_backup_dir.'/'.$db_backup_file.'.gz', 0750);
+				chown($db_backup_dir.'/'.$db_backup_file.'.gz', fileowner($db_backup_dir)); 
+				chgrp($db_backup_dir.'/'.$db_backup_file.'.gz', filegroup($db_backup_dir));
+				
+				//* Insert web backup record in database
+				$insert_data = "(server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",$web_id,'mysql','sqlgz',".time().",'".$app->db->quote($db_backup_file).".gz')";
+				$app->dbmaster->datalogInsert('web_backup', $insert_data, 'backup_id');
+				
+				//* Remove the uncompressed file
+				unlink($db_backup_dir.'/'.$db_backup_file);
+				
+				//* Remove old backups
+				$backup_copies = intval($rec['backup_copies']);
+				
+				$dir_handle = dir($db_backup_dir);
+				$files = array();
+				while (false !== ($entry = $dir_handle->read())) {
+					if($entry != '.' && $entry != '..' && substr($entry,0,2) == 'db' && is_file($db_backup_dir.'/'.$entry)) {
+						$files[] = $entry;
+					}
+				}
+				$dir_handle->close();
+				
+				rsort($files);
+				
+				for ($n = $backup_copies; $n <= 10; $n++) {
+					if(isset($files[$n]) && is_file($db_backup_dir.'/'.$files[$n])) {
+						unlink($db_backup_dir.'/'.$files[$n]);
+						$sql = "SELECT backup_id FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
+						$tmp = $app->dbmaster->queryOneRecord($sql);
+						$app->dbmaster->datalogDelete('web_backup', 'backup_id', $tmp['backup_id']);
+					}
+				}
+				
+				unset($files);
+				unset($dir_handle);
+			}
+		}
+		
+		unset($clientdb_host);
+		unset($clientdb_user);
+		unset($clientdb_password);
+		
+	}
 }
 
 
diff --git a/server/lib/classes/db_mysql.inc.php b/server/lib/classes/db_mysql.inc.php
index 18f09bf..54561c0 100644
--- a/server/lib/classes/db_mysql.inc.php
+++ b/server/lib/classes/db_mysql.inc.php
@@ -1,4 +1,3 @@
-
 <?php
 /*
    Copyright (c) 2005, Till Brehm, projektfarm Gmbh
@@ -226,10 +225,10 @@
     if(is_array($record_old) && count($record_old) > 0) {
       foreach($record_old as $key => $val) {
 	// if(!isset($record_new[$key]) || $record_new[$key] != $val) {
-	if($record_new[$key] != $val) {
+	if(@$record_new[$key] != $val) {
 	  // Record has changed
 	  $diffrec_full['old'][$key] = $val;
-	  $diffrec_full['new'][$key] = $record_new[$key];
+	  $diffrec_full['new'][$key] = @$record_new[$key];
 	  $diff_num++;
 	} else {
 	  $diffrec_full['old'][$key] = $val;
@@ -593,4 +592,4 @@
 
 	      }
 
-	      ?>
+	      ?>
\ No newline at end of file
diff --git a/server/lib/classes/modules.inc.php b/server/lib/classes/modules.inc.php
index 2c5b349..ef600bd 100644
--- a/server/lib/classes/modules.inc.php
+++ b/server/lib/classes/modules.inc.php
@@ -229,8 +229,46 @@
 				$app->log('Processed datalog_id '.$d['datalog_id'],LOGLEVEL_DEBUG);
 			}
 		}
+	}
+	
+	function processActions() {
+		global $app,$conf;
 		
+		//* get the server_id of the local server
+		$server_id = intval($conf["server_id"]);
 		
+		include_once (SCRIPT_PATH."/lib/remote_action.inc.php");
+		
+		//* SQL query to get all pending actions
+		$sql = "SELECT action_id, action_type, action_param " .
+				"FROM sys_remoteaction " .
+				"WHERE server_id = " . $server_id . " ".
+				" AND  action_id > " . intval($maxid_remote_action) . " ".
+				"ORDER BY action_id";
+		
+		$actions = $app->dbmaster->queryAllRecords($sql);
+		
+		if(is_array($actions)) {
+			foreach($actions as $action) {
+				
+				//* Raise the action
+				$state = $app->plugins->raiseAction($action['action_type'],$action['action_param']);
+				
+				//* Update the action state
+				$sql = "UPDATE sys_remoteaction " .
+						"SET action_state = '" . $app->dbmaster->quote($state) . "' " .
+						"WHERE action_id = " . intval($action['action_id']);
+				$app->dbmaster->query($sql);
+
+				/*
+				* Then save the maxid for the next time...
+				*/
+				$fp = fopen(ISPC_LIB_PATH."/remote_action.inc.php", 'wb');
+				$content = '<?php' . "\n" . '$maxid_remote_action = ' . $action['action_id'] . ';' . "\n?>";
+				fwrite($fp, $content);
+				fclose($fp);
+			}
+		}
 		
 		
 		
diff --git a/server/lib/classes/plugins.inc.php b/server/lib/classes/plugins.inc.php
index ba12981..afef438 100644
--- a/server/lib/classes/plugins.inc.php
+++ b/server/lib/classes/plugins.inc.php
@@ -1,7 +1,7 @@
 <?php
 
 /*
-Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+Copyright (c) 2007-2012, Till Brehm, projektfarm Gmbh
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
@@ -32,6 +32,7 @@
 	
 	var $available_events = array();
 	var $subscribed_events = array();
+	var $subscribed_actions = array();
 	var $debug = false;
 	
 	/*
@@ -126,6 +127,45 @@
 		unset($events);
 	}
 	
+	/*
+	 This function is called by the plugin to register for an action
+	*/
+	
+	function registerAction($action_name,$plugin_name,$function_name) {
+		global $app;
+		$this->subscribed_actions[$action_name][] = array('plugin' => $plugin_name, 'function' => $function_name);
+		if($this->debug)  $app->log("Registered function '$function_name' from plugin '$plugin_name' for action '$event_name'.",LOGLEVEL_DEBUG);
+	}
+	
+	
+	function raiseAction($action_name,$data) {
+		global $app;
+		
+		//* Get the subscriptions for this action
+		$actions = (isset($this->subscribed_actions[$action_name]))?$this->subscribed_actions[$action_name]:'';
+		if($this->debug) $app->log('Raised action: '.$action_name,LOGLEVEL_DEBUG);
+		
+		if(is_array($actions)) {
+			foreach($actions as $action) {
+				$plugin_name = $action['plugin'];
+				$function_name = $action['function'];
+				$state_out = 'ok';
+				//* Call the processing function of the plugin
+				$app->log("Calling function '$function_name' from plugin '$plugin_name' raised by action '$action_name'.",LOGLEVEL_DEBUG);
+				$state = call_user_func(array($app->loaded_plugins[$plugin_name],$function_name),$action_name,$data);
+				//* ensure that we return the highest warning / error level if a error occured in one of the functions
+				if($state == 'warning' && $state_out != 'error') $state_out = 'warning';
+				if($state == 'error') $state_out = 'error';
+				unset($plugin_name);
+				unset($function_name);
+			}
+		}
+		unset($action);
+		unset($actions);
+		
+		return $state_out;
+	}
+	
 }
 
 ?>
diff --git a/server/mods-available/remoteaction_core_module.inc.php b/server/mods-available/remoteaction_core_module.inc.php
index 49294f4..5ee3fc6 100644
--- a/server/mods-available/remoteaction_core_module.inc.php
+++ b/server/mods-available/remoteaction_core_module.inc.php
@@ -1,6 +1,6 @@
 <?php
 /*
-Copyright (c) 2007-2010, Till Brehm, projektfarm Gmbh and Oliver Vogel www.muv.com
+Copyright (c) 2007-2010, Till Brehm, projektfarm Gmbh, Oliver Vogel www.muv.com
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
@@ -35,7 +35,7 @@
 	//* This function is called during ispconfig installation to determine
 	//  if a symlink shall be created for this plugin.
 	function onInstall() {
-		return true;
+		return false;
 	}
 
 	/*
@@ -45,7 +45,8 @@
 		/*
        	 * Check for actions to execute
 		*/
-		$this->_execActions();
+		//* This module has been replaced by the new action framework.
+		// $this->_execActions();
 	}
 
 	/*
diff --git a/server/mods-available/web_module.inc.php b/server/mods-available/web_module.inc.php
index 653940f..086b601 100644
--- a/server/mods-available/web_module.inc.php
+++ b/server/mods-available/web_module.inc.php
@@ -49,7 +49,10 @@
 									'web_folder_delete',
 									'web_folder_user_insert',
 									'web_folder_user_update',
-									'web_folder_user_delete');
+									'web_folder_user_delete',
+									'web_backup_insert',
+									'web_backup_update',
+									'web_backup_delete');
 	
 	//* This function is called during ispconfig installation to determine
 	//  if a symlink shall be created for this plugin.
@@ -94,6 +97,7 @@
 		$app->modules->registerTableHook('webdav_user','web_module','process');
 		$app->modules->registerTableHook('web_folder','web_module','process');
 		$app->modules->registerTableHook('web_folder_user','web_module','process');
+		$app->modules->registerTableHook('web_backup','web_module','process');
 		
 		// Register service
 		$app->services->registerService('httpd','web_module','restartHttpd');
@@ -139,6 +143,11 @@
 				if($action == 'u') $app->plugins->raiseEvent('web_folder_user_update',$data);
 				if($action == 'd') $app->plugins->raiseEvent('web_folder_user_delete',$data);
 			break;
+			case 'web_backup':
+				if($action == 'i') $app->plugins->raiseEvent('web_backup_insert',$data);
+				if($action == 'u') $app->plugins->raiseEvent('web_backup_update',$data);
+				if($action == 'd') $app->plugins->raiseEvent('web_backup_delete',$data);
+			break;
 		} // end switch
 	} // end function
 	
diff --git a/server/plugins-available/backup_plugin.inc.php b/server/plugins-available/backup_plugin.inc.php
new file mode 100644
index 0000000..dfe3ddd
--- /dev/null
+++ b/server/plugins-available/backup_plugin.inc.php
@@ -0,0 +1,134 @@
+<?php
+
+/*
+Copyright (c) 2012, Till Brehm, ISPConfig UG
+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 backup_plugin {
+	
+	var $plugin_name = 'backup_plugin';
+	var $class_name  = 'backup_plugin';
+	
+	//* This function is called during ispconfig installation to determine
+	//  if a symlink shall be created for this plugin.
+	public function onInstall() {
+		global $conf;
+		
+		return true;
+		
+	}
+	
+		
+	/*
+	 	This function is called when the plugin is loaded
+	*/
+	
+	public function onLoad() {
+		global $app;
+		
+		//* Register for actions
+		$app->plugins->registerAction('backup_download',$this->plugin_name,'backup_action');
+		$app->plugins->registerAction('backup_restore',$this->plugin_name,'backup_action');
+		
+	}
+	
+	//* Do a backup action
+	public function backup_action($action_name,$data) {
+		global $app,$conf;
+		
+		$backup_id = intval($data);
+		$backup = $app->db->queryOneRecord("SELECT * FROM web_backup WHERE backup_id = $backup_id");
+		
+		if(is_array($backup)) {
+		
+			$app->uses('ini_parser,file,getconf');
+			
+			$web = $app->db->queryOneRecord("SELECT * FROM web_domain WHERE domain_id = ".$backup['parent_domain_id']);
+			$server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
+			$backup_dir = $server_config['backup_dir'].'/web'.$web['domain_id'];
+			
+			//* Make backup available for download
+			if($action_name == 'backup_download') {
+				//* Copy the backup file to the backup folder of the website
+				if(file_exists($backup_dir.'/'.$backup['filename']) && !stristr($backup_dir.'/'.$backup['filename'],'..') && !stristr($backup_dir.'/'.$backup['filename'],'etc')) {
+					copy($backup_dir.'/'.$backup['filename'],$web['document_root'].'/backup/'.$backup['filename']);
+					chgrp($web['document_root'].'/backup/'.$backup['filename'],$web['system_group']);
+					$app->log('cp '.$backup_dir.'/'.$backup['filename'].' '.$web['document_root'].'/backup/'.$backup['filename'],LOGLEVEL_DEBUG);
+				}
+			}
+			
+			//* Restore a mysql backup
+			if($action_name == 'backup_restore' && $backup['backup_type'] == 'mysql') {
+				//* Load sql dump into db
+				include('lib/mysql_clientdb.conf');
+				
+				if(file_exists($backup_dir.'/'.$backup['filename'])) {
+					$parts = explode('_',$backup['filename']);
+					$db_name = $parts[1];
+					$command = "gunzip --stdout ".escapeshellarg($backup_dir.'/'.$backup['filename'])." | mysql -h '".escapeshellcmd($clientdb_host)."' -u '".escapeshellcmd($clientdb_user)."' -p'".escapeshellcmd($clientdb_password)."' '".$db_name."'";
+					exec($command);
+				}
+				unset($clientdb_host);
+				unset($clientdb_user);
+				unset($clientdb_password);
+				$app->log('Restored MySQL backup '.$backup_dir.'/'.$backup['filename'],LOGLEVEL_DEBUG);
+			}
+			
+			//* Restore a web backup
+			if($action_name == 'backup_restore' && $backup['backup_type'] == 'web') {
+				if($backup['backup_mode'] == 'userzip') {
+					if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'],'..') && !stristr($backup_dir.'/'.$backup['filename'],'etc')) {
+						if(file_exists($web['document_root'].'/backup/'.$backup['filename'])) rename($web['document_root'].'/backup/'.$backup['filename'],$web['document_root'].'/backup/'.$backup['filename'].'.bak');
+						copy($backup_dir.'/'.$backup['filename'],$web['document_root'].'/backup/'.$backup['filename']);
+						chgrp($web['document_root'].'/backup/'.$backup['filename'],$web['system_group']);
+						//chown($web['document_root'].'/backup/'.$backup['filename'],$web['system_user']);
+						$command = 'sudo -u '.escapeshellarg($web['system_user']).' unzip -qq -o  '.escapeshellarg($web['document_root'].'/backup/'.$backup['filename']).' -d '.escapeshellarg($web['document_root']).' 2> /dev/null';
+						exec($command);
+						unlink($web['document_root'].'/backup/'.$backup['filename']);
+						if(file_exists($web['document_root'].'/backup/'.$backup['filename'].'.bak')) rename($web['document_root'].'/backup/'.$backup['filename'].'.bak',$web['document_root'].'/backup/'.$backup['filename']);
+						$app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'],LOGLEVEL_DEBUG);
+					}
+				}
+				if($backup['backup_mode'] == 'rootgz') {
+					if(file_exists($backup_dir.'/'.$backup['filename']) && $web['document_root'] != '' && $web['document_root'] != '/' && !stristr($backup_dir.'/'.$backup['filename'],'..') && !stristr($backup_dir.'/'.$backup['filename'],'etc')) {
+						$command = 'tar xzf '.escapeshellarg($backup_dir.'/'.$backup['filename']).' --directory '.escapeshellarg($web['document_root']);
+						exec($command);
+						$app->log('Restored Web backup '.$backup_dir.'/'.$backup['filename'],LOGLEVEL_DEBUG);
+					}
+				}
+			}
+			
+		} else {
+			$app->log('No backup with ID '.$backup_id.' found.',LOGLEVEL_DEBUG);
+		}
+		
+		return 'ok';
+	}
+
+} // end class
+
+?>
diff --git a/server/plugins-available/mysql_clientdb_plugin.inc.php b/server/plugins-available/mysql_clientdb_plugin.inc.php
index 624ef2b..59a6cf4 100644
--- a/server/plugins-available/mysql_clientdb_plugin.inc.php
+++ b/server/plugins-available/mysql_clientdb_plugin.inc.php
@@ -100,7 +100,7 @@
           if($valid == false) continue;
           
           if($action == 'GRANT') {
-              if(!$link->query("GRANT ALL ON ".$link->escape_string($database_name,$link).".* TO '".$link->escape_string($database_user,$link)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($database_password,$link)."';",$link)) $success = false;
+              if(!$link->query("GRANT ALL ON ".$link->escape_string($database_name).".* TO '".$link->escape_string($database_user)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($database_password)."';")) $success = false;
           } elseif($action == 'REVOKE') {
               //mysql_query("REVOKE ALL PRIVILEGES ON ".mysql_real_escape_string($database_name,$link).".* FROM '".mysql_real_escape_string($database_user,$link)."';",$link);
           } elseif($action == 'DROP') {
@@ -108,7 +108,7 @@
           } elseif($action == 'RENAME') {
               if(!$link->query("RENAME USER '".$link->escape_string($database_user)."'@'$db_host' TO '".$link->escape_string($database_rename_user)."'@'$db_host'")) $success = false;
           } elseif($action == 'PASSWORD') {
-              if(!$link->query("SET PASSWORD FOR '".$link->escape_string($database_user,$link)."'@'$db_host' = '".$link->escape_string($database_password,$link)."';",$link)) $success = false;
+              if(!$link->query("SET PASSWORD FOR '".$link->escape_string($database_user)."'@'$db_host' = '".$link->escape_string($database_password)."';")) $success = false;
           }
       }
       
@@ -158,7 +158,7 @@
 				}
 				
 				$db_host = 'localhost';
-				$link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name'],$link))."`.* TO '".$link->escape_string($data['new']['database_user'],$link)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'],$link)."';",$link);
+				$link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name']))."`.* TO '".$link->escape_string($data['new']['database_user'])."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'])."';");
 
 				
 			}
@@ -197,7 +197,7 @@
 				}
 				
 				$db_host = 'localhost';
-				$link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name'],$link))."`.* TO '".$link->escape_string($data['new']['database_user'],$link)."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'],$link)."';",$link);
+				$link->query("GRANT ALL ON `".str_replace(array('_','%'),array('\\_','\\%'),$link->escape_string($data['new']['database_name']))."`.* TO '".$link->escape_string($data['new']['database_user'])."'@'$db_host' IDENTIFIED BY PASSWORD '".$link->escape_string($data['new']['database_password'])."';");
 				
 				// mysql_query("GRANT ALL ON ".mysql_real_escape_string($data["new"]["database_name"],$link).".* TO '".mysql_real_escape_string($data["new"]["database_user"],$link)."'@'$db_host' IDENTIFIED BY '".mysql_real_escape_string($data["new"]["database_password"],$link)."';",$link);
 				//echo "GRANT ALL ON ".mysql_real_escape_string($data["new"]["database_name"]).".* TO '".mysql_real_escape_string($data["new"]["database_user"])."'@'$db_host' IDENTIFIED BY '".mysql_real_escape_string($data["new"]["database_password"])."';";
@@ -247,10 +247,10 @@
 			//* Change password
 			if($data['new']['database_password'] != $data['old']['database_password']) {
 				$db_host = 'localhost';
-				$link->query("SET PASSWORD FOR '".$link->escape_string($data['new']['database_user'],$link)."'@'$db_host' = '".$link->escape_string($data['new']['database_password'],$link)."';",$link);
+				$link->query("SET PASSWORD FOR '".$link->escape_string($data['new']['database_user'])."'@'$db_host' = '".$link->escape_string($data['new']['database_password'])."';");
 
 				if($data['new']['remote_access'] == 'y') {
-					$this->process_host_list('PASSWORD', '', $data['new']['database_user'], $data['new']['database_password'], $data['new']['remote_ips']);
+					$this->process_host_list('PASSWORD', '', $data['new']['database_user'], $data['new']['database_password'], $data['new']['remote_ips'],$link);
 				}
 				$app->log('Changing MySQL user password for: '.$data['new']['database_user'],LOGLEVEL_DEBUG);
 			}
diff --git a/server/plugins-available/openvz_plugin.inc.php b/server/plugins-available/openvz_plugin.inc.php
index ac9e0aa..9e0d75d 100644
--- a/server/plugins-available/openvz_plugin.inc.php
+++ b/server/plugins-available/openvz_plugin.inc.php
@@ -1,7 +1,7 @@
 <?php
 
 /*
-Copyright (c) 2011, Till Brehm, projektfarm Gmbh
+Copyright (c) 2011-2012, Till Brehm, projektfarm Gmbh
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
@@ -63,6 +63,14 @@
 		$app->plugins->registerEvent('openvz_vm_insert',$this->plugin_name,'vm_insert');
 		$app->plugins->registerEvent('openvz_vm_update',$this->plugin_name,'vm_update');
 		$app->plugins->registerEvent('openvz_vm_delete',$this->plugin_name,'vm_delete');
+		
+		//* Register for actions
+		$app->plugins->registerAction('openvz_start_vm',$this->plugin_name,'actions');
+		$app->plugins->registerAction('openvz_stop_vm',$this->plugin_name,'actions');
+		$app->plugins->registerAction('openvz_restart_vm',$this->plugin_name,'actions');
+		$app->plugins->registerAction('openvz_create_ostpl',$this->plugin_name,'actions');
+		
+		
 		
 	}
 	
@@ -149,6 +157,50 @@
 			
 	}
 	
+	function actions($action_name,$data) {
+		global $app, $conf;
+		
+		if ($action_name == 'openvz_start_vm') {
+			$veid = intval($data);
+			if($veid > 0) {
+				exec("vzctl start $veid");
+				$app->log("Start VM: vzctl start $veid",LOGLEVEL_DEBUG);
+			}
+			return 'ok';
+		}
+		if ($action_name == 'openvz_stop_vm') {
+			$veid = intval($data);
+			if($veid > 0) {
+				exec("vzctl stop $veid");
+				$app->log("Stop VM: vzctl stop $veid",LOGLEVEL_DEBUG);
+			}
+			return 'ok';
+		}
+		if ($action_name == 'openvz_restart_vm') {
+			$veid = intval($data);
+			if($veid > 0) {
+				exec("vzctl restart $veid");
+				$app->log("Restart VM: vzctl restart $veid",LOGLEVEL_DEBUG);
+			}
+			return 'ok';
+		}
+		if ($action_name == 'openvz_create_ostpl') {
+			$parts = explode(':',$data);
+			$veid = intval($parts[0]);
+			$template_cache_dir = '/vz/template/cache/';
+			$template_name = escapeshellcmd($parts[1]);
+			if($veid > 0 && $template_name != '' && is_dir($template_cache_dir)) {
+				$command = "vzdump --suspend --compress --stdexcludes --dumpdir $template_cache_dir $veid";
+				exec($command);
+				exec("mv ".$template_cache_dir."vzdump-openvz-".$veid."*.tgz ".$template_cache_dir.$template_name.".tar.gz");
+				exec("rm -f ".$template_cache_dir."vzdump-openvz-".$veid."*.log");
+			}
+			$app->log("Created OpenVZ OStemplate $template_name from VM $veid",LOGLEVEL_DEBUG);
+			return 'ok';
+		}
+			
+	}
+	
 
 } // end class
 
diff --git a/server/plugins-available/software_update_plugin.inc.php b/server/plugins-available/software_update_plugin.inc.php
index 55feaee..a6b7162 100644
--- a/server/plugins-available/software_update_plugin.inc.php
+++ b/server/plugins-available/software_update_plugin.inc.php
@@ -1,7 +1,7 @@
 <?php
 
 /*
-Copyright (c) 2007, Till Brehm, projektfarm Gmbh
+Copyright (c) 2007-2012, Till Brehm, projektfarm Gmbh, Oliver Vogel www.muv.com
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
@@ -35,7 +35,7 @@
 	
 	//* This function is called during ispconfig installation to determine
 	//  if a symlink shall be created for this plugin.
-	function onInstall() {
+	public function onInstall() {
 		global $conf;
 		
 		return true;
@@ -47,29 +47,31 @@
 	 	This function is called when the plugin is loaded
 	*/
 	
-	function onLoad() {
+	public function onLoad() {
 		global $app;
 		
 		/*
 		Register for the events
 		*/
 		
-		//* Mailboxes
 		$app->plugins->registerEvent('software_update_inst_insert',$this->plugin_name,'process');
 		//$app->plugins->registerEvent('software_update_inst_update',$this->plugin_name,'process');
 		//$app->plugins->registerEvent('software_update_inst_delete',$this->plugin_name,'process');
 		
+		//* Register for actions
+		$app->plugins->registerAction('os_update',$this->plugin_name,'os_update');
+		
 		
 	}
 	
-	function set_install_status($inst_id, $status) {
+	private function set_install_status($inst_id, $status) {
         global $app;
         
         $app->db->query("UPDATE software_update_inst SET status = '{$status}' WHERE software_update_inst_id = '{$inst_id}'");
         $app->dbmaster->query("UPDATE software_update_inst SET status = '{$status}' WHERE software_update_inst_id = '{$inst_id}'");
     }
     
-	function process($event_name,$data) {
+	public function process($event_name,$data) {
 		global $app, $conf;
 		
 		//* Get the info of the package:
@@ -271,6 +273,26 @@
 		}
 		
 	}
+	
+	//* Operating system update
+	public function os_update($action_name,$data) {
+		global $app;
+		 
+		//** Debian and compatible Linux distributions
+		if(file_exists('/etc/debian_version')) {
+			exec("aptitude update");
+			exec("aptitude safe-upgrade -y");
+			$app->log('Execeuted Debian / Ubuntu update',LOGLEVEL_DEBUG);
+		}
+
+		//** Gentoo Linux
+		if(file_exists('/etc/gentoo-release')) {
+			exec("glsa-check -f --nocolor affected");
+			$app->log('Execeuted Gentoo update',LOGLEVEL_DEBUG);
+		}
+		
+		return 'ok';
+	}
 
 } // end class
 
diff --git a/server/server.php b/server/server.php
index 869a621..3683f19 100644
--- a/server/server.php
+++ b/server/server.php
@@ -144,27 +144,26 @@
 
 	$tmp_num_records = $tmp_rec['number'];
 	unset($tmp_rec);
-
+	
+	//** Load required base-classes
+	$app->uses('modules,plugins,file,services');
+	//** Load the modules that are in the mods-enabled folder
+	$app->modules->loadModules('all');
+	//** Load the plugins that are in the plugins-enabled folder
+	$app->plugins->loadPlugins('all');
 	if ($tmp_num_records > 0) {
-		/*
-		  There is something to do, triggert by the database -> do it!
-		 */
-		// Write the Log
 		$app->log("Found $tmp_num_records changes, starting update process.", LOGLEVEL_DEBUG);
-		// Load required base-classes
-		$app->uses('modules,plugins,file,services');
-		// Load the modules that are in the mods-enabled folder
-		$app->modules->loadModules('all');
-		// Load the plugins that are in the plugins-enabled folder
-		$app->plugins->loadPlugins('all');
-		// Go through the sys_datalog table and call the processing functions
-		// from the modules that are hooked on to the table actions
+		//** Go through the sys_datalog table and call the processing functions
+		//** from the modules that are hooked on to the table actions
 		$app->modules->processDatalog();
-		// Restart services that need to after configuration
-		$app->services->processDelayedActions();
-		// All modules are already loaded and processed, so there is NO NEED to load the core once again...
-		$needStartCore = false;
 	}
+	//** Process actions from sys_remoteaction table
+	$app->modules->processActions();
+	//** Restart services that need to after configuration
+	$app->services->processDelayedActions();
+	//** All modules are already loaded and processed, so there is NO NEED to load the core once again...
+	$needStartCore = false;
+	
 } else {
 	if ($app->db->connect->connect_error == NULL) {
 		$app->log('Unable to connect to local server.' . $app->db->errorMessage, LOGLEVEL_WARN);

--
Gitblit v1.9.1