From 8d0c6b7369d168a1a9ad329c56c63b2e28575d5a Mon Sep 17 00:00:00 2001 From: tbrehm Date: Thu, 18 Feb 2010 14:33:52 +0000 Subject: [PATCH] Implemented: FS#555 - Implemet traffic quota. --- install/sql/ispconfig3.sql | 5 +- interface/lib/classes/tform.inc.php | 2 +- interface/web/client/form/client.tform.php | 14 +++ .../web/client/form/client_template.tform.php | 14 +++ interface/web/client/form/reseller.tform.php | 14 +++ interface/web/client/lib/lang/en_client.lng | 2 + .../client/lib/lang/en_client_template.lng | 2 + interface/web/client/lib/lang/en_reseller.lng | 2 + .../client/templates/client_edit_limits.htm | 4 + .../templates/client_template_edit_limits.htm | 4 + .../client/templates/reseller_edit_limits.htm | 4 + interface/web/sites/form/web_domain.tform.php | 2 - .../web/sites/lib/lang/en_web_domain.lng | 2 +- .../web/sites/templates/web_domain_edit.htm | 4 +- interface/web/sites/web_domain_edit.php | 18 ++- server/cron_daily.php | 52 +++++++++ server/lib/classes/db_mysql.inc.php | 110 ++++++++++++++++++ 17 files changed, 246 insertions(+), 9 deletions(-) diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql index d2df925bab..b4c5793612 100644 --- a/install/sql/ispconfig3.sql +++ b/install/sql/ispconfig3.sql @@ -97,6 +97,7 @@ CREATE TABLE `client` ( `limit_cron` int(11) NOT NULL default '0', `limit_cron_type` enum('url','chrooted','full') NOT NULL default 'url', `limit_cron_frequency` int(11) NOT NULL default '5', + `limit_traffic_quota` int(11) NOT NULL default '-1', `limit_client` int(11) NOT NULL default '0', `parent_client_id` int(11) unsigned NOT NULL default '0', `username` varchar(64) default NULL, @@ -150,6 +151,7 @@ CREATE TABLE `client_template` ( `limit_cron` int(11) NOT NULL default '0', `limit_cron_type` enum('url','chrooted','full') NOT NULL default 'url', `limit_cron_frequency` int(11) NOT NULL default '5', + `limit_traffic_quota` int(11) NOT NULL default '-1', `limit_client` int(11) NOT NULL default '0', PRIMARY KEY (`template_id`) ) ENGINE=MyISAM AUTO_INCREMENT=1; @@ -1035,7 +1037,7 @@ CREATE TABLE `web_domain` ( `system_user` varchar(255) default NULL, `system_group` varchar(255) default NULL, `hd_quota` bigint(20) NOT NULL default '0', - `traffic_quota` bigint(20) NOT NULL default '0', + `traffic_quota` bigint(20) NOT NULL default '-1', `cgi` enum('n','y') NOT NULL default 'y', `ssi` enum('n','y') NOT NULL default 'y', `suexec` enum('n','y') NOT NULL default 'y', @@ -1061,6 +1063,7 @@ CREATE TABLE `web_domain` ( `apache_directives` text, `php_open_basedir` text, `active` enum('n','y') NOT NULL default 'y', + `traffic_quota_lock` enum('n','y') NOT NULL default 'n', PRIMARY KEY (`domain_id`) ) ENGINE=MyISAM AUTO_INCREMENT=1; diff --git a/interface/lib/classes/tform.inc.php b/interface/lib/classes/tform.inc.php index 31916acee4..4e047da76b 100644 --- a/interface/lib/classes/tform.inc.php +++ b/interface/lib/classes/tform.inc.php @@ -1199,7 +1199,7 @@ class tform { // translation function for forms, tries the form wordbook first and if this fails, it tries the global wordbook function lng($msg) { - global $app; + global $app,$conf; if(isset($this->wordbook[$msg])) { return $this->wordbook[$msg]; diff --git a/interface/web/client/form/client.tform.php b/interface/web/client/form/client.tform.php index cd599dfa1e..99b1cab2d0 100644 --- a/interface/web/client/form/client.tform.php +++ b/interface/web/client/form/client.tform.php @@ -727,6 +727,20 @@ $form["tabs"]['limits'] = array ( 'rows' => '', 'cols' => '' ), + 'limit_traffic_quota' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'TEXT', + 'validators' => array ( 0 => array ( 'type' => 'ISINT', + 'errmsg'=> 'limit_traffic_quota_error_notint'), + ), + 'default' => '-1', + 'value' => '', + 'separator' => '', + 'width' => '10', + 'maxlength' => '10', + 'rows' => '', + 'cols' => '' + ), ################################## # END Datatable fields ################################## diff --git a/interface/web/client/form/client_template.tform.php b/interface/web/client/form/client_template.tform.php index 6f924501f3..1b7ed25a95 100644 --- a/interface/web/client/form/client_template.tform.php +++ b/interface/web/client/form/client_template.tform.php @@ -459,6 +459,20 @@ $form["tabs"]['limits'] = array ( 'rows' => '', 'cols' => '' ), + 'limit_traffic_quota' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'TEXT', + 'validators' => array ( 0 => array ( 'type' => 'ISINT', + 'errmsg'=> 'limit_traffic_quota_error_notint'), + ), + 'default' => '-1', + 'value' => '', + 'separator' => '', + 'width' => '10', + 'maxlength' => '10', + 'rows' => '', + 'cols' => '' + ), ################################## # END Datatable fields ################################## diff --git a/interface/web/client/form/reseller.tform.php b/interface/web/client/form/reseller.tform.php index a7c5ea6b08..4cfb52d631 100644 --- a/interface/web/client/form/reseller.tform.php +++ b/interface/web/client/form/reseller.tform.php @@ -714,6 +714,20 @@ $form["tabs"]['limits'] = array ( 'rows' => '', 'cols' => '' ), + 'limit_traffic_quota' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'TEXT', + 'validators' => array ( 0 => array ( 'type' => 'ISINT', + 'errmsg'=> 'limit_traffic_quota_error_notint'), + ), + 'default' => '-1', + 'value' => '', + 'separator' => '', + 'width' => '10', + 'maxlength' => '10', + 'rows' => '', + 'cols' => '' + ), ################################## # END Datatable fields ################################## diff --git a/interface/web/client/lib/lang/en_client.lng b/interface/web/client/lib/lang/en_client.lng index 4c0332cc6c..e87ba45e07 100644 --- a/interface/web/client/lib/lang/en_client.lng +++ b/interface/web/client/lib/lang/en_client.lng @@ -92,4 +92,6 @@ $wb["ssh_chroot_txt"] = 'SSH-Chroot Options'; $wb["web_php_options_txt"] = 'PHP Options'; $wb["limit_client_error"] = 'The max. number of clients is reached.'; $wb["limit_web_quota_txt"] = 'Web Quota'; +$wb["limit_traffic_quota_txt"] = 'Traffic Quota'; +$wb["limit_trafficquota_error_notint"] = 'Traffic Quota must be a number.'; ?> diff --git a/interface/web/client/lib/lang/en_client_template.lng b/interface/web/client/lib/lang/en_client_template.lng index f7299744a8..53878e97d6 100644 --- a/interface/web/client/lib/lang/en_client_template.lng +++ b/interface/web/client/lib/lang/en_client_template.lng @@ -55,4 +55,6 @@ $wb["limit_cron_error_notint"] = 'The cron limit must be a number.'; $wb["limit_cron_error_frequency"] = 'The cron frequency limit must be a number.'; $wb["error_template_name_empty"] = 'Please enter a Template name'; $wb["limit_web_quota_txt"] = 'Web Quota'; +$wb["limit_traffic_quota_txt"] = 'Traffic Quota'; +$wb["limit_trafficquota_error_notint"] = 'Traffic Quota must be a number.'; ?> \ No newline at end of file diff --git a/interface/web/client/lib/lang/en_reseller.lng b/interface/web/client/lib/lang/en_reseller.lng index e3e7e30704..2b06c33d30 100644 --- a/interface/web/client/lib/lang/en_reseller.lng +++ b/interface/web/client/lib/lang/en_reseller.lng @@ -91,4 +91,6 @@ $wb["web_php_options_txt"] = 'PHP Options'; $wb["limit_client_error"] = 'The max. number of clients is reached.'; $wb["limit_client_error_positive"] = 'The number of clients must be > 0'; $wb["limit_web_quota_txt"] = 'Web Quota'; +$wb["limit_traffic_quota_txt"] = 'Traffic Quota'; +$wb["limit_trafficquota_error_notint"] = 'Traffic Quota must be a number.'; ?> diff --git a/interface/web/client/templates/client_edit_limits.htm b/interface/web/client/templates/client_edit_limits.htm index 3bdca10974..ba18508709 100644 --- a/interface/web/client/templates/client_edit_limits.htm +++ b/interface/web/client/templates/client_edit_limits.htm @@ -173,6 +173,10 @@ +
+ +  MB +
diff --git a/interface/web/client/templates/client_template_edit_limits.htm b/interface/web/client/templates/client_template_edit_limits.htm index 8f5d3d2655..6509329eed 100644 --- a/interface/web/client/templates/client_template_edit_limits.htm +++ b/interface/web/client/templates/client_template_edit_limits.htm @@ -107,6 +107,10 @@ +
+ +  MB +
diff --git a/interface/web/client/templates/reseller_edit_limits.htm b/interface/web/client/templates/reseller_edit_limits.htm index d413eb5dc7..dfed34f65b 100644 --- a/interface/web/client/templates/reseller_edit_limits.htm +++ b/interface/web/client/templates/reseller_edit_limits.htm @@ -162,6 +162,10 @@ +
+ +  MB +
diff --git a/interface/web/sites/form/web_domain.tform.php b/interface/web/sites/form/web_domain.tform.php index d0a31d8ae7..1e5afad80d 100644 --- a/interface/web/sites/form/web_domain.tform.php +++ b/interface/web/sites/form/web_domain.tform.php @@ -139,7 +139,6 @@ $form["tabs"]['domain'] = array ( 'width' => '7', 'maxlength' => '7' ), - /* 'traffic_quota' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', @@ -151,7 +150,6 @@ $form["tabs"]['domain'] = array ( 'width' => '7', 'maxlength' => '7' ), - */ 'cgi' => array ( 'datatype' => 'VARCHAR', 'formtype' => 'CHECKBOX', diff --git a/interface/web/sites/lib/lang/en_web_domain.lng b/interface/web/sites/lib/lang/en_web_domain.lng index 2ad101375f..474240b73a 100644 --- a/interface/web/sites/lib/lang/en_web_domain.lng +++ b/interface/web/sites/lib/lang/en_web_domain.lng @@ -54,5 +54,5 @@ $wb["ssl_locality_error_regex"] = 'Invalid SSL Locality. Valid characters are: a $wb["ssl_organisation_error_regex"] = 'Invalid SSL Organisation. Valid characters are: a-z, 0-9 and .,-_'; $wb["ssl_organistaion_unit_error_regex"] = 'Invalid SSL Organisation Unit. Valid characters are: a-z, 0-9 and .,-_'; $wb["ssl_country_error_regex"] = 'Invalid SSL Country. Valid characters are: A-Z'; - +$wb["limit_web_quota_free_txt"] = 'Max. available Traffic Quota'; ?> diff --git a/interface/web/sites/templates/web_domain_edit.htm b/interface/web/sites/templates/web_domain_edit.htm index d19550fc7f..3ea0603f21 100644 --- a/interface/web/sites/templates/web_domain_edit.htm +++ b/interface/web/sites/templates/web_domain_edit.htm @@ -49,12 +49,10 @@  MB -

{tmpl_var name='cgi_txt'}

diff --git a/interface/web/sites/web_domain_edit.php b/interface/web/sites/web_domain_edit.php index 6ad9f4b2d1..77367000e7 100644 --- a/interface/web/sites/web_domain_edit.php +++ b/interface/web/sites/web_domain_edit.php @@ -217,7 +217,7 @@ class page_action extends tform_actions { if($_SESSION["s"]["user"]["typ"] != 'admin') { // Get the limits of the client $client_group_id = $_SESSION["s"]["user"]["default_group"]; - $client = $app->db->queryOneRecord("SELECT limit_web_domain, default_webserver, parent_client_id, limit_web_quota FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id"); + $client = $app->db->queryOneRecord("SELECT limit_traffic_quota, limit_web_domain, default_webserver, parent_client_id, limit_web_quota FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id"); //* Check the website quota if(isset($_POST["hd_quota"]) && $client["limit_web_quota"] >= 0) { @@ -235,6 +235,22 @@ class page_action extends tform_actions { unset($tmp_quota); } + //* Check the traffic quota + if(isset($_POST["traffic_quota"]) && $client["limit_traffic_quota"] > 0) { + $tmp = $app->db->queryOneRecord("SELECT sum(traffic_quota) as trafficquota FROM web_domain WHERE domain_id != ".intval($this->id)." AND ".$app->tform->getAuthSQL('u')); + $trafficquota = $tmp["trafficquota"]; + $new_traffic_quota = intval($this->dataRecord["traffic_quota"]); + if(($trafficquota + $new_traffic_quota > $client["limit_traffic_quota"]) || ($new_traffic_quota == -1 && $client["limit_traffic_quota"] != -1)) { + $max_free_quota = floor($client["limit_traffic_quota"] - $trafficquota); + if($max_free_quota < 0) $max_free_quota = 0; + $app->tform->errorMessage .= $app->tform->lng("limit_traffic_quota_free_txt").": ".$max_free_quota." MB
"; + // Set the quota field to the max free space + $this->dataRecord["traffic_quota"] = $max_free_quota; + } + unset($tmp); + unset($tmp_quota); + } + // When the record is updated if($this->id > 0) { // restore the server ID if the user is not admin and record is edited diff --git a/server/cron_daily.php b/server/cron_daily.php index 0d949367cc..c68f8ab893 100644 --- a/server/cron_daily.php +++ b/server/cron_daily.php @@ -233,5 +233,57 @@ if ($app->dbmaster == $app->db) { } } +####################################################################################################### +// enforce traffic quota (only the "master-server") +####################################################################################################### + +if ($app->dbmaster == $app->db) { + + $current_month = date('Y-m'); + + //* Check website traffic quota + $sql = "SELECT sys_groupid,domain_id,domain,traffic_quota,traffic_quota_lock FROM web_domain WHERE traffic_quota > 0 and type = 'vhost'"; + $records = $app->db->queryAllRecords($sql); + if(is_array($records)) { + foreach($records as $rec) { + + $web_traffic_quota = $rec['traffic_quota']; + $domain = $rec['web_domain']; + + // get the client + /* + $client_group_id = $rec["sys_groupid"]; + $client = $app->db->queryOneRecord("SELECT limit_traffic_quota,parent_client_id FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id"); + $reseller = $app->db->queryOneRecord("SELECT limit_traffic_quota FROM client WHERE client_id = ".intval($client['parent_client_id'])); + + $client_traffic_quota = intval($client['limit_traffic_quota']); + $reseller_traffic_quota = intval($reseller['limit_traffic_quota']); + */ + + //* get the traffic + $tmp = $app->db->query("SELECT traffic_bytes FROM web_traffic WHERE traffic_date like '$current_month%' AND hostname = '$domain'"); + $web_traffic = $tmp['traffic_bytes']/1024/1024; + + //* Website is over quota, we will disable it + /*if( ($web_traffic_quota > 0 && $web_traffic > $web_traffic_quota) || + ($client_traffic_quota > 0 && $web_traffic > $client_traffic_quota) || + ($reseller_traffic_quota > 0 && $web_traffic > $reseller_traffic_quota)) {*/ + if($web_traffic_quota > 0 && $web_traffic > $web_traffic_quota) { + $app->db->datalogUpdate('web_domain', "traffic_quota_lock = 'y',active = 'n'", 'domain_id', $rec['domain_id']); + $app->log("Traffic quota for ".$rec['domain_id']." Exceeded. Disabling website.",LOGLEVEL_DEBUG); + } else { + //* unlock the website, if traffic is lower then quota + if($rec['traffic_quota_lock'] == 'y') { + $app->db->datalogUpdate('web_domain', "traffic_quota_lock = 'n',active = 'y'", 'domain_id', $rec['domain_id']); + $app->log("Traffic quota for ".$rec['domain_id']." ok again. Enabling website.",LOGLEVEL_DEBUG); + } + } + } + } + + +} + + die("finished.\n"); ?> \ No newline at end of file diff --git a/server/lib/classes/db_mysql.inc.php b/server/lib/classes/db_mysql.inc.php index 364fd7e7ea..229b11e49e 100644 --- a/server/lib/classes/db_mysql.inc.php +++ b/server/lib/classes/db_mysql.inc.php @@ -229,6 +229,116 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } } */ + + public function diffrec($record_old, $record_new) { + $diffrec_full = array(); + $diff_num = 0; + + 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) { + // Record has changed + $diffrec_full['old'][$key] = $val; + $diffrec_full['new'][$key] = $record_new[$key]; + $diff_num++; + } else { + $diffrec_full['old'][$key] = $val; + $diffrec_full['new'][$key] = $val; + } + } + } elseif(is_array($record_new)) { + foreach($record_new as $key => $val) { + if(isset($record_new[$key]) && @$record_old[$key] != $val) { + // Record has changed + $diffrec_full['new'][$key] = $val; + $diffrec_full['old'][$key] = @$record_old[$key]; + $diff_num++; + } else { + $diffrec_full['new'][$key] = $val; + $diffrec_full['old'][$key] = $val; + } + } + } + + return array('diff_num' => $diff_num, 'diff_rec' => $diffrec_full); + + } + + //** Function to fill the datalog with a full differential record. + public function datalogSave($db_table, $action, $primary_field, $primary_id, $record_old, $record_new) { + global $app,$conf; + + // Insert backticks only for incomplete table names. + if(stristr($db_table,'.')) { + $escape = ''; + } else { + $escape = '`'; + } + + $tmp = $this->diffrec($record_old, $record_new); + $diffrec_full = $tmp['diff_rec']; + $diff_num = $tmp['diff_num']; + unset($tmp); + + // Insert the server_id, if the record has a server_id + $server_id = (isset($record_old["server_id"]) && $record_old["server_id"] > 0)?$record_old["server_id"]:0; + if(isset($record_new["server_id"])) $server_id = $record_new["server_id"]; + + + if($diff_num > 0) { + //print_r($diff_num); + //print_r($diffrec_full); + $diffstr = $app->db->quote(serialize($diffrec_full)); + $username = $app->db->quote($_SESSION["s"]["user"]["username"]); + $dbidx = $primary_field.":".$primary_id; + + if($action == 'INSERT') $action = 'i'; + if($action == 'UPDATE') $action = 'u'; + if($action == 'DELETE') $action = 'd'; + $sql = "INSERT INTO sys_datalog (dbtable,dbidx,server_id,action,tstamp,user,data) VALUES ('".$db_table."','$dbidx','$server_id','$action','".time()."','$username','$diffstr')"; + $app->db->query($sql); + } + + return true; + } + + //** Inserts a record and saves the changes into the datalog + public function datalogInsert($tablename, $insert_data, $index_field) { + global $app; + + $old_rec = array(); + $this->query("INSERT INTO $tablename $insert_data"); + $index_value = $this->insertID(); + $new_rec = $this->queryOneRecord("SELECT * FROM $tablename WHERE $index_field = '$index_value'"); + $this->datalogSave($tablename, 'INSERT', $index_field, $index_value, $old_rec, $new_rec); + + return $index_value; + } + + //** Updates a record and saves the changes into the datalog + public function datalogUpdate($tablename, $update_data, $index_field, $index_value) { + global $app; + + $old_rec = $this->queryOneRecord("SELECT * FROM $tablename WHERE $index_field = '$index_value'"); + $this->query("UPDATE $tablename SET $update_data WHERE $index_field = '$index_value'"); + $new_rec = $this->queryOneRecord("SELECT * FROM $tablename WHERE $index_field = '$index_value'"); + $this->datalogSave($tablename, 'UPDATE', $index_field, $index_value, $old_rec, $new_rec); + + return true; + } + + //** Deletes a record and saves the changes into the datalog + public function datalogDelete($tablename, $index_field, $index_value) { + global $app; + + $old_rec = $this->queryOneRecord("SELECT * FROM $tablename WHERE $index_field = '$index_value'"); + $this->query("DELETE FROM $tablename WHERE $index_field = '$index_value'"); + $new_rec = array(); + $this->datalogSave($tablename, 'DELETE', $index_field, $index_value, $old_rec, $new_rec); + + return true; + } public function closeConn() { -- GitLab