From 5a43e7a2ea0cf7af35c100cb67e4a53566cbc496 Mon Sep 17 00:00:00 2001 From: tbrehm Date: Fri, 3 Feb 2012 10:58:01 +0000 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. --- install/sql/incremental/upd_0028.sql | 12 ++ install/sql/ispconfig3.sql | 20 +++ install/tpl/server.ini.master | 2 +- .../lib/classes/plugin_backuplist.inc.php | 138 +++++++++++++++ .../web/admin/form/server_config.tform.php | 10 +- .../web/admin/lib/lang/en_server_config.lng | 5 +- .../templates/server_config_server_edit.htm | 10 +- interface/web/js/scrigo.js.php | 6 + interface/web/mail/mail_user_edit.php | 8 +- interface/web/sites/database_edit.php | 93 +++-------- interface/web/sites/form/database.tform.php | 11 ++ interface/web/sites/form/web_domain.tform.php | 11 +- interface/web/sites/lib/lang/en_database.lng | 5 + .../web/sites/lib/lang/en_web_backup_list.lng | 16 ++ .../web/sites/templates/database_edit.htm | 18 +- .../web/sites/templates/web_backup_list.htm | 40 +++++ .../web/sites/templates/web_domain_backup.htm | 2 + interface/web/sites/tools.inc.php | 2 +- interface/web/sites/web_domain_del.php | 6 + interface/web/sites/web_domain_edit.php | 23 +++ .../default/css/screen/content_ispc.css | 2 + server/cron_daily.php | 158 ++++++++++++++---- server/lib/classes/db_mysql.inc.php | 7 +- server/lib/classes/modules.inc.php | 38 +++++ server/lib/classes/plugins.inc.php | 42 ++++- .../remoteaction_core_module.inc.php | 7 +- server/mods-available/web_module.inc.php | 11 +- .../plugins-available/backup_plugin.inc.php | 134 +++++++++++++++ .../mysql_clientdb_plugin.inc.php | 12 +- .../plugins-available/openvz_plugin.inc.php | 54 +++++- .../software_update_plugin.inc.php | 34 +++- server/server.php | 33 ++-- 32 files changed, 804 insertions(+), 166 deletions(-) create mode 100644 install/sql/incremental/upd_0028.sql create mode 100644 interface/lib/classes/plugin_backuplist.inc.php create mode 100644 interface/web/sites/lib/lang/en_web_backup_list.lng create mode 100644 interface/web/sites/templates/web_backup_list.htm create mode 100644 server/plugins-available/backup_plugin.inc.php diff --git a/install/sql/incremental/upd_0028.sql b/install/sql/incremental/upd_0028.sql new file mode 100644 index 000000000..0020cdd9a --- /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 9c2d5cfaa..fdd31a89c 100644 --- a/install/sql/ispconfig3.sql +++ b/install/sql/ispconfig3.sql @@ -1437,6 +1437,23 @@ CREATE TABLE `webdav_user` ( -- -------------------------------------------------------- +-- +-- 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 @@ CREATE TABLE `web_database` ( `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 @@ CREATE TABLE `web_database` ( `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 71e61e6cb..275fa7ace 100644 --- a/install/tpl/server.ini.master +++ b/install/tpl/server.ini.master @@ -13,7 +13,7 @@ hostname=server1.domain.tld 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 000000000..ac0396b3f --- /dev/null +++ b/interface/lib/classes/plugin_backuplist.inc.php @@ -0,0 +1,138 @@ +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 027328def..24f3f18c6 100644 --- a/interface/web/admin/form/server_config.tform.php +++ b/interface/web/admin/form/server_config.tform.php @@ -138,11 +138,13 @@ $form["tabs"]['server'] = array( '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 ec2e05341..4cce27548 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["network_config_warning_txt"] = 'The network configuration option is only av $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["set_folder_permissions_on_update_txt"] = 'Set folder permissions on update' $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 8f42d8cc5..7b777bf41 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 @@
-

{tmpl_var name='backup_dir_ftpread_txt'}

-
- {tmpl_var name='backup_dir_ftpread'} -
-
+ + + diff --git a/interface/web/js/scrigo.js.php b/interface/web/js/scrigo.js.php index 94b70754f..f8da5387a 100644 --- a/interface/web/js/scrigo.js.php +++ b/interface/web/js/scrigo.js.php @@ -269,6 +269,12 @@ function del_record(link,confirmation) { } } +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 f57a4e8d1..398bf12da 100644 --- a/interface/web/mail/mail_user_edit.php +++ b/interface/web/mail/mail_user_edit.php @@ -231,14 +231,14 @@ class page_action extends tform_actions { // 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 @@ class page_action extends tform_actions { // 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 @@ class page_action extends tform_actions { } 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 e06694398..501c65033 100644 --- a/interface/web/sites/database_edit.php +++ b/interface/web/sites/database_edit.php @@ -91,20 +91,6 @@ class page_action extends tform_actions { $tmp = $app->db->queryOneRecord("SELECT server_name FROM server WHERE server_id = $client[default_webserver]"); $app->tpl->setVar("server_id",""); 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 = ''; - //$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 .= "\r\n"; - } - } - $app->tpl->setVar("client_group_id",$client_select); } else { @@ -117,33 +103,6 @@ class page_action extends tform_actions { $server_id = $tmp['server_id']; } - $sql = "SELECT ip_address FROM server_ip WHERE server_id = $server_id"; - $ips = $app->db->queryAllRecords($sql); - $ip_select = ""; - //$ip_select = ""; - if(is_array($ips)) { - foreach( $ips as $ip) { - $selected = ($ip["ip_address"] == $this->dataRecord["ip_address"])?'SELECTED':''; - $ip_select .= "\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 = ""; - //$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 .= "\r\n"; - } - } - $app->tpl->setVar("client_group_id",$client_select); - } /* @@ -212,9 +171,6 @@ class page_action extends tform_actions { } } - - // 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 @@ class page_action extends tform_actions { 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").'
'; //* Get the database name and database user prefix $app->uses('getconf'); @@ -290,6 +244,9 @@ class page_action extends tform_actions { 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").'
'; + //* Database username and database name shall not be empty if($this->dataRecord['database_name'] == '') $app->tform->errorMessage .= $app->tform->wordbook["database_name_error_empty"].'
'; if($this->dataRecord['database_user'] == '') $app->tform->errorMessage .= $app->tform->wordbook["database_user_error_empty"].'
'; @@ -330,31 +287,33 @@ 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 - // 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 ffb711fbf..9bef10939 100644 --- a/interface/web/sites/form/database.tform.php +++ b/interface/web/sites/form/database.tform.php @@ -69,6 +69,17 @@ $form["tabs"]['database'] = array ( ), '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 34cbbc8f5..241e6eaca 100644 --- a/interface/web/sites/form/web_domain.tform.php +++ b/interface/web/sites/form/web_domain.tform.php @@ -431,7 +431,7 @@ $form["tabs"]['stats'] = array ( ) ); -if($_SESSION["s"]["user"]["typ"] == 'admin') { +// if($_SESSION["s"]["user"]["typ"] == 'admin') { //* Backup $form["tabs"]['backup'] = array ( @@ -458,10 +458,17 @@ $form["tabs"]['backup'] = array ( ################################## # 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 48d7ea3b1..abf302ec6 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_name_change_txt"] = 'The database name can not be changed'; $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 000000000..1ac668762 --- /dev/null +++ b/interface/web/sites/lib/lang/en_web_backup_list.lng @@ -0,0 +1,16 @@ + \ 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 99e1d07cd..59b1bff77 100644 --- a/interface/web/sites/templates/database_edit.htm +++ b/interface/web/sites/templates/database_edit.htm @@ -20,21 +20,13 @@ -
- - -
- -
- - + {tmpl_var name='parent_domain_id'} -
-
+
+ + {tmpl_var name='backup_records'} diff --git a/interface/web/sites/tools.inc.php b/interface/web/sites/tools.inc.php index 38e880432..316abe632 100644 --- a/interface/web/sites/tools.inc.php +++ b/interface/web/sites/tools.inc.php @@ -96,7 +96,7 @@ function getClientID($dataRecord) { } } /* 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 a69923f85..f84f4f106 100644 --- a/interface/web/sites/web_domain_del.php +++ b/interface/web/sites/web_domain_del.php @@ -91,6 +91,12 @@ class page_action extends tform_actions { $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 b6236bd3f..91b965c12 100644 --- a/interface/web/sites/web_domain_edit.php +++ b/interface/web/sites/web_domain_edit.php @@ -607,6 +607,14 @@ class page_action extends tform_actions { } 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 @@ class page_action extends tform_actions { $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 9f53502fe..9e65bd41c 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 67d994587..87ba2ee34 100644 --- a/server/cron_daily.php +++ b/server/cron_daily.php @@ -1,7 +1,7 @@ dbmaster == $app->db) { $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 @@ if($backup_dir != '') { 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 @@ if($backup_dir != '') { $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); + + //* 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'); - // Rename or remove old backups + //* 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'); + $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; + } } + $dir_handle->close(); + + rsort($files); - 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'); + 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']); } } - - 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']); + + 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 @@ if($backup_dir != '') { } } } + + $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 18f09bf5f..54561c07d 100644 --- a/server/lib/classes/db_mysql.inc.php +++ b/server/lib/classes/db_mysql.inc.php @@ -1,4 +1,3 @@ - 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 @@ public function toLower($record) { } - ?> + ?> \ No newline at end of file diff --git a/server/lib/classes/modules.inc.php b/server/lib/classes/modules.inc.php index 2c5b34947..ef600bde4 100644 --- a/server/lib/classes/modules.inc.php +++ b/server/lib/classes/modules.inc.php @@ -229,8 +229,46 @@ class modules { $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 = '"; + fwrite($fp, $content); + fclose($fp); + } + } diff --git a/server/lib/classes/plugins.inc.php b/server/lib/classes/plugins.inc.php index ba129815e..afef438ba 100644 --- a/server/lib/classes/plugins.inc.php +++ b/server/lib/classes/plugins.inc.php @@ -1,7 +1,7 @@ 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 49294f47b..5ee3fc6db 100644 --- a/server/mods-available/remoteaction_core_module.inc.php +++ b/server/mods-available/remoteaction_core_module.inc.php @@ -1,6 +1,6 @@ _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 653940f83..086b601c7 100644 --- a/server/mods-available/web_module.inc.php +++ b/server/mods-available/web_module.inc.php @@ -49,7 +49,10 @@ class web_module { '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 @@ class web_module { $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 @@ class web_module { 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 000000000..dfe3ddd5b --- /dev/null +++ b/server/plugins-available/backup_plugin.inc.php @@ -0,0 +1,134 @@ +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 624ef2b60..59a6cf480 100644 --- a/server/plugins-available/mysql_clientdb_plugin.inc.php +++ b/server/plugins-available/mysql_clientdb_plugin.inc.php @@ -100,7 +100,7 @@ class mysql_clientdb_plugin { 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 @@ class mysql_clientdb_plugin { } 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 @@ class mysql_clientdb_plugin { } $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 @@ class mysql_clientdb_plugin { } $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 @@ class mysql_clientdb_plugin { //* 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 ac9e0aaa0..9e0d75d3f 100644 --- a/server/plugins-available/openvz_plugin.inc.php +++ b/server/plugins-available/openvz_plugin.inc.php @@ -1,7 +1,7 @@ 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 @@ class openvz_plugin { } + 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 55feaee2e..a6b71625f 100644 --- a/server/plugins-available/software_update_plugin.inc.php +++ b/server/plugins-available/software_update_plugin.inc.php @@ -1,7 +1,7 @@ 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 @@ class software_update_plugin { } } + + //* 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 869a621f5..3683f1968 100644 --- a/server/server.php +++ b/server/server.php @@ -144,27 +144,26 @@ if ($app->db->connect_error == NULL && $app->dbmaster->connect_error == NULL) { $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); -- GitLab