mcramer
2013-08-22 5de2af21516f28817ff0a7d6274142e26e3e1253
- Implemented: New cron class and split up cron_daily
- Implemented: FS#3110 - Turn monitoring module into cron jobs

2 files deleted
3 files modified
34 files added
9968 ■■■■■ changed files
install/sql/ispconfig3.sql 16 ●●●●● patch | view | raw | blame | history
server/cron.php 90 ●●●●● patch | view | raw | blame | history
server/cron.sh 12 ●●●●● patch | view | raw | blame | history
server/cron_daily.php 1346 ●●●●● patch | view | raw | blame | history
server/cron_daily.sh 15 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-mailbox_stats.inc.php 269 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_clamav_log.inc.php 179 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_cpu.inc.php 136 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_disk_usage.inc.php 166 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_email_quota.inc.php 149 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_fail2ban.inc.php 127 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_hd_quota.inc.php 162 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_iptables.inc.php 132 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_ispconfig_log.inc.php 146 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_ispconfig_version.inc.php 110 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_mail_log.inc.php 181 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_mail_queue.inc.php 138 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_mem_usage.inc.php 124 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_mongodb.inc.php 127 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_openvz.inc.php 183 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_os_version.inc.php 112 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_raid.inc.php 263 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_rkhunter.inc.php 127 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_server.inc.php 133 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_services.inc.php 94 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_syslog.inc.php 153 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/100-monitor_system_update.inc.php 208 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/150-awstats.inc.php 183 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/150-webalizer.inc.php 142 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/200-logfiles.inc.php 271 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/300-quota_notify.inc.php 479 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/400-openvz.inc.php 83 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.d/500-backup.inc.php 330 ●●●●● patch | view | raw | blame | history
server/lib/classes/cron.inc.php 271 ●●●●● patch | view | raw | blame | history
server/lib/classes/cronjob.inc.php 158 ●●●●● patch | view | raw | blame | history
server/lib/classes/functions.inc.php 398 ●●●●● patch | view | raw | blame | history
server/lib/classes/libdatetime.inc.php 479 ●●●●● patch | view | raw | blame | history
server/lib/classes/monitor_tools.inc.php 1406 ●●●●● patch | view | raw | blame | history
server/mods-available/monitor_core_module.inc.php 870 ●●●●● patch | view | raw | blame | history
install/sql/ispconfig3.sql
@@ -1429,6 +1429,22 @@
  `value` varchar(255) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `sys_cron`
--
CREATE TABLE IF NOT EXISTS `sys_cron` (
  `name` varchar(50) NOT NULL,
  `last_run` datetime NULL DEFAULT NULL,
  `next_run` datetime NULL DEFAULT NULL,
  `running` tinyint(1) UNSIGNED NOT NULL DEFAULT '0',
  PRIMARY KEY (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
server/cron.php
New file
@@ -0,0 +1,90 @@
<?php
/*
Copyright (c) 2007-2012, Till Brehm, projektfarm Gmbh
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of ISPConfig nor the names of its contributors
      may be used to endorse or promote products derived from this software without
      specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
define('SCRIPT_PATH', dirname($_SERVER["SCRIPT_FILENAME"]));
require(SCRIPT_PATH."/lib/config.inc.php");
require(SCRIPT_PATH."/lib/app.inc.php");
set_time_limit(0);
ini_set('error_reporting', E_ALL & ~E_NOTICE);
// make sure server_id is always an int
$conf['server_id'] = intval($conf['server_id']);
// Load required base-classes
$app->uses('ini_parser,file,services,getconf,system,cron,functions');
$app->load('libdatetime,cronjob');
// read all cron jobs
$path = SCRIPT_PATH . '/lib/classes/cron.d';
if(!is_dir($path)) die('Cron path missing!');
$files = array();
$d = opendir($path);
while($f = readdir($d)) {
    $file_path = $path . '/' . $f;
    if($f === '.' || $f === '..' || !is_file($file_path)) continue;
    if(substr($f, strrpos($f, '.')) !== '.php') continue;
    $files[] = $f;
}
closedir($d);
// sort in alphabetical order, so we can use prefixes like 000-xxx
sort($files);
foreach($files as $f) {
    $name = substr($f, 0, strpos($f, '.'));
    if(preg_match('/^\d+\-(.*)$/', $name, $match)) $name = $match[1]; // strip numerical prefix from file name
    include($path . '/' . $f);
    $class_name = 'cronjob_' . $name;
    if(class_exists($class_name, false)) {
        $cronjob = new $class_name();
        if(get_parent_class($cronjob) !== 'cronjob') {
            print 'Invalid class ' . $class_name . ' not extending class cronjob (' . get_parent_class($cronjob) . ')!' . "\n";
            unset($cronjob);
            continue;
        }
        print 'Included ' . $class_name . ' from ' . $file_path . ' -> will now run job.' . "\n";
        $cronjob->run();
        print 'run job (' . $class_name . ') done.' . "\n";
        unset($cronjob);
    }
}
unset($files);
die("finished.\n");
?>
server/cron.sh
New file
@@ -0,0 +1,12 @@
#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin
if [ -f /usr/local/ispconfig/server/lib/php.ini ]; then
        PHPINIOWNER=`stat -c %U /usr/local/ispconfig/server/lib/php.ini`
        if [ $PHPINIOWNER == 'root' ] || [ $PHPINIOWNER == 'ispconfig'  ]; then
                export PHPRC=/usr/local/ispconfig/server/lib
        fi
fi
/usr/bin/php -q /usr/local/ispconfig/server/cron.php
server/cron_daily.php
File was deleted
server/cron_daily.sh
old mode 100644 new mode 100755
@@ -1,16 +1,3 @@
#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin
if [ -f /usr/local/ispconfig/server/lib/php.ini ]; then
        PHPINIOWNER=`stat -c %U /usr/local/ispconfig/server/lib/php.ini`
        if [ $PHPINIOWNER == 'root' ] || [ $PHPINIOWNER == 'ispconfig'  ]; then
                export PHPRC=/usr/local/ispconfig/server/lib
        fi
fi
/usr/bin/php -q /usr/local/ispconfig/server/cron_daily.php
if [ -f /usr/local/ispconfig/server/cron_daily_billing.sh ]; then
        /usr/local/ispconfig/server/cron_daily_billing.sh
fi
echo "This script is no longer used. Use the cron.sh instead."
server/lib/classes/cron.d/100-mailbox_stats.inc.php
New file
@@ -0,0 +1,269 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_mailbox_stats extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        // cronjob code here
        #######################################################################################################
        // store the mailbox statistics in the database
        #######################################################################################################
        $parse_mail_log = false;
        $sql = "SELECT mailuser_id,maildir FROM mail_user WHERE server_id = ".$conf['server_id'];
        $records = $app->db->queryAllRecords($sql);
        if(count($records) > 0) $parse_mail_log = true;
        foreach($records as $rec) {
            if(@is_file($rec['maildir'].'/ispconfig_mailsize')) {
                $parse_mail_log = false;
                // rename file
                rename($rec['maildir'].'/ispconfig_mailsize',$rec['maildir'].'/ispconfig_mailsize_save');
                // Read the file
                $lines = file($rec['maildir'].'/ispconfig_mailsize_save');
                $mail_traffic = 0;
                foreach($lines as $line) {
                    $mail_traffic += intval($line);
                }
                unset($lines);
                // Delete backup file
                if(@is_file($rec['maildir'].'/ispconfig_mailsize_save')) unlink($rec['maildir'].'/ispconfig_mailsize_save');
                // Save the traffic stats in the sql database
                $tstamp = date('Y-m');
                $sql = "SELECT * FROM mail_traffic WHERE month = '$tstamp' AND mailuser_id = ".$rec['mailuser_id'];
                $tr = $app->dbmaster->queryOneRecord($sql);
                $mail_traffic += $tr['traffic'];
                if($tr['traffic_id'] > 0) {
                    $sql = "UPDATE mail_traffic SET traffic = $mail_traffic WHERE traffic_id = ".$tr['traffic_id'];
                } else {
                    $sql = "INSERT INTO mail_traffic (month,mailuser_id,traffic) VALUES ('$tstamp',".$rec['mailuser_id'].",$mail_traffic)";
                }
                $app->dbmaster->query($sql);
                //echo $sql;
            }
        }
        if($parse_mail_log == true) {
            $mailbox_traffic = array();
            $mail_boxes = array();
            $mail_rewrites = array(); // we need to read all mail aliases and forwards because the address in amavis is not always the mailbox address
            function parse_mail_log_line($line) {
                //Oct 31 17:35:48 mx01 amavis[32014]: (32014-05) Passed CLEAN, [IPv6:xxxxx] [IPv6:xxxxx] <xxx@yyyy> -> <aaaa@bbbb>, Message-ID: <xxxx@yyyyy>, mail_id: xxxxxx, Hits: -1.89, size: 1591, queued_as: xxxxxxx, 946 ms
                if(preg_match('/^(\w+\s+\d+\s+\d+:\d+:\d+)\s+[^ ]+\s+amavis.* <([^>]+)>\s+->\s+((<[^>]+>,)+) .*Message-ID:\s+<([^>]+)>.* size:\s+(\d+),.*$/', $line, $matches) == false) return false;
                $timestamp = strtotime($matches[1]);
                if(!$timestamp) return false;
                $to = array();
                $recipients = explode(',', $matches[3]);
                foreach($recipients as $recipient) {
                    $recipient = substr($recipient, 1, -1);
                    if(!$recipient || $recipient == $matches[2]) continue;
                    $to[] = $recipient;
                }
                return array('line' => $line, 'timestamp' => $timestamp, 'size' => $matches[6], 'from' => $matches[2], 'to' => $to, 'message-id' => $matches[5]);
            }
            function add_mailbox_traffic(&$traffic_array, $address, $traffic) {
                global $mail_boxes, $mail_rewrites;
                $address = strtolower($address);
                if(in_array($address, $mail_boxes) == true) {
                    if(!isset($traffic_array[$address])) $traffic_array[$address] = 0;
                    $traffic_array[$address] += $traffic;
                } elseif(array_key_exists($address, $mail_rewrites)) {
                    foreach($mail_rewrites[$address] as $address) {
                        if(!isset($traffic_array[$address])) $traffic_array[$address] = 0;
                        $traffic_array[$address] += $traffic;
                    }
                } else {
                    // this is not a local address - skip it
                }
            }
            $sql = "SELECT email FROM mail_user WHERE server_id = ".$conf['server_id'];
            $records = $app->db->queryAllRecords($sql);
            foreach($records as $record) {
                $mail_boxes[] = $record['email'];
            }
            $sql = "SELECT source, destination FROM mail_forwarding WHERE server_id = ".$conf['server_id'];
            $records = $app->db->queryAllRecords($sql);
            foreach($records as $record) {
                $targets = preg_split('/[\n,]+/', $record['destination']);
                foreach($targets as $target) {
                    if(in_array($target, $mail_boxes)) {
                        if(isset($mail_rewrites[$record['source']])) $mail_rewrites[$record['source']][] = $target;
                        else $mail_rewrites[$record['source']] = array($target);
                    }
                }
            }
            $state_file = dirname(__FILE__) . '/mail_log_parser.state';
            $prev_line = false;
            $last_line = false;
            $cur_line = false;
            if(file_exists($state_file)) {
                $prev_line = parse_mail_log_line(trim(file_get_contents($state_file)));
                //if($prev_line) echo "continuing from previous run, log position: " . $prev_line['message-id'] . " at " . strftime('%d.%m.%Y %H:%M:%S', $prev_line['timestamp']) . "\n";
            }
            if(file_exists('/var/log/mail.log')) {
                $fp = fopen('/var/log/mail.log', 'r');
                //echo "Parsing mail.log...\n";
                $l = 0;
                while($line = fgets($fp, 8192)) {
                    $l++;
                    //if($l % 1000 == 0) echo "\rline $l";
                    $cur_line = parse_mail_log_line($line);
                    if(!$cur_line) continue;
                    if($prev_line) {
                        // check if this line has to be processed
                        if($cur_line['timestamp'] < $prev_line['timestamp']) {
                            $parse_mail_log = false; // we do not need to parse the second file!
                            continue; // already processed
                        } elseif($cur_line['timestamp'] == $prev_line['timestamp'] && $cur_line['message-id'] == $prev_line['message-id']) {
                            $parse_mail_log = false; // we do not need to parse the second file!
                            $prev_line = false; // this line has already been processed but the next one has to be!
                            continue;
                        }
                    }
                    add_mailbox_traffic($mailbox_traffic, $cur_line['from'], $cur_line['size']);
                    foreach($cur_line['to'] as $to) {
                        add_mailbox_traffic($mailbox_traffic, $to, $cur_line['size']);
                    }
                    $last_line = $line; // store for the state file
                }
                fclose($fp);
                //echo "\n";
            }
            if($parse_mail_log == true && file_exists('/var/log/mail.log.1')) {
                $fp = fopen('/var/log/mail.log.1', 'r');
                //echo "Parsing mail.log.1...\n";
                $l = 0;
                while($line = fgets($fp, 8192)) {
                    $l++;
                    //if($l % 1000 == 0) echo "\rline $l";
                    $cur_line = parse_mail_log_line($line);
                    if(!$cur_line) continue;
                    if($prev_line) {
                        // check if this line has to be processed
                        if($cur_line['timestamp'] < $prev_line['timestamp']) continue; // already processed
                        if($cur_line['timestamp'] == $prev_line['timestamp'] && $cur_line['message-id'] == $prev_line['message-id']) {
                            $prev_line = false; // this line has already been processed but the next one has to be!
                            continue;
                        }
                    }
                    add_mailbox_traffic($mailbox_traffic, $cur_line['from'], $cur_line['size']);
                    foreach($cur_line['to'] as $to) {
                        add_mailbox_traffic($mailbox_traffic, $to, $cur_line['size']);
                    }
                }
                fclose($fp);
                //echo "\n";
            }
            unset($mail_rewrites);
            unset($mail_boxes);
            // Save the traffic stats in the sql database
            $tstamp = date('Y-m');
            $sql = "SELECT mailuser_id,email FROM mail_user WHERE server_id = ".$conf['server_id'];
            $records = $app->db->queryAllRecords($sql);
            foreach($records as $rec) {
                if(array_key_exists($rec['email'], $mailbox_traffic)) {
                    $sql = "SELECT * FROM mail_traffic WHERE month = '$tstamp' AND mailuser_id = ".$rec['mailuser_id'];
                    $tr = $app->dbmaster->queryOneRecord($sql);
                    $mail_traffic = $tr['traffic'] + $mailbox_traffic[$rec['email']];
                    if($tr['traffic_id'] > 0) {
                        $sql = "UPDATE mail_traffic SET traffic = $mail_traffic WHERE traffic_id = ".$tr['traffic_id'];
                    } else {
                        $sql = "INSERT INTO mail_traffic (month,mailuser_id,traffic) VALUES ('$tstamp',".$rec['mailuser_id'].",$mail_traffic)";
                    }
                    $app->dbmaster->query($sql);
                    //echo $sql;
                }
            }
            unset($mailbox_traffic);
            if($last_line) file_put_contents($state_file, $last_line);
        }
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_clamav_log.inc.php
New file
@@ -0,0 +1,179 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_clamav_log extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_clamav';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        // Todo: the state should be calculated.
        $state = 'ok';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        /** The type of the data */
        $type = 'log_freshclam';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        /* Get the data from the LAST log-Entry.
         * if there can be found:
         * WARNING: Your ClamAV installation is OUTDATED!
         * then the clamav is outdated. This is a warning!
         */
        $state = 'ok';
        $tmp = explode("\n", $data);
        $lastLog = array();
        if ($tmp[sizeof($tmp) - 1] == '') {
            /* the log ends with an empty line remove this */
            array_pop($tmp);
        }
        if (strpos($tmp[sizeof($tmp) - 1], '-------------') !== false) {
            /* the log ends with "-----..." remove this */
            array_pop($tmp);
        }
        for ($i = sizeof($tmp) - 1; $i > 0; $i--) {
            if (strpos($tmp[$i], '---------') === false) {
                /* no delimiter found, so add this to the last-log */
                $lastLog[] = $tmp[$i];
            } else {
                /* delimiter found, so there is no more line left! */
                break;
            }
        }
        /*
         * Now we have the last log in the array.
         * Check if the outdated-string is found...
         */
        foreach ($lastLog as $line) {
            if (strpos(strtolower($line), 'outdated') !== false) {
                /*
                 * Outdatet is only info, because if we set this to warning, the server is
                 * as long in state warning, as there is a new version of ClamAv which takes
                 * sometimes weeks!
                 */
                $state = $this->_tools->_setState($state, 'info');
            }
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_cpu.inc.php
New file
@@ -0,0 +1,136 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_cpu extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'cpu_info';
        /*
            Fetch the data into a array
         */
        if (file_exists('/proc/cpuinfo')) {
            $cpuData = shell_exec('cat /proc/cpuinfo');
            $cpuInfo = explode("\n", $cpuData);
            $processor = 0;
            foreach ($cpuInfo as $line) {
                $part = preg_split('/:/', $line);
                $key = trim($part[0]);
                $value = trim($part[1]);
                if ($key == 'processor')
                    $processor = intval($value);
                if ($key != '')
                    $data[$key . ' ' . $processor] = $value;
            }
            /* the cpu has no state. It is, what it is */
            $state = 'no_state';
        } else {
            /*
             * It is not Linux, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data['output'] = '';
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_disk_usage.inc.php
New file
@@ -0,0 +1,166 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_disk_usage extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'disk_usage';
        /** The state of the disk-usage */
        $state = 'ok';
        /** Fetch the data of ALL devices into a array (needed for monitoring!) */
        //$dfData = shell_exec('df -hT 2>/dev/null');
        $app->uses('getconf');
        $web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
        $dfData = shell_exec('df -hT|grep -v "'.$web_config['website_basedir'].'/" 2>/dev/null');
        // split into array
        $df = explode("\n", $dfData);
        /*
         * ignore the first line, process the rest
         */
        for ($i = 1; $i <= sizeof($df); $i++) {
            if ($df[$i] != '') {
                /*
                 * Make an array of the data
                 */
                $s = preg_split('/[\s]+/', $df[$i]);
                $data[$i]['fs'] = $s[0];
                $data[$i]['type'] = $s[1];
                $data[$i]['size'] = $s[2];
                $data[$i]['used'] = $s[3];
                $data[$i]['available'] = $s[4];
                $data[$i]['percent'] = $s[5];
                $data[$i]['mounted'] = $s[6];
                /*
                 * calculate the state
                 */
                $usePercent = floatval($data[$i]['percent']);
                //* get the free memsize
                if(substr($data[$i]['available'],-1) == 'G') {
                    $freesize = floatval($data[$i]['available'])*1024;
                } elseif(substr($data[$i]['available'],-1) == 'T') {
                    $freesize = floatval($data[$i]['available'])*1024*1024;
                } else {
                    $freesize = floatval($data[$i]['available']);
                }
                //* We don't want to check some filesystem which have no sensible filling levels
                switch ($data[$i]['type']) {
                    case 'iso9660':
                    case 'cramfs':
                    case 'udf':
                    case 'tmpfs':
                    case 'devtmpfs':
                    case 'udev':
                        break;
                    default:
                        if ($usePercent > 75 && $freesize < 2000)
                            $state = $this->_tools->_setState($state, 'info');
                        if ($usePercent > 80 && $freesize < 1000)
                            $state = $this->_tools->_setState($state, 'warning');
                        if ($usePercent > 90 && $freesize < 500)
                            $state = $this->_tools->_setState($state, 'critical');
                        if ($usePercent > 95 && $freesize < 100)
                            $state = $this->_tools->_setState($state, 'error');
                        break;
                }
            }
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_email_quota.inc.php
New file
@@ -0,0 +1,149 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_email_quota extends cronjob {
    // job schedule
    protected $_schedule = '*/15 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        $app->uses('getconf');
        $mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail');
        if($mail_config['mailbox_quota_stats'] == 'n') return;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        //* Initialize data array
        $data = array();
        //* the id of the server as int
        $server_id = intval($conf['server_id']);
        //* The type of the data
        $type = 'email_quota';
        //* The state of the email_quota.
        $state = 'ok';
        $mailboxes = $app->db->queryAllRecords("SELECT email,maildir FROM mail_user WHERE server_id = $server_id");
        if(is_array($mailboxes)) {
            foreach($mailboxes as $mb) {
                $email = $mb['email'];
                $email_parts = explode('@',$mb['email']);
                $filename = $mb['maildir'].'/.quotausage';
                if(file_exists($filename) && !is_link($filename)) {
                    $quotafile = file($filename);
                    $data[$email]['used'] = trim($quotafile['1']);
                    unset($quotafile);
                } else {
                    exec('du -s '.escapeshellcmd($mb['maildir']),$out);
                    $parts = explode(' ',$out[0]);
                    $data[$email]['used'] = intval($parts[0])*1024;
                    unset($out);
                    unset($parts);
                }
            }
        }
        unset($mailboxes);
                //* Dovecot quota check Courier in progress lathama@gmail.com
        /*
                if($dir = opendir("/var/vmail")){
                        while (($quotafiles = readdir($dir)) !== false){
                                if(preg_match('/.\_quota$/', $quotafiles)){
                                        $quotafile = (file("/var/vmail/" . $quotafiles));
                                        $emailaddress = preg_replace('/_quota/',"", $quotafiles);
                                        $emailaddress = preg_replace('/_/',"@", $emailaddress);
                                        $data[$emailaddress]['used'] = trim($quotafile['1']);
                                }
                        }
                        closedir($dir);
                }
        */
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_fail2ban.inc.php
New file
@@ -0,0 +1,127 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_fail2ban extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_fail2ban';
        /* This monitoring is only available if fail2ban is installed */
        system('which fail2ban-client', $retval); // Debian, Ubuntu, Fedora
        if ($retval !== 0)
            system('which fail2ban', $retval); // CentOS
        if ($retval === 0) {
            /*  Get the data of the log */
            $data = $this->_tools->_getLogData($type);
            /*
             * At this moment, there is no state (maybe later)
             */
            $state = 'no_state';
        } else {
            /*
             * fail2ban is not installed, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data = '';
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_hd_quota.inc.php
New file
@@ -0,0 +1,162 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_hd_quota extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        //* Initialize data array
        $data = array();
        //* the id of the server as int
        $server_id = intval($conf['server_id']);
        //* The type of the data
        $type = 'harddisk_quota';
        //* The state of the harddisk_quota.
        $state = 'ok';
        //* Fetch the data for all users
        $dfData = shell_exec('repquota -au 2>/dev/null');
        //* Split into array
        $df = explode("\n", $dfData);
        //* ignore the first 5 lines, process the rest
        for ($i = 5; $i <= sizeof($df); $i++) {
            if ($df[$i] != '') {
                //* Make a array of the data
                $s = preg_split('/[\s]+/', $df[$i]);
                $username = $s[0];
                if (substr($username, 0, 3) == 'web') {
                    if (isset($data['user'][$username])) {
                        $data['user'][$username]['used'] += $s[2];
                        $data['user'][$username]['soft'] += $s[3];
                        $data['user'][$username]['hard'] += $s[4];
                        $data['user'][$username]['files'] += $s[5];
                    } else {
                        $data['user'][$username]['used'] = $s[2];
                        $data['user'][$username]['soft'] = $s[3];
                        $data['user'][$username]['hard'] = $s[4];
                        $data['user'][$username]['files'] = $s[5];
                    }
                }
            }
        }
        //** Fetch the data for all users
        $dfData = shell_exec('repquota -ag 2>/dev/null');
        //* split into array
        $df = explode("\n", $dfData);
        //* ignore the first 5 lines, process the rest
        for ($i = 5; $i <= sizeof($df); $i++) {
            if ($df[$i] != '') {
                //* Make a array of the data
                $s = preg_split('/[\s]+/', $df[$i]);
                $groupname = $s[0];
                if (substr($groupname, 0, 6) == 'client') {
                    if (isset($data['group'][$groupname])) {
                        $data['group'][$groupname]['used'] += $s[1];
                        $data['group'][$groupname]['soft'] += $s[2];
                        $data['group'][$groupname]['hard'] += $s[3];
                    } else {
                        $data['group'][$groupname]['used'] = $s[1];
                        $data['group'][$groupname]['soft'] = $s[2];
                        $data['group'][$groupname]['hard'] = $s[3];
                    }
                }
            }
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_iptables.inc.php
New file
@@ -0,0 +1,132 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_iptables extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'iptables_rules';
        /* This monitoring is only available if fail2ban is installed */
        system('which iptables', $retval); // Debian, Ubuntu, Fedora
        if ($retval === 0) {
                /*  Get the data of the log */
                $data['output'] = '<h2>iptables -S (ipv4)</h2>'.shell_exec('iptables -S 2>/dev/null');
                /*
                 * At this moment, there is no state (maybe later)
                 */
                $state = 'no_state';
        } else {
                $state = 'no_state';
                $data = '';
        }
        /* This monitoring is only available if fail2ban is installed */
        system('which ip6tables', $retval); // Debian, Ubuntu, Fedora
        if ($retval === 0) {
                /*  Get the data of the log */
                $data['output'] .= '<br><h2>ip6tables -S (ipv6)</h2>'.shell_exec('ip6tables -S 2>/dev/null');
                /*
                 * At this moment, there is no state (maybe later)
                 */
                $state = 'no_state';
        } else {
                $state = 'no_state';
                $data = '';
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_ispconfig_log.inc.php
New file
@@ -0,0 +1,146 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_ispconfig_log extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_ispconfig';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        // Todo: the state should be calculated.
        $state = 'ok';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        /** The type of the data */
        $type = 'log_ispc_cron';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        //* Ensure that output is encoded so that it does not break the serialize
        if(is_array($res) && isset($res['data'])) $res['data'] = htmlentities($res['data']);
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_ispconfig_version.inc.php
New file
@@ -0,0 +1,110 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_ispconfig_version extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'ispc_info';
        /*
            Fetch the data into a array
         */
        $data['name'] = ISPC_APP_TITLE;
        $data['version'] = ISPC_APP_VERSION;
        /* the ISPC-Version has no state. It is, what it is */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_mail_log.inc.php
New file
@@ -0,0 +1,181 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_mail_log extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_mail';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        /** The type of the data */
        $type = 'log_mail_warn';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        /** The type of the data */
        $type = 'log_mail_err';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_mail_queue.inc.php
New file
@@ -0,0 +1,138 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_mail_queue extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    private function _getIntArray($line) {
        /** The array of float found */
        $res = array();
        /* First build a array from the line */
        $data = explode(' ', $line);
        /* then check if any item is a float */
        foreach ($data as $item) {
            if ($item . '' == (int) $item . '') {
                $res[] = $item;
            }
        }
        return $res;
    }
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'mailq';
        /* Get the data from the mailq */
        $data['output'] = shell_exec('mailq');
        /*
         *  The last line has more informations
         */
        $tmp = explode("\n", $data['output']);
        $more = $tmp[sizeof($tmp) - 1];
        $res = $this->_getIntArray($more);
        $data['bytes'] = $res[0];
        $data['requests'] = $res[1];
        /** The state of the mailq. */
        $state = 'ok';
        if ($data['requests'] > 2000)
            $state = 'info';
        if ($data['requests'] > 5000)
            $state = 'warning';
        if ($data['requests'] > 8000)
            $state = 'critical';
        if ($data['requests'] > 10000)
            $state = 'error';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_mem_usage.inc.php
New file
@@ -0,0 +1,124 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_mem_usage extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'mem_usage';
        /*
            Fetch the data into a array
         */
        $miData = shell_exec('cat /proc/meminfo');
        $memInfo = explode("\n", $miData);
        foreach ($memInfo as $line) {
            $part = preg_split('/:/', $line);
            $key = trim($part[0]);
            $tmp = explode(' ', trim($part[1]));
            $value = 0;
            if ($tmp[1] == 'kB')
                $value = $tmp[0] * 1024;
            $data[$key] = $value;
        }
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_mongodb.inc.php
New file
@@ -0,0 +1,127 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_mongodb extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_mongodb';
        /* This monitoring is only available if MongoDB is installed */
        system('which mongod', $retval); // Debian, Ubuntu, Fedora
        if ($retval !== 0)
            system('which mongod', $retval); // CentOS
        if ($retval === 0) {
            /*  Get the data of the log */
            $data = $this->_tools->_getLogData($type);
            /*
             * At this moment, there is no state (maybe later)
             */
            $state = 'no_state';
        } else {
            /*
             * MongoDB is not installed, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data = '';
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_openvz.inc.php
New file
@@ -0,0 +1,183 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_openvz extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'openvz_veinfo';
        /*
            Fetch the data into a array
         */
        $app->load(openvz_tools);
        $openVzTools = new openvz_tools();
        $data = $openVzTools->getOpenVzVeInfo();
        /* the VE-Info has no state. It is, what it is */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        /** The type of the data */
        $type = 'openvz_beancounter';
        /*
            Fetch the data into a array
         */
        $app->load(openvz_tools);
        $openVzTools = new openvz_tools();
        $data = $openVzTools->getOpenVzVeBeanCounter();
        /* calculate the state of the beancounter */
        if ($data == '') {
            $state = 'no_state';
        } else {
            $state = 'ok';
            /* transfer this output-string into a array */
            $test = explode("\n", $data);
            /* the first list of the output is not needed */
            array_shift($test);
            /* now process all items of the rest */
            foreach ($test as $item) {
                /*
                 * eliminate all doubled spaces and spaces at the beginning and end
                 */
                while (strpos($item, '  ') !== false) {
                    $item = str_replace('  ', ' ', $item);
                }
                $item = trim($item);
                /*
                 * The failcounter is the LAST
                 */
                if ($item != '') {
                    $tmp = explode(' ', $item);
                    $failCounter = $tmp[sizeof($tmp) - 1];
                    if ($failCounter > 0)
                        $state = 'info';
                    if ($failCounter > 50)
                        $state = 'warning';
                    if ($failCounter > 200)
                        $state = 'critical';
                    if ($failCounter > 10000)
                        $state = 'error';
                }
            }
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_os_version.inc.php
New file
@@ -0,0 +1,112 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_os_version extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = $app->functions->intval($conf['server_id']);
        /** The type of the data */
        $type = 'os_info';
        /*
            Fetch the data into a array
         */
        $dist = $this->_tools->get_distname();
        $data['name'] = $dist['name'];
        $data['version'] = $dist['version'];
        /* the OS has no state. It is, what it is */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_raid.inc.php
New file
@@ -0,0 +1,263 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_raid extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'raid_state';
        /*
         * We support several RAID types, but if we can't find any of them, we have no data
         */
        $state = 'no_state';
        $data['output'] = '';
        /*
         * Check, if Software-RAID is enabled
         */
        if (file_exists('/proc/mdstat')) {
            /*
             * Fetch the output
             */
            $data['output'] = shell_exec('cat /proc/mdstat');
            /*
             * Then calc the state.
             */
            $tmp = explode("\n", $data['output']);
            $state = 'ok';
            for ($i = 0; $i < sizeof($tmp); $i++) {
                /* fetch the next line */
                $line = $tmp[$i];
                if ((strpos($line, '[U_]') !== false) || (strpos($line, '[_U]') !== false)) {
                    /* One Disk is not working.
                     * if the next line starts with "[>" or "[=" then
                     * recovery (resync) is in state and the state is
                     * information instead of critical
                     */
                    $nextLine = $tmp[$i + 1];
                    if ((strpos($nextLine, '[>') === false) && (strpos($nextLine, '[=') === false)) {
                        $state = $this->_tools->_setState($state, 'critical');
                    } else {
                        $state = $this->_tools->_setState($state, 'info');
                    }
                }
                if (strpos($line, '[__]') !== false) {
                    /* both Disk are not working */
                    $state = $this->_tools->_setState($state, 'error');
                }
                if (strpos($line, '[UU]') !== false) {
                    /* The disks are OK.
                     * if the next line starts with "[>" or "[=" then
                     * recovery (resync) is in state and the state is
                     * information instead of ok
                     */
                    $nextLine = $tmp[$i + 1];
                    if ((strpos($nextLine, '[>') === false) && (strpos($nextLine, '[=') === false)) {
                        $state = $this->_tools->_setState($state, 'ok');
                    } else {
                        $state = $this->_tools->_setState($state, 'info');
                    }
                }
            }
        }
        /*
         * Check, if we have mpt-status installed (LSIsoftware-raid)
         */
        if (file_exists('/proc/mpt/summary')) {
            system('which mpt-status', $retval);
            if ($retval === 0) {
                /*
                 * Fetch the output
                 */
                $data['output'] = shell_exec('mpt-status --autoload');
                /*
                 * Then calc the state.
                 */
                $state = 'ok';
                if(is_array($data['output'])) {
                    foreach ($data['output'] as $item) {
                        /*
                        * The output contains information for every RAID and every HDD.
                        * We only need the state of the RAID
                        */
                        if (strpos($item, 'state ') !== false) {
                            /*
                            * We found a raid, process the state of it
                            */
                            if (strpos($item, ' ONLINE ') !== false) {
                                $this->_tools->_setState($state, 'ok');
                            } elseif (strpos($item, ' OPTIMAL ') !== false) {
                                $this->_tools->_setState($state, 'ok');
                            } elseif (strpos($item, ' INITIAL ') !== false) {
                                $this->_tools->_setState($state, 'info');
                            } elseif (strpos($item, ' INACTIVE ') !== false) {
                                $this->_tools->_setState($state, 'critical');
                            } elseif (strpos($item, ' RESYNC ') !== false) {
                                $this->_tools->_setState($state, 'info');
                            } elseif (strpos($item, ' DEGRADED ') !== false) {
                                $this->_tools->_setState($state, 'critical');
                            } else {
                                /* we don't know the state. so we set the state to critical, that the
                                * admin is warned, that something is wrong
                                */
                                $this->_tools->_setState($state, 'critical');
                            }
                        }
                    }
                }
            }
        }
        /*
        * 3ware Controller
        */
        system('which tw_cli', $retval);
        if($retval === 0) {
            $data['output'] = shell_exec('tw_cli info c0');
            $state = 'ok';
            if(is_array($data['output'])) {
            foreach ($data['output'] as $item) {
                if (strpos($item, 'RAID') !== false) {
                    if (strpos($item, ' VERIFYING ') !== false) {
                        $this->_tools->_setState($state, 'info');
                    }
                    else if (strpos($item, ' MIGRATE-PAUSED ') !== false) {
                        $this->_tools->_setState($state, 'info');
                    }
                    else if (strpos($item, ' MIGRATING ') !== false) {
                        $this->_tools->_setState($state, 'ok');
                    }
                    else if (strpos($item, ' INITIALIZING ') !== false) {
                        $this->_tools->_setState($state, 'info');
                    }
                    else if (strpos($item, ' INIT-PAUSED ') !== false) {
                        $this->_tools->_setState($state, 'info');
                    }
                    else if (strpos($item, ' REBUILDING ') !== false) {
                        $this->_tools->_setState($state, 'info');
                    }
                    else if (strpos($item, ' REBUILD-PAUSED ') !== false) {
                        $this->_tools->_setState($state, 'warning');
                    }
                    else if (strpos($item, ' RECOVERY ') !== false) {
                        $this->_tools->_setState($state, 'warning');
                    }
                    else if (strpos($item, ' DEGRADED ') !== false) {
                        $this->_tools->_setState($state, 'critical');
                    }
                    else if (strpos($item, ' UNKNOWN ') !== false) {
                        $this->_tools->_setState($state, 'critical');
                    }
                    else if (strpos($item, ' OK ') !== false) {
                        $this->_tools->_setState($state, 'ok');
                    }
                    else if (strpos($item, ' OPTIMAL ') !== false) {
                        $this->_tools->_setState($state, 'ok');
                    }
                    else {
                        $this->_tools->_setState($state, 'critical');
                    }
                }
            }
            }
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_rkhunter.inc.php
New file
@@ -0,0 +1,127 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_rkhunter extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'rkhunter';
        /* This monitoring is only available if rkhunter is installed */
        system('which rkhunter', $retval);
        if ($retval === 0) {
            /*
             * Fetch the output
             */
            $data['output'] = shell_exec('rkhunter --update --checkall --nocolors --skip-keypress');
            /*
             * At this moment, there is no state (maybe later)
             */
            $state = 'no_state';
        } else {
            /*
             * rkhunter is not installed, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data['output'] = '';
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_server.inc.php
New file
@@ -0,0 +1,133 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_server extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'server_load';
        /*
            Fetch the data into a array
         */
        $procUptime = shell_exec("cat /proc/uptime | cut -f1 -d' '");
        $data['up_days'] = floor($procUptime / 86400);
        $data['up_hours'] = floor(($procUptime - $data['up_days'] * 86400) / 3600);
        $data['up_minutes'] = floor(($procUptime - $data['up_days'] * 86400 - $data['up_hours'] * 3600) / 60);
        $data['uptime'] = shell_exec('uptime');
        $tmp = explode(',', $data['uptime'], 4);
        $tmpUser = explode(' ', trim($tmp[2]));
        $data['user_online'] = intval($tmpUser[0]);
        //* New Load Average code to fix "always zero" bug in non-english distros. NEEDS TESTING
        $loadTmp = shell_exec("cat /proc/loadavg | cut -f1-3 -d' '");
        $load = explode(' ', $loadTmp);
        $data['load_1'] = floatval(str_replace(',', '.', $load[0]));
        $data['load_5'] = floatval(str_replace(',', '.', $load[1]));
        $data['load_15'] = floatval(str_replace(',', '.', $load[2]));
        /** The state of the server-load. */
        $state = 'ok';
        if ($data['load_1'] > 20)
            $state = 'info';
        if ($data['load_1'] > 50)
            $state = 'warning';
        if ($data['load_1'] > 100)
            $state = 'critical';
        if ($data['load_1'] > 150)
            $state = 'error';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_services.inc.php
New file
@@ -0,0 +1,94 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_services extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /*
         * First we get the Monitoring-data from the tools
         */
        $res = $this->_tools->monitorServices();
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_syslog.inc.php
New file
@@ -0,0 +1,153 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_syslog extends cronjob {
    // job schedule
    protected $_schedule = '*/5 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'sys_log';
        /*
         * is there any warning or error for this server?
         */
        $state = 'ok';
        $dbData = $app->dbmaster->queryAllRecords('SELECT loglevel FROM sys_log WHERE server_id = ' . $server_id . ' AND loglevel > 0');
        if (is_array($dbData)) {
            foreach ($dbData as $item) {
                if ($item['loglevel'] == 1)
                    $state = $this->_tools->_setState($state, 'warning');
                if ($item['loglevel'] == 2)
                    $state = $this->_tools->_setState($state, 'error');
            }
        }
        /** There is no monitor-data because the data is in the sys_log table */
        $data['output'] = '';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        /** The type of the data */
        $type = 'log_messages';
        /* Get the data of the log */
        $data = $this->_tools->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/100-monitor_system_update.inc.php
New file
@@ -0,0 +1,208 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_monitor_system_update extends cronjob {
    // job schedule
    protected $_schedule = '0 * * * *';
    protected $_run_at_new = true;
    private $_tools = null;
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        /* used for all monitor cronjobs */
        $app->load('monitor_tools');
        $this->_tools = new monitor_tools();
        /* end global section for monitor cronjobs */
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'system_update';
        /* This monitoring is only available on Debian or Ubuntu */
        if (file_exists('/etc/debian_version')) {
            /*
             * first update the "apt database"
             */
            shell_exec('apt-get update');
            /*
             * Then test the upgrade.
             * if there is any output, then there is a needed update
             */
            $aptData = shell_exec('apt-get -s -qq dist-upgrade');
            if ($aptData == '') {
                /* There is nothing to update! */
                $state = 'ok';
            } else {
                /*
                 * There is something to update! this is in most cases not critical, so we can
                 * do a system-update once a month or so...
                 */
                $state = 'info';
            }
            /*
             * Fetch the output
             */
            $data['output'] = $aptData;
        } elseif (file_exists('/etc/gentoo-release')) {
            /*
             * first update the portage tree
             */
            // In keeping with gentoo's rsync policy, don't update to frequently (every four hours - taken from http://www.gentoo.org/doc/en/source_mirrors.xml)
            $do_update = true;
            if (file_exists('/usr/portage/metadata/timestamp.chk')) {
                $datetime = file_get_contents('/usr/portage/metadata/timestamp.chk');
                $datetime = trim($datetime);
                $dstamp = strtotime($datetime);
                if ($dstamp) {
                    $checkat = $dstamp + 14400; // + 4hours
                    if (mktime() < $checkat) {
                        $do_update = false;
                    }
                }
            }
            if ($do_update) {
                shell_exec('emerge --sync --quiet');
            }
            /*
             * Then test the upgrade.
             * if there is any output, then there is a needed update
             */
            $emergeData = shell_exec('glsa-check -t affected');
            if ($emergeData == '') {
                /* There is nothing to update! */
                $state = 'ok';
                $data['output'] = 'No unapplied GLSA\'s found on the system.';
            } else {
                /* There is something to update! */
                $state = 'info';
                $data['output'] = shell_exec('glsa-check -pv --nocolor affected 2>/dev/null');
            }
        } elseif (file_exists('/etc/SuSE-release')) {
            /*
             * update and find the upgrade.
             * if there is any output, then there is a needed update
             */
            $aptData = shell_exec('zypper -q lu');
            if ($aptData == '') {
                /* There is nothing to update! */
                $state = 'ok';
            } else {
                /*
                 * There is something to update! this is in most cases not critical, so we can
                 * do a system-update once a month or so...
                 */
                $state = 'info';
            }
            /*
             * Fetch the output
             */
            $data['output'] = shell_exec('zypper lu');
        } else {
            /*
             * It is not Debian/Ubuntu, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data['output'] = '';
        }
        $res = array();
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        //* Ensure that output is encoded so that it does not break the serialize
        //$res['data']['output'] = htmlentities($res['data']['output']);
        $res['data']['output'] = htmlentities($res['data']['output'],ENT_QUOTES,'UTF-8');
        /*
         * Insert the data into the database
         */
        $sql = 'REPLACE INTO monitor_data (server_id, type, created, data, state) ' .
                'VALUES (' .
                $res['server_id'] . ', ' .
                "'" . $app->dbmaster->quote($res['type']) . "', " .
                'UNIX_TIMESTAMP(), ' .
                "'" . $app->dbmaster->quote(serialize($res['data'])) . "', " .
                "'" . $res['state'] . "'" .
                ')';
        $app->dbmaster->query($sql);
        /* The new data is written, now we can delete the old one */
        $this->_tools->delOldRecords($res['type'], $res['server_id']);
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/150-awstats.inc.php
New file
@@ -0,0 +1,183 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_awstats extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        #######################################################################################################
        // Create awstats statistics
        #######################################################################################################
        $sql = "SELECT domain_id, domain, document_root, web_folder, type, system_user, system_group, parent_domain_id FROM web_domain WHERE (type = 'vhost' or type = 'vhostsubdomain') and stats_type = 'awstats' AND server_id = ".$conf['server_id'];
        $records = $app->db->queryAllRecords($sql);
        $web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
        foreach($records as $rec) {
            //$yesterday = date('Ymd',time() - 86400);
            $yesterday = date('Ymd',strtotime("-1 day", time()));
            $log_folder = 'log';
            if($rec['type'] == 'vhostsubdomain') {
                $tmp = $app->db->queryOneRecord('SELECT `domain` FROM web_domain WHERE domain_id = '.intval($rec['parent_domain_id']));
                $subdomain_host = preg_replace('/^(.*)\.' . preg_quote($tmp['domain'], '/') . '$/', '$1', $rec['domain']);
                if($subdomain_host == '') $subdomain_host = 'web'.$rec['domain_id'];
                $log_folder .= '/' . $subdomain_host;
                unset($tmp);
            }
            $logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/'.$yesterday.'-access.log');
            if(!@is_file($logfile)) {
                $logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/'.$yesterday.'-access.log.gz');
                if(!@is_file($logfile)) {
                    continue;
                }
            }
            $web_folder = ($rec['type'] == 'vhostsubdomain' ? $rec['web_folder'] : 'web');
            $domain = escapeshellcmd($rec['domain']);
            $statsdir = escapeshellcmd($rec['document_root'].'/'.$web_folder.'/stats');
            $awstats_pl = $web_config['awstats_pl'];
            $awstats_buildstaticpages_pl = $web_config['awstats_buildstaticpages_pl'];
            $awstats_conf_dir = $web_config['awstats_conf_dir'];
            $awstats_website_conf_file = $web_config['awstats_conf_dir'].'/awstats.'.$domain.'.conf';
            if(is_file($awstats_website_conf_file)) unlink($awstats_website_conf_file);
            $sql = "SELECT domain FROM web_domain WHERE (type = 'alias' OR type = 'subdomain') AND parent_domain_id = ".$rec['domain_id'];
            $aliases = $app->db->queryAllRecords($sql);
            $aliasdomain = '';
            if(is_array($aliases)) {
                foreach ($aliases as $alias) {
                    $aliasdomain.= ' '.$alias['domain']. ' www.'.$alias['domain'];
                }
            }
            if(!is_file($awstats_website_conf_file)) {
                $awstats_conf_file_content = 'Include "'.$awstats_conf_dir.'/awstats.conf"
        LogFile="/var/log/ispconfig/httpd/'.$domain.'/yesterday-access.log"
        SiteDomain="'.$domain.'"
        HostAliases="www.'.$domain.' localhost 127.0.0.1'.$aliasdomain.'"';
                file_put_contents($awstats_website_conf_file,$awstats_conf_file_content);
            }
            if(!@is_dir($statsdir)) mkdir($statsdir);
            if(is_link('/var/log/ispconfig/httpd/'.$domain.'/yesterday-access.log')) unlink('/var/log/ispconfig/httpd/'.$domain.'/yesterday-access.log');
            symlink($logfile,'/var/log/ispconfig/httpd/'.$domain.'/yesterday-access.log');
            $awmonth = date("n");
            $awyear = date("Y");
            if (date("d") == 1) {
                $awmonth = date("m")-1;
                if (date("m") == 1) {
                    $awyear = date("Y")-1;
                    $awmonth = "12";
                }
            }
            // awstats_buildstaticpages.pl -update -config=mydomain.com -lang=en -dir=/var/www/domain.com/'.$web_folder.'/stats -awstatsprog=/path/to/awstats.pl
            // $command = "$awstats_buildstaticpages_pl -update -config='$domain' -lang=".$conf['language']." -dir='$statsdir' -awstatsprog='$awstats_pl'";
            $command = "$awstats_buildstaticpages_pl -month='$awmonth' -year='$awyear' -update -config='$domain' -lang=".$conf['language']." -dir='$statsdir' -awstatsprog='$awstats_pl'";
            if (date("d") == 2) {
                $awmonth = date("m")-1;
                if (date("m") == 1) {
                    $awyear = date("Y")-1;
                    $awmonth = "12";
                }
                $statsdirold = $statsdir."/".$awyear."-".$awmonth."/";
                mkdir($statsdirold);
                $files = scandir($statsdir);
                foreach ($files as $file) {
                    if (substr($file,0,1) != "." && !is_dir("$statsdir"."/"."$file") && substr($file,0,1) != "w" && substr($file,0,1) != "i") copy("$statsdir"."/"."$file","$statsdirold"."$file");
                }
            }
            if($awstats_pl != '' && $awstats_buildstaticpages_pl != '' && fileowner($awstats_pl) == 0 && fileowner($awstats_buildstaticpages_pl) == 0) {
                exec($command);
                if(is_file($rec['document_root'].'/'.$web_folder.'/stats/index.html')) unlink($rec['document_root'].'/'.$web_folder.'/stats/index.html');
                rename($rec['document_root'].'/'.$web_folder.'/stats/awstats.'.$domain.'.html',$rec['document_root'].'/'.$web_folder.'/stats/awsindex.html');
                if(!is_file($rec['document_root']."/".$web_folder."/stats/index.php")) {
                    if(file_exists("/usr/local/ispconfig/server/conf-custom/awstats_index.php.master")) {
                        copy("/usr/local/ispconfig/server/conf-custom/awstats_index.php.master",$rec['document_root']."/".$web_folder."/stats/index.php");
                    } else {
                        copy("/usr/local/ispconfig/server/conf/awstats_index.php.master",$rec['document_root']."/".$web_folder."/stats/index.php");
                    }
                }
                $app->log('Created awstats statistics with command: '.$command,LOGLEVEL_DEBUG);
            } else {
                $app->log("No awstats statistics created. Either $awstats_pl or $awstats_buildstaticpages_pl is not owned by root user.",LOGLEVEL_WARN);
            }
            if(is_file($rec['document_root']."/".$web_folder."/stats/index.php")) {
                chown($rec['document_root']."/".$web_folder."/stats/index.php",$rec['system_user']);
                chgrp($rec['document_root']."/".$web_folder."/stats/index.php",$rec['system_group']);
            }
        }
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/150-webalizer.inc.php
New file
@@ -0,0 +1,142 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_webalizer extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        #######################################################################################################
        // Create webalizer statistics
        #######################################################################################################
        function setConfigVar( $filename, $varName, $varValue, $append = 0 ) {
            if($lines = @file($filename)) {
                $out = '';
                $found = 0;
                foreach($lines as $line) {
                    @list($key, $value) = preg_split('/[\t= ]+/', $line, 2);
                    if($key == $varName) {
                        $out .= $varName.' '.$varValue."\n";
                        $found = 1;
                    } else {
                        $out .= $line;
                    }
                }
                if($found == 0) {
                    //* add \n if the last line does not end with \n or \r
                    if(substr($out,-1) != "\n" && substr($out,-1) != "\r") $out .= "\n";
                    //* add the new line at the end of the file
                    if($append == 1) $out .= $varName.' '.$varValue."\n";
                }
                file_put_contents($filename,$out);
            }
        }
        $sql = "SELECT domain_id, domain, document_root, web_folder, type, parent_domain_id FROM web_domain WHERE (type = 'vhost' or type = 'vhostsubdomain') and stats_type = 'webalizer' AND server_id = ".$conf['server_id'];
        $records = $app->db->queryAllRecords($sql);
        foreach($records as $rec) {
            //$yesterday = date('Ymd',time() - 86400);
            $yesterday = date('Ymd',strtotime("-1 day", time()));
            $log_folder = 'log';
            if($rec['type'] == 'vhostsubdomain') {
                $tmp = $app->db->queryOneRecord('SELECT `domain` FROM web_domain WHERE domain_id = '.intval($rec['parent_domain_id']));
                $subdomain_host = preg_replace('/^(.*)\.' . preg_quote($tmp['domain'], '/') . '$/', '$1', $rec['domain']);
                if($subdomain_host == '') $subdomain_host = 'web'.$rec['domain_id'];
                $log_folder .= '/' . $subdomain_host;
                unset($tmp);
            }
            $logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/'.$yesterday.'-access.log');
            if(!@is_file($logfile)) {
                $logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/'.$yesterday.'-access.log.gz');
                if(!@is_file($logfile)) {
                    continue;
                }
            }
            $domain = escapeshellcmd($rec['domain']);
            $statsdir = escapeshellcmd($rec['document_root'].'/'.($rec['type'] == 'vhostsubdomain' ? $rec['web_folder'] : 'web').'/stats');
            $webalizer = '/usr/bin/webalizer';
            $webalizer_conf_main = '/etc/webalizer/webalizer.conf';
            $webalizer_conf = escapeshellcmd($rec['document_root'].'/log/webalizer.conf');
            if(is_file($statsdir.'/index.php')) unlink($statsdir.'/index.php');
            if(!@is_file($webalizer_conf)) {
                copy($webalizer_conf_main,$webalizer_conf);
            }
            if(@is_file($webalizer_conf)) {
                setConfigVar($webalizer_conf, 'Incremental', 'yes');
                setConfigVar($webalizer_conf, 'IncrementalName', $statsdir.'/webalizer.current');
                setConfigVar($webalizer_conf, 'HistoryName', $statsdir.'/webalizer.hist');
            }
            if(!@is_dir($statsdir)) mkdir($statsdir);
            exec("$webalizer -c $webalizer_conf -n $domain -s $domain -r $domain -q -T -p -o $statsdir $logfile");
        }
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/200-logfiles.inc.php
New file
@@ -0,0 +1,271 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_logfiles extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        #######################################################################################################
        // Make the web logfiles directories world readable to enable ftp access
        #######################################################################################################
        if(is_dir('/var/log/ispconfig/httpd')) exec('chmod +r /var/log/ispconfig/httpd/*');
        #######################################################################################################
        // Manage and compress web logfiles and create traffic statistics
        #######################################################################################################
        $sql = "SELECT domain_id, domain, type, document_root, web_folder, parent_domain_id FROM web_domain WHERE (type = 'vhost' or type = 'vhostsubdomain') AND server_id = ".$conf['server_id'];
        $records = $app->db->queryAllRecords($sql);
        foreach($records as $rec) {
            //* create traffic statistics based on yesterdays access log file
            $yesterday = date('Ymd',time() - 86400);
            $log_folder = 'log';
            if($rec['type'] == 'vhostsubdomain') {
                $tmp = $app->db->queryOneRecord('SELECT `domain` FROM web_domain WHERE domain_id = '.intval($rec['parent_domain_id']));
                $subdomain_host = preg_replace('/^(.*)\.' . preg_quote($tmp['domain'], '/') . '$/', '$1', $rec['domain']);
                if($subdomain_host == '') $subdomain_host = 'web'.$rec['domain_id'];
                $log_folder .= '/' . $subdomain_host;
                unset($tmp);
            }
            $logfile = $rec['document_root'].'/' . $log_folder . '/'.$yesterday.'-access.log';
            $total_bytes = 0;
            $handle = @fopen($logfile, "r");
            if ($handle) {
                while (($line = fgets($handle, 4096)) !== false) {
                    if (preg_match('/^\S+ \S+ \S+ \[.*?\] "\S+.*?" \d+ (\d+) ".*?" ".*?"/', $line, $m)) {
                        $total_bytes += intval($m[1]);
                    }
                }
                //* Insert / update traffic in master database
                $traffic_date = date('Y-m-d',time() - 86400);
                $tmp = $app->dbmaster->queryOneRecord("select hostname from web_traffic where hostname='".$rec['domain']."' and traffic_date='".$traffic_date."'");
                if(is_array($tmp) && count($tmp) > 0) {
                    $sql = "update web_traffic set traffic_bytes=traffic_bytes+"
                          . $total_bytes
                          . " where hostname='" . $rec['domain']
                          . "' and traffic_date='" . $traffic_date . "'";
                } else {
                    $sql = "insert into web_traffic (hostname, traffic_date, traffic_bytes) values ('".$rec['domain']."', '".$traffic_date."', '".$total_bytes."')";
                }
                $app->dbmaster->query($sql);
                fclose($handle);
            }
            $yesterday2 = date('Ymd',time() - 86400*2);
            $logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/'.$yesterday2.'-access.log');
            //* Compress logfile
            if(@is_file($logfile)) {
                // Compress yesterdays logfile
                exec("gzip -c $logfile > $logfile.gz");
                unlink($logfile);
            }
            // rotate and compress the error.log when it exceeds a size of 10 MB
            $logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/error.log');
            if(is_file($logfile) && filesize($logfile) > 10000000) {
                exec("gzip -c $logfile > $logfile.1.gz");
                exec("cat /dev/null > $logfile");
            }
            // delete logfiles after 30 days
            $month_ago = date('Ymd',time() - 86400 * 30);
            $logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/'.$month_ago.'-access.log.gz');
            if(@is_file($logfile)) {
                unlink($logfile);
            }
            //* Delete older Log files, in case that we missed them before due to serverdowntimes.
            $datepart = date('Ym',time() - 86400 * 31 * 2);
            $logfile = escapeshellcmd($rec['document_root']).'/' . $log_folder . '/'.$datepart.'*-access.log.gz';
            exec('rm -f '.$logfile);
            $logfile = escapeshellcmd($rec['document_root']).'/' . $log_folder . '/'.$datepart.'*-access.log';
            exec('rm -f '.$logfile);
        }
        //* Delete old logfiles in /var/log/ispconfig/httpd/ that were created by vlogger for the hostname of the server
        exec('hostname -f', $tmp_hostname);
        if($tmp_hostname[0] != '' && is_dir('/var/log/ispconfig/httpd/'.$tmp_hostname[0])) {
            exec('cd /var/log/ispconfig/httpd/'.$tmp_hostname[0]."; find . -mtime +30 -name '*.log' | xargs rm > /dev/null 2> /dev/null");
        }
        unset($tmp_hostname);
        #######################################################################################################
        // Rotate the ispconfig.log file
        #######################################################################################################
        // rotate the ispconfig.log when it exceeds a size of 10 MB
        $logfile = $conf['ispconfig_log_dir'].'/ispconfig.log';
        if(is_file($logfile) && filesize($logfile) > 10000000) {
            exec("gzip -c $logfile > $logfile.1.gz");
            exec("cat /dev/null > $logfile");
        }
        // rotate the cron.log when it exceeds a size of 10 MB
        $logfile = $conf['ispconfig_log_dir'].'/cron.log';
        if(is_file($logfile) && filesize($logfile) > 10000000) {
            exec("gzip -c $logfile > $logfile.1.gz");
            exec("cat /dev/null > $logfile");
        }
        // rotate the auth.log when it exceeds a size of 10 MB
        $logfile = $conf['ispconfig_log_dir'].'/auth.log';
        if(is_file($logfile) && filesize($logfile) > 10000000) {
            exec("gzip -c $logfile > $logfile.1.gz");
            exec("cat /dev/null > $logfile");
        }
        #######################################################################################################
        // Cleanup website tmp directories
        #######################################################################################################
        $sql = "SELECT domain_id, domain, document_root, system_user FROM web_domain WHERE server_id = ".$conf['server_id'];
        $records = $app->db->queryAllRecords($sql);
        $app->uses('system');
        if(is_array($records)) {
            foreach($records as $rec){
                $tmp_path = realpath(escapeshellcmd($rec['document_root'].'/tmp'));
                if($tmp_path != '' && strlen($tmp_path) > 10 && is_dir($tmp_path) && $app->system->is_user($rec['system_user'])){
                    exec('cd '.$tmp_path."; find . -mtime +1 -name 'sess_*' | grep -v -w .no_delete | xargs rm > /dev/null 2> /dev/null");
                }
            }
        }
        #######################################################################################################
        // Cleanup logs in master database (only the "master-server")
        #######################################################################################################
        if ($app->dbmaster == $app->db) {
            /** 7 days */
            $tstamp = time() - (60*60*24*7);
            /*
             *  Keep 7 days in sys_log
             * (we can delete the old items, because if they are OK, they don't interrest anymore
             * if they are NOT ok, the server will try to process them in 1 minute and so the
             * error appears again after 1 minute. So it is no problem to delete the old one!
             */
            $sql = "DELETE FROM sys_log WHERE tstamp < " . $tstamp . " AND server_id != 0";
            $app->dbmaster->query($sql);
            /*
             * Delete all remote-actions "done" and older than 7 days
             * ATTENTION: We have the same problem as described in cleaning the datalog. We must not
             * delete the last entry
             */
            $sql = "SELECT max(action_id) FROM sys_remoteaction";
            $res = $app->dbmaster->queryOneRecord($sql);
            $maxId = $res['max(action_id)'];
            $sql =  "DELETE FROM sys_remoteaction " .
                    "WHERE tstamp < " . $tstamp . " " .
                    " AND action_state = 'ok' " .
                    " AND action_id <" . intval($maxId);
            $app->dbmaster->query($sql);
            /*
             * The sys_datalog is more difficult.
             * 1) We have to keet ALL entries with
             *    server_id=0, because they depend on ALL servers (even if they are not
             *    actually in the system (and will be insered in 3 days or so).
             * 2) We have to keey ALL entries which are not actually precessed by the
             *    server never mind how old they are!
             * 3) We have to keep the entry with the highest autoinc-id, because mysql calculates the
             *    autoinc-id as "new value = max(row) +1" and does not store this in a separate table.
             *    This means, if we delete to entry with the highest autoinc-value then this value is
             *    reused as autoinc and so there are more than one entries with the same value (over
             *    for example 4 Weeks). This is confusing for our system.
             *    ATTENTION 2) and 3) is in some case NOT the same! so we have to check both!
             */
            /* First we need all servers and the last sys_datalog-id they processed */
            $sql = "SELECT server_id, updated FROM server ORDER BY server_id";
            $records = $app->dbmaster->queryAllRecords($sql);
            /* Then we need the highest value ever */
            $sql = "SELECT max(datalog_id) FROM sys_datalog";
            $res = $app->dbmaster->queryOneRecord($sql);
            $maxId = $res['max(datalog_id)'];
            /* Then delete server by server */
            foreach($records as $server) {
                $tmp_server_id = intval($server['server_id']);
                if($tmp_server_id > 0) {
                    $sql =     "DELETE FROM sys_datalog " .
                            "WHERE tstamp < " . $tstamp .
                            " AND server_id = " . intval($server['server_id']) .
                            " AND datalog_id < " . intval($server['updated']) .
                            " AND datalog_id < " . intval($maxId);
                }
        //        echo $sql . "\n";
                $app->dbmaster->query($sql);
            }
        }
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/300-quota_notify.inc.php
New file
@@ -0,0 +1,479 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_quota_notify extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        #########
        // function for sending notification emails
        #########
        function send_notification_email($template, $placeholders, $recipients) {
            global $conf;
            if(!is_array($recipients) || count($recipients) < 1) return false;
            if(!is_array($placeholders)) $placeholders = array();
            if(file_exists($conf['rootpath'].'/conf-custom/mail/' . $template . '_'.$conf['language'].'.txt')) {
                $lines = file($conf['rootpath'].'/conf-custom/mail/' . $template . '_'.$conf['language'].'.txt');
            } elseif(file_exists($conf['rootpath'].'/conf-custom/mail/' . $template . '_en.txt')) {
                $lines = file($conf['rootpath'].'/conf-custom/mail/' . $template . '_en.txt');
            } elseif(file_exists($conf['rootpath'].'/conf/mail/' . $template . '_'.$conf['language'].'.txt')) {
                $lines = file($conf['rootpath'].'/conf/mail/' . $template . '_'.$conf['language'].'.txt');
            } else {
                $lines = file($conf['rootpath'].'/conf/mail/' . $template . '_en.txt');
            }
            //* get mail headers, subject and body
            $mailHeaders = '';
            $mailBody = '';
            $mailSubject = '';
            $inHeader = true;
            for($l = 0; $l < count($lines); $l++) {
                if($lines[$l] == '') {
                    $inHeader = false;
                    continue;
                }
                if($inHeader == true) {
                    $parts = explode(':', $lines[$l], 2);
                    if(strtolower($parts[0]) == 'subject') $mailSubject = trim($parts[1]);
                    unset($parts);
                    $mailHeaders .= trim($lines[$l]) . "\n";
                } else {
                    $mailBody .= trim($lines[$l]) . "\n";
                }
            }
            $mailBody = trim($mailBody);
            //* Replace placeholders
            $mailHeaders = strtr($mailHeaders, $placeholders);
            $mailSubject = strtr($mailSubject, $placeholders);
            $mailBody = strtr($mailBody, $placeholders);
            for($r = 0; $r < count($recipients); $r++) {
                mail($recipients[$r], $mailSubject, $mailBody, $mailHeaders);
            }
            unset($mailSubject);
            unset($mailHeaders);
            unset($mailBody);
            unset($lines);
            return true;
        }
        #######################################################################################################
        // enforce traffic quota (run only on the "master-server")
        #######################################################################################################
        if ($app->dbmaster == $app->db) {
            $global_config = $app->getconf->get_global_config('mail');
            $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 or traffic_quota_lock = 'y') and (type = 'vhost' OR type = 'vhostsubdomain')";
            $records = $app->db->queryAllRecords($sql);
            if(is_array($records)) {
                foreach($records as $rec) {
                    $web_traffic_quota = $rec['traffic_quota'];
                    $domain = $rec['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->queryOneRecord("SELECT SUM(traffic_bytes) As total_traffic_bytes FROM web_traffic WHERE traffic_date like '$current_month%' AND hostname = '$domain'");
                    $web_traffic = round($tmp['total_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->dbmaster->datalogUpdate('web_domain', "traffic_quota_lock = 'y',active = 'n'", 'domain_id', $rec['domain_id']);
                        $app->log('Traffic quota for '.$rec['domain'].' exceeded. Disabling website.',LOGLEVEL_DEBUG);
                        //* Send traffic notifications
                        if($rec['traffic_quota_lock'] != 'y' && ($web_config['overtraffic_notify_admin'] == 'y' || $web_config['overtraffic_notify_client'] == 'y')) {
                            $placeholders = array('{domain}' => $rec['domain'],
                                                  '{admin_mail}' => ($global_config['admin_mail'] != ''? $global_config['admin_mail'] : 'root'));
                            $recipients = array();
                            //* send email to admin
                            if($global_config['admin_mail'] != '' && $web_config['overtraffic_notify_admin'] == 'y') {
                                $recipients[] = $global_config['admin_mail'];
                            }
                            //* Send email to client
                            if($web_config['overtraffic_notify_client'] == 'y') {
                                $client_group_id = $rec["sys_groupid"];
                                $client = $app->db->queryOneRecord("SELECT client.email FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
                                if($client['email'] != '') {
                                    $recipients[] = $client['email'];
                                }
                            }
                            send_notification_email('web_traffic_notification', $placeholders, $recipients);
                        }
                    } else {
                        //* unlock the website, if traffic is lower then quota
                        if($rec['traffic_quota_lock'] == 'y') {
                            $app->dbmaster->datalogUpdate('web_domain', "traffic_quota_lock = 'n',active = 'y'", 'domain_id', $rec['domain_id']);
                            $app->log('Traffic quota for '.$rec['domain'].' ok again. Re-enabling website.',LOGLEVEL_DEBUG);
                        }
                    }
                }
            }
        }
        #######################################################################################################
        // send website quota warnings by email
        #######################################################################################################
        if ($app->dbmaster == $app->db) {
            $global_config = $app->getconf->get_global_config('mail');
            //* Check website disk quota
            $sql = "SELECT domain_id,sys_groupid,domain,system_user,last_quota_notification,DATEDIFF(CURDATE(), last_quota_notification) as `notified_before` FROM web_domain WHERE (type = 'vhost' OR type = 'vhostsubdomain')";
            $records = $app->db->queryAllRecords($sql);
            if(is_array($records) && !empty($records)) {
                $tmp_rec =  $app->db->queryAllRecords("SELECT data from monitor_data WHERE type = 'harddisk_quota' ORDER BY created DESC");
                $monitor_data = array();
                if(is_array($tmp_rec)) {
                    foreach ($tmp_rec as $tmp_mon) {
                        $monitor_data = array_merge_recursive($monitor_data,unserialize($app->db->unquote($tmp_mon['data'])));
                    }
                }
                foreach($records as $rec) {
                    //$web_hd_quota = $rec['hd_quota'];
                    $domain = $rec['domain'];
                    $username = $rec['system_user'];
                    $rec['used'] = $monitor_data['user'][$username]['used'];
                    $rec['soft'] = $monitor_data['user'][$username]['soft'];
                    $rec['hard'] = $monitor_data['user'][$username]['hard'];
                    $rec['files'] = $monitor_data['user'][$username]['files'];
                    if (!is_numeric($rec['used'])){
                        if ($rec['used'][0] > $rec['used'][1]){
                            $rec['used'] = $rec['used'][0];
                        } else {
                            $rec['used'] = $rec['used'][1];
                        }
                    }
                    if (!is_numeric($rec['soft'])) $rec['soft']=$rec['soft'][1];
                    if (!is_numeric($rec['hard'])) $rec['hard']=$rec['hard'][1];
                    if (!is_numeric($rec['files'])) $rec['files']=$rec['files'][1];
                    // used space ratio
                    if($rec['soft'] > 0){
                        $used_ratio = $rec['used']/$rec['soft'];
                    } else {
                        $used_ratio = 0;
                    }
                    $rec['ratio'] = number_format($used_ratio * 100, 2, '.', '').'%';
                    if($rec['used'] > 1024) {
                        $rec['used'] = round($rec['used'] / 1024,2).' MB';
                    } else {
                        if ($rec['used'] != '') $rec['used'] .= ' KB';
                    }
                    if($rec['soft'] > 1024) {
                        $rec['soft'] = round($rec['soft'] / 1024,2).' MB';
                    } elseif($rec['soft'] == 0){
                        $rec['soft'] = '----';
                    } else {
                        $rec['soft'] .= ' KB';
                    }
                    if($rec['hard'] > 1024) {
                        $rec['hard'] = round($rec['hard'] / 1024,2).' MB';
                    } elseif($rec['hard'] == 0){
                        $rec['hard'] = '----';
                    } else {
                        $rec['hard'] .= ' KB';
                    }
                    // send notifications only if 90% or more of the quota are used
                    if($used_ratio < 0.9) {
                        // reset notification date
                        if($rec['last_quota_notification']) $app->dbmaster->datalogUpdate('web_domain', "last_quota_notification = NULL", 'domain_id', $rec['domain_id']);
                        // send notification - everything ok again
                        if($rec['last_quota_notification'] && $web_config['overquota_notify_onok'] == 'y' && ($web_config['overquota_notify_admin'] == 'y' || $web_config['overquota_notify_client'] == 'y')) {
                            $placeholders = array('{domain}' => $rec['domain'],
                                                  '{admin_mail}' => ($global_config['admin_mail'] != ''? $global_config['admin_mail'] : 'root'),
                                                  '{used}' => $rec['used'],
                                                  '{soft}' => $rec['soft'],
                                                  '{hard}' => $rec['hard'],
                                                  '{ratio}' => $rec['ratio']);
                            $recipients = array();
                            //* send email to admin
                            if($global_config['admin_mail'] != '' && $web_config['overquota_notify_admin'] == 'y') {
                                $recipients[] = $global_config['admin_mail'];
                            }
                            //* Send email to client
                            if($web_config['overquota_notify_client'] == 'y') {
                                $client_group_id = $rec["sys_groupid"];
                                $client = $app->db->queryOneRecord("SELECT client.email FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
                                if($client['email'] != '') {
                                    $recipients[] = $client['email'];
                                }
                            }
                            send_notification_email('web_quota_ok_notification', $placeholders, $recipients);
                        }
                        continue;
                    }
                    // could a notification be sent?
                    $send_notification = false;
                    if(!$rec['last_quota_notification']) $send_notification = true; // not yet notified
                    elseif($web_config['overquota_notify_freq'] > 0 && $rec['notified_before'] >= $web_config['overquota_notify_freq']) $send_notification = true;
                    //* Send quota notifications
                    if(($web_config['overquota_notify_admin'] == 'y' || $web_config['overquota_notify_client'] == 'y') && $send_notification == true) {
                        $app->dbmaster->datalogUpdate('web_domain', "last_quota_notification = CURDATE()", 'domain_id', $rec['domain_id']);
                        $placeholders = array('{domain}' => $rec['domain'],
                                              '{admin_mail}' => ($global_config['admin_mail'] != ''? $global_config['admin_mail'] : 'root'),
                                              '{used}' => $rec['used'],
                                              '{soft}' => $rec['soft'],
                                              '{hard}' => $rec['hard'],
                                              '{ratio}' => $rec['ratio']);
                        $recipients = array();
                        //* send email to admin
                        if($global_config['admin_mail'] != '' && $web_config['overquota_notify_admin'] == 'y') {
                            $recipients[] = $global_config['admin_mail'];
                        }
                        //* Send email to client
                        if($web_config['overquota_notify_client'] == 'y') {
                            $client_group_id = $rec["sys_groupid"];
                            $client = $app->db->queryOneRecord("SELECT client.email FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
                            if($client['email'] != '') {
                                $recipients[] = $client['email'];
                            }
                        }
                        send_notification_email('web_quota_notification', $placeholders, $recipients);
                    }
                }
            }
        }
        #######################################################################################################
        // send mail quota warnings by email
        #######################################################################################################
        if ($app->dbmaster == $app->db) {
            $global_config = $app->getconf->get_global_config('mail');
            $mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail');
            //* Check email quota
            $sql = "SELECT mailuser_id,sys_groupid,email,name,quota,last_quota_notification,DATEDIFF(CURDATE(), last_quota_notification) as `notified_before` FROM mail_user";
            $records = $app->db->queryAllRecords($sql);
            if(is_array($records) && !empty($records)) {
                $tmp_rec =  $app->db->queryAllRecords("SELECT data from monitor_data WHERE type = 'email_quota' ORDER BY created DESC");
                $monitor_data = array();
                if(is_array($tmp_rec)) {
                    foreach ($tmp_rec as $tmp_mon) {
                        //$monitor_data = array_merge_recursive($monitor_data,unserialize($app->db->unquote($tmp_mon['data'])));
                        $tmp_array = unserialize($app->db->unquote($tmp_mon['data']));
                        if(is_array($tmp_array)) {
                            foreach($tmp_array as $username => $data) {
                                if(@!$monitor_data[$username]['used']) $monitor_data[$username]['used'] = $data['used'];
                            }
                        }
                    }
                }
                foreach($records as $rec) {
                    $email = $rec['email'];
                    $rec['used'] = isset($monitor_data[$email]['used']) ? $monitor_data[$email]['used'] : array(1 => 0);
                    if (!is_numeric($rec['used'])) $rec['used']=$rec['used'][1];
                    // used space ratio
                    if($rec['quota'] > 0){
                        $used_ratio = $rec['used']/$rec['quota'];
                    } else {
                        $used_ratio = 0;
                    }
                    $rec['ratio'] = number_format($used_ratio * 100, 2, '.', '').'%';
                    if($rec['quota'] > 0){
                        $rec['quota'] = round($rec['quota'] / 1048576,4).' MB';
                    } else {
                        $rec['quota'] = '----';
                    }
                    if($rec['used'] < 1544000) {
                        $rec['used'] = round($rec['used'] / 1024,4).' KB';
                    } else {
                        $rec['used'] = round($rec['used'] / 1048576,4).' MB';
                    }
                    // send notifications only if 90% or more of the quota are used
                    if($used_ratio < 0.9) {
                        // reset notification date
                        if($rec['last_quota_notification']) $app->dbmaster->datalogUpdate('mail_user', "last_quota_notification = NULL", 'mailuser_id', $rec['mailuser_id']);
                        // send notification - everything ok again
                        if($rec['last_quota_notification'] && $mail_config['overquota_notify_onok'] == 'y' && ($mail_config['overquota_notify_admin'] == 'y' || $mail_config['overquota_notify_client'] == 'y')) {
                            $placeholders = array('{email}' => $rec['email'],
                                      '{admin_mail}' => ($global_config['admin_mail'] != ''? $global_config['admin_mail'] : 'root'),
                                      '{used}' => $rec['used'],
                                      '{name}' => $rec['name'],
                                      '{quota}' => $rec['quota'],
                                      '{ratio}' => $rec['ratio']);
                            $recipients = array();
                            //* send email to admin
                            if($global_config['admin_mail'] != '' && $mail_config['overquota_notify_admin'] == 'y') {
                                $recipients[] = $global_config['admin_mail'];
                            }
                            //* Send email to client
                            if($mail_config['overquota_notify_client'] == 'y') {
                                $client_group_id = $rec["sys_groupid"];
                                $client = $app->db->queryOneRecord("SELECT client.email FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
                                if($client['email'] != '') {
                                    $recipients[] = $client['email'];
                                }
                            }
                            send_notification_email('mail_quota_ok_notification', $placeholders, $recipients);
                        }
                        continue;
                    }
                    //* Send quota notifications
                    // could a notification be sent?
                    $send_notification = false;
                    if(!$rec['last_quota_notification']) $send_notification = true; // not yet notified
                    elseif($mail_config['overquota_notify_freq'] > 0 && $rec['notified_before'] >= $mail_config['overquota_notify_freq']) $send_notification = true;
                    if(($mail_config['overquota_notify_admin'] == 'y' || $mail_config['overquota_notify_client'] == 'y') && $send_notification == true) {
                        $app->dbmaster->datalogUpdate('mail_user', "last_quota_notification = CURDATE()", 'mailuser_id', $rec['mailuser_id']);
                        $placeholders = array('{email}' => $rec['email'],
                                  '{admin_mail}' => ($global_config['admin_mail'] != ''? $global_config['admin_mail'] : 'root'),
                                  '{used}' => $rec['used'],
                                  '{name}' => $rec['name'],
                                  '{quota}' => $rec['quota'],
                                  '{ratio}' => $rec['ratio']);
                        $recipients = array();
                        //* send email to admin
                        if($global_config['admin_mail'] != '' && $mail_config['overquota_notify_admin'] == 'y') {
                            $recipients[] = $global_config['admin_mail'];
                        }
                        //* Send email to client
                        if($mail_config['overquota_notify_client'] == 'y') {
                            $client_group_id = $rec["sys_groupid"];
                            $client = $app->db->queryOneRecord("SELECT client.email FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id");
                            if($client['email'] != '') {
                                $recipients[] = $client['email'];
                            }
                        }
                        send_notification_email('mail_quota_notification', $placeholders, $recipients);
                    }
                }
            }
        }
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/400-openvz.inc.php
New file
@@ -0,0 +1,83 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_openvz extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        #######################################################################################################
        // deactivate virtual servers (run only on the "master-server")
        #######################################################################################################
        if ($app->dbmaster == $app->db) {
            $current_date = date('Y-m-d');
            //* Check which virtual machines have to be deactivated
            $sql = "SELECT * FROM openvz_vm WHERE active = 'y' AND active_until_date != '0000-00-00' AND active_until_date < '$current_date'";
            $records = $app->db->queryAllRecords($sql);
            if(is_array($records)) {
                foreach($records as $rec) {
                    $app->dbmaster->datalogUpdate('openvz_vm', "active = 'n'", 'vm_id', $rec['vm_id']);
                    $app->log('Virtual machine active date expired. Disabling VM '.$rec['veid'],LOGLEVEL_DEBUG);
                }
            }
        }
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.d/500-backup.inc.php
New file
@@ -0,0 +1,330 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob_backup extends cronjob {
    // job schedule
    protected $_schedule = '0 0 * * *';
    /* this function is optional if it contains no custom code */
    public function onPrepare() {
        global $app;
        parent::onPrepare();
    }
    /* this function is optional if it contains no custom code */
    public function onBeforeRun() {
        global $app;
        return parent::onBeforeRun();
    }
    public function onRunJob() {
        global $app, $conf;
        #######################################################################################################
        // Create website backups
        #######################################################################################################
        function formatBytes($size, $precision = 2) {
            $base=log($size)/log(1024);
            $suffixes=array('','k','M','G','T');
                return round(pow(1024,$base-floor($base)),$precision).$suffixes[floor($base)];
        }
        $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 != '') {
            if(isset($server_config['backup_dir_ftpread']) && $server_config['backup_dir_ftpread'] == 'y') {
                $backup_dir_permissions = 0755;
            } else {
                $backup_dir_permissions = 0750;
            }
            if(!is_dir($backup_dir)) {
                mkdir(escapeshellcmd($backup_dir), $backup_dir_permissions, true);
            } else {
                chmod(escapeshellcmd($backup_dir), $backup_dir_permissions);
            }
            $sql = "SELECT * FROM web_domain WHERE server_id = '".$conf['server_id']."' AND (type = 'vhost' OR type = 'vhostsubdomain') AND backup_interval != 'none'";
            $records = $app->db->queryAllRecords($sql);
            if(is_array($records)) {
                foreach($records as $rec) {
                    //* 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'];
                        $web_user = $rec['system_user'];
                        $web_group = $rec['system_group'];
                        $web_id = $rec['domain_id'];
                        $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 {
                            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 -b /tmp --exclude=backup\* --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@', $tmp_output, $retval);
                            if($retval == 0) exec('cd '.escapeshellarg($web_path).' && sudo -u '.escapeshellarg($web_user).' find . -user '.escapeshellarg($http_server_user).' -print 2> /dev/null | zip -b /tmp --exclude=backup\* --update --symlinks '.escapeshellarg($web_backup_dir.'/'.$web_backup_file).' -@', $tmp_output, $retval);
                        } 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).' .', $tmp_output, $retval);
                        }
                        if($retval == 0){
                            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');
                            $sql = "INSERT INTO web_backup (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->db->query($sql);
                            if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                        } else {
                            if(is_file($web_backup_dir.'/'.$web_backup_file)) unlink($web_backup_dir.'/'.$web_backup_file);
                        }
                        //* Remove old backups
                        $backup_copies = intval($rec['backup_copies']);
                        $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; $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']);
                                //$sql = "DELETE FROM web_backup WHERE backup_id = ".intval($tmp['backup_id']);
                                $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($files[$n])."'";
                                $app->db->query($sql);
                                if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                            }
                        }
                        unset($files);
                        unset($dir_handle);
                        //* Remove backupdir symlink and create as directory instead
                        $app->uses('system');
                        $app->system->web_folder_protection($web_path,false);
                        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']);
                        }
                        $app->system->web_folder_protection($web_path,true);
                    }
                    /* 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'];
                        $web_backup_dir = realpath($backup_dir.'/web'.$web_id);
                        if(is_dir($web_backup_dir)) {
                            exec('sudo -u '.escapeshellarg($web_user).' rm -f '.escapeshellarg($web_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($db_backup_dir)) mkdir($db_backup_dir, 0750);
                        chmod($db_backup_dir, 0750);
                        chown($db_backup_dir, 'root');
                        chgrp($db_backup_dir, 'root');
                        //* Do the mysql database backup with mysqldump or mongodump
                        $db_id = $rec['database_id'];
                        $db_name = $rec['database_name'];
                        if ($rec['type'] == 'mysql') {
                            $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."'";
                            $command = "mysqldump -h ".escapeshellarg($clientdb_host)." -u ".escapeshellarg($clientdb_user)." -p".escapeshellarg($clientdb_password)." -c --add-drop-table --quote-names --routines --events --triggers --hex-blob --create-options --quick --result-file='".$db_backup_dir.'/'.$db_backup_file."' '".$db_name."'";
                            exec($command, $tmp_output, $retval);
                            //* Compress the backup with gzip
                            if($retval == 0) exec("gzip -c '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file)."' > '".escapeshellcmd($db_backup_dir.'/'.$db_backup_file).".gz'", $tmp_output, $retval);
                            if($retval == 0){
                                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');
                                $sql = "INSERT INTO web_backup (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->db->query($sql);
                                if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                            } else {
                                if(is_file($db_backup_dir.'/'.$db_backup_file.'.gz')) unlink($db_backup_dir.'/'.$db_backup_file.'.gz');
                            }
                            //* Remove the uncompressed file
                            if(is_file($db_backup_dir.'/'.$db_backup_file)) unlink($db_backup_dir.'/'.$db_backup_file);
                        } else if ($rec['type'] == 'mongo') {
                            $db_backup_file = 'db_'.$db_name.'_'.date('Y-m-d_H-i');
                            try {
                                $connection = new MongoClient("mongodb://root:123456@127.0.0.1:27017/admin");
                                $db = $connection->selectDB($db_name);
                                // exclude not supported by mongodump, only get user collections
                                $collections = $db->getCollectionNames(false);
                                foreach ($collections as $collection) {
                                    // mongodump -h 127.0.0.1 --port 27017 -u root -p 123456 --authenticationDatabase admin -d <db> -c <table> -o /tmp/test
                                    $command = "mongodump -h 127.0.0.1 --port 27017 -u root -p 123456 --authenticationDatabase admin -d ".escapeshellcmd($db_name)." -c ".escapeshellcmd($collection)." -o ".escapeshellcmd($db_backup_dir.'/'.$db_backup_file);
                                    exec($command);
                                }
                                if (is_dir(escapeshellcmd($db_backup_dir.'/'.$db_backup_file))) {
                                    //* Compress the backup with gzip
                                    exec("cd ".escapeshellcmd($db_backup_dir)." && tar -pczf ".escapeshellcmd($db_backup_dir.'/'.$db_backup_file).".tar.gz ".escapeshellcmd($db_backup_file));
                                    chmod($db_backup_dir.'/'.$db_backup_file.'.tar.gz', 0750);
                                    chown($db_backup_dir.'/'.$db_backup_file.'.tar.gz', fileowner($db_backup_dir));
                                    chgrp($db_backup_dir.'/'.$db_backup_file.'.tar.gz', filegroup($db_backup_dir));
                                    //* Insert web backup record in database
                                    $sql = "INSERT INTO web_backup (server_id,parent_domain_id,backup_type,backup_mode,tstamp,filename) VALUES (".$conf['server_id'].",$web_id,'mongodb','rootgz',".time().",'".$app->db->quote($db_backup_file).".tar.gz')";
                                    $app->db->query($sql);
                                    if ($app->db->dbHost != $app->dbmaster->dbHost) {
                                        $app->dbmaster->query($sql);
                                    }
                                    //* Remove the uncompressed file
                                    exec("rm -rf ".escapeshellcmd($db_backup_dir.'/'.$db_backup_file));
                                }
                            } catch (MongoConnnectionException $e) {
                                // connecting to MongoDB failed - cannot create backup
                            }
                        }
                        //* 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 != '..' && (preg_match('/^db_(.*?)_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.sql.gz$/', $entry, $matches) || preg_match('/^db_(.*?)_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.tar.gz$/', $entry, $matches)) && is_file($db_backup_dir.'/'.$entry)) {
                                if(array_key_exists($matches[1], $files) == false) $files[$matches[1]] = array();
                                $files[$matches[1]][] = $entry;
                            }
                        }
                        $dir_handle->close();
                        reset($files);
                        foreach($files as $db_name => $filelist) {
                            rsort($filelist);
                            for ($n = $backup_copies; $n <= 10; $n++) {
                                if(isset($filelist[$n]) && is_file($db_backup_dir.'/'.$filelist[$n])) {
                                    unlink($db_backup_dir.'/'.$filelist[$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($filelist[$n])."'";
                                    //$tmp = $app->dbmaster->queryOneRecord($sql);
                                    //$sql = "DELETE FROM web_backup WHERE backup_id = ".intval($tmp['backup_id']);
                                    $sql = "DELETE FROM web_backup WHERE server_id = ".$conf['server_id']." AND parent_domain_id = $web_id AND filename = '".$app->db->quote($filelist[$n])."'";
                                    $app->db->query($sql);
                                    if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql);
                                }
                            }
                        }
                        unset($files);
                        unset($dir_handle);
                    }
                }
                unset($clientdb_host);
                unset($clientdb_user);
                unset($clientdb_password);
            }
        }
        parent::onRunJob();
    }
    /* this function is optional if it contains no custom code */
    public function onAfterRun() {
        global $app;
        parent::onAfterRun();
    }
}
?>
server/lib/classes/cron.inc.php
New file
@@ -0,0 +1,271 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cron {
    /**#@+
     * @access private
     */
    private $_sMinute = '';
    private $_sHour = '';
    private $_sDay = '';
    private $_sMonth = '';
    private $_sWDay = '';
    private $_bParsed = false;
    private $_iNextRun = null;
    private $_aValidValues;
    public function __construct() {
        // empty
        $this->_sMinute = '';
        $this->_sHour = '';
        $this->_sDay = '';
        $this->_sMonth = '';
        $this->_sWDay = '';
        $this->_bParsed = false;
        $this->_aValidValues = array('minute' => array(),
                                     'hour' => array(),
                                     'day' => array(),
                                     'month' => array(),
                                     'weekday' => array());
    }
    private function _calcValidValues() {
        // minute field
        $this->_aValidValues['minute'] = $this->_calcFieldValues('minute', $this->_sMinute);
        $this->_aValidValues['hour'] = $this->_calcFieldValues('hour', $this->_sHour);
        $this->_aValidValues['day'] = $this->_calcFieldValues('day', $this->_sDay);
        $this->_aValidValues['month'] = $this->_calcFieldValues('month', $this->_sMonth);
        $this->_aValidValues['weekday'] = $this->_calcFieldValues('weekday', $this->_sWDay);
        $this->_bParsed = true;
    }
    private function _calcFieldValues($sField, $sValue) {
        global $app;
        $aValidValues = array();
        // global checks
        $iFrom = 0;
        $iTo = 0;
        switch($sField) {
            case 'minute':
                $iTo = 59;
                break;
            case 'hour':
                $iTo = 23;
                break;
            case 'day':
                $iFrom = 1;
                $iTo = 31;
                break;
            case 'month':
                $sValue = strtr($sValue, array('JAN' => 1,
                                               'FEB' => 2,
                                               'MAR' => 3,
                                               'APR' => 4,
                                               'MAY' => 5,
                                               'JUN' => 6,
                                               'JUL' => 7,
                                               'AUG' => 8,
                                               'SEP' => 9,
                                               'OCT' => 10,
                                               'NOV' => 11,
                                               'DEC' => 12)
                                              );
                $iFrom = 1;
                $iTo = 12;
                break;
            case 'weekday':
                $sValue = strtr($sValue, array('SUN' => 0,
                                               'MON' => 1,
                                               'TUE' => 2,
                                               'WED' => 3,
                                               'THU' => 4,
                                               'FRI' => 5,
                                               'SAT' => 6,
                                               '7' => 0)
                                              );
                $iTo = 6;
                break;
        }
        $aParts = explode(',', $sValue);
        for($a = 0; $a < count($aParts); $a++) {
            $sValue = $aParts[$a];
            $iValue = $app->functions->intval($sValue);
            if($sValue === '*') {
                // everything is valid
                for($i = $iFrom; $i <= $iTo; $i++) {
                    $aValidValues[] = $i;
                }
                break; // no need to go any further
            } elseif((string)$iValue == $sValue) {
                if($iValue >= $iFrom && $iValue <= $iTo) $aValidValues[] = $iValue;
            } elseif(preg_match('/^([0-9]+)-([0-9]+)(\/([1-9][0-9]*))?$/', $sValue, $aMatch)) {
                if($aMatch[1] < $iFrom) $aMatch[1] = $iFrom;
                if($aMatch[2] > $iTo) $aMatch[2] = $iTo;
                if(isset($aMatch[3])) {
                    for($i = $aMatch[1]; $i <= $aMatch[2]; $i++) {
                        if(($i - $aMatch[1]) % $aMatch[4] == 0) $aValidValues[] = $i;
                    }
                } else {
                    for($i = $aMatch[1]; $i <= $aMatch[2]; $i++) $aValidValues[] = $i;
                }
            } elseif(preg_match('/^\*\/([1-9][0-9]*)$/', $sValue, $aMatch)) {
                for($i = $iFrom; $i <= $iTo; $i++) {
                    if($i % $aMatch[1] == 0) $aValidValues[] = $i;
                }
            }
        }
        $aValidValues = array_unique($aValidValues);
        sort($aValidValues);
        return $aValidValues;
    }
    /**#@-*/
    /**
     * Set the cron field values
     *
     * @param string $sMinute the minute field value
     * @param string $sHour the hour field value
     * @param string $sDay the day field value
     * @param string $sWDay the weekday field value
     * @param string $sMonth the month field value
     */
    public function setCronFields($sMinute = '*', $sHour = '*', $sDay = '*', $sMonth = '*', $sWDay = '*') {
        $this->_sMinute = $sMinute;
        $this->_sHour = $sHour;
        $this->_sDay = $sDay;
        $this->_sMonth = $sMonth;
        $this->_sWDay = $sWDay;
        $this->_bParsed = false;
    }
    /**
     * Parse a line of a cron and set the internal field values
     *
     * @param string $sLine cron line
     */
    public function parseCronLine($sLine) {
        $aFields = preg_split('/[ \t]+/', trim($sLine));
        for($i = 0; $i < 5; $i++) {
            if(!isset($aFields[$i])) $aFields[$i] = '*';
        }
        if($aFields[0] == '@yearly' || $aFields[0] == '@annually') $aFields = array(0, 0, 1, 1, '*');
        elseif($aFields[0] == '@monthly') $aFields = array(0, 0, 1, '*', '*');
        elseif($aFields[0] == '@weekly') $aFields = array(0, 0, '*', '*', 0);
        elseif($aFields[0] == '@daily' || $aFields[0] == '@midnight') $aFields = array(0, 0, '*', '*', '*');
        elseif($aFields[0] == '@hourly') $aFields = array(0, '*', '*', '*', '*');
        $this->setCronFields($aFields[0], $aFields[1], $aFields[2], $aFields[3], $aFields[4]);
    }
    public function getNextRun($vDate) {
        global $app;
        $iTimestamp = ISPConfigDatetime::to_timestamp($vDate);
        if($iTimestamp === false) return $iTimestamp;
        if($this->_bParsed == false) $this->_calcValidValues();
        // get the field values for the given Date.
        list($iMinute, $iHour, $iDay, $iWDay, $iMonth, $iYear) = explode(':', ISPConfigDateTime::to_string($vDate, 'custom:%M:%H:%d:%w:%m:%Y'));
        $bValid = false;
        $iStartYear = $iYear;
        while($bValid == false) {
            $iCurMinute = $this->_getNextValue('minute', $iMinute, true);
            $iCurHour = $this->_getNextValue('hour', $iHour, true);
            $iCurDay = $this->_getNextValue('day', $iDay, true);
            $iCurMonth = $this->_getNextValue('month', $iMonth, true);
            $iCurWDay = $this->_getNextValue('weekday', $iWDay, true);
            $iNextMinute = $this->_getNextValue('minute', $iMinute);
            $iNextHour = $this->_getNextValue('hour', $iHour);
            $iNextDay = $this->_getNextValue('day', $iDay);
            $iNextMonth = $this->_getNextValue('month', $iMonth);
            $iNextWDay = $this->_getNextValue('weekday', $iWDay);
            if($iNextMinute > $iMinute && $iHour == $iCurHour && $iDay == $iCurDay && $iWDay == $iCurWDay && $iMonth == $iCurMonth) {
                $iMinute = $iNextMinute;
            } elseif($iNextHour > $iHour && $iDay == $iCurDay && $iWDay == $iCurWDay && $iMonth == $iCurMonth) {
                $iMinute = reset($this->_aValidValues['minute']);
                $iHour = $iNextHour;
            } elseif($iNextDay > $iDay && ISPConfigDateTime::last_day($iMonth) >= $iNextDay && $iMonth == $iCurMonth) {
                $iMinute = reset($this->_aValidValues['minute']);
                $iHour = reset($this->_aValidValues['hour']);
                $iDay = $iNextDay;
            } elseif($iNextMonth > $iMonth) {
                $iMinute = reset($this->_aValidValues['minute']);
                $iHour = reset($this->_aValidValues['hour']);
                $iDay = reset($this->_aValidValues['day']);
                $iMonth = $iNextMonth;
            } else {
                $iMinute = reset($this->_aValidValues['minute']);
                $iHour = reset($this->_aValidValues['hour']);
                $iDay = reset($this->_aValidValues['day']);
                $iMonth = reset($this->_aValidValues['month']);
                $iYear++;
            }
            $ts = mktime($iHour, $iMinute, 0, $iMonth, $iDay, $iYear);
            //print strftime('%d.%m.%Y (%A) %H:%M', $ts) . "\n";
            //var_dump($iCurMinute, $iCurHour, $iCurDay, $iCurMonth, $iCurWDay, '--', $iNextMinute, $iNextHour, $iNextDay, $iNextMonth, $iNextWDay);
            if(ISPConfigDateTime::last_day($iMonth, $iYear) >= $iDay && in_array($app->functions->intval(strftime('%w', $ts)), $this->_aValidValues['weekday'], true) === true) {
                $bValid = true;
            } else {
                if($iYear - $iStartYear > 5) {
                    if(LOG_PRIORITY <= PRIO_ERROR) $portal->log('No valid run dates for schedule ' . $this->_sMinute . ' ' . $this->_sHour . ' ' . $this->_sDay . ' ' . $this->_sMonth . ' ' . $this->_sWDay . ' in the next 5 years!', PRIO_ERROR, __FILE__, __LINE__);
                    return false;
                }
            }
        }
        //var_dump($vDate, implode('-', array($iYear, $iMonth, $iDay, $iHour, $iNextMinute, 0)), $this->_sMinute, $this->_sHour, $this->_sDay, $this->_sWDay, $this->_sMonth, $this->_aValidValues);
        return $iYear . '-' . $iMonth . '-' . $iDay . ' ' . $iHour . ':' . $iNextMinute . ':0';
    }
    private function _getNextValue($sField, $iValue, $bIncludeCurrent = false) {
        if(!array_key_exists($sField, $this->_aValidValues)) return false;
        reset($this->_aValidValues[$sField]);
        while(($cur = each($this->_aValidValues[$sField])) !== false) {
            if($bIncludeCurrent == true && $cur['value'] >= $iValue) return $cur['value'];
            elseif($cur['value'] > $iValue) return $cur['value'];
        }
        return reset($this->_aValidValues[$sField]);
    }
}
?>
server/lib/classes/cronjob.inc.php
New file
@@ -0,0 +1,158 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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 cronjob {
    // default is every 5 minutes
    protected $_schedule = '*/5 * * * *';
    // may a run be skipped?
    protected $_no_skip = false;
    // if true, this job is run when it is first recognized. If false, the next run is calculated from schedule on first run.
    protected $_run_at_new = false;
    protected $_last_run = null;
    protected $_next_run = null;
    private $_running = false;
    /** return schedule */
    public function getSchedule() {
        return $this->_schedule;
    }
    /** run through cronjob sequence **/
    public function run() {
        print "Called run() for class " . get_class($this) . "\n";
        print "Job has schedule: " . $this->_schedule . "\n";
        $this->onPrepare();
        $run_it = $this->onBeforeRun();
        if($run_it == true) {
            $this->onRunJob();
            $this->onAfterRun();
        }
        $this->onCompleted();
        return;
    }
    /* this function prepares some data for the job and sets next run time if first executed */
    protected function onPrepare() {
        global $app;
        print "Called onPrepare() for class " . get_class($this) . "\n";
        // check the run time and values for this job
        // get previous run data
        $data = $app->db->queryOneRecord("SELECT `last_run`, `next_run`, `running` FROM `sys_cron` WHERE `name` = '" . $app->db->quote(get_class($this)) . "'");
        if($data) {
            if($data['last_run']) $this->_last_run = $data['last_run'];
            if($data['next_run']) $this->_next_run = $data['next_run'];
            if($data['running'] == 1) $this->_running = true;
        }
        if(!$this->_next_run) {
            if($this->_run_at_new == true) {
                $this->_next_run = ISPConfigDateTime::dbtime(); // run now.
            } else {
                $app->cron->parseCronLine($this->_schedule);
                $next_run = $app->cron->getNextRun(ISPConfigDateTime::dbtime());
                $this->_next_run = $next_run;
                $app->db->query("REPLACE INTO `sys_cron` (`name`, `last_run`, `next_run`, `running`) VALUES ('" . $app->db->quote(get_class($this)) . "', " . ($this->_last_run ? "'" . $app->db->quote($this->_last_run) . "'" : "NULL") . ", " . ($next_run === false ? "NULL" : "'" . $app->db->quote($next_run) . "'") . ", " . ($this->_running == true ? "1" : "0") . ")");
            }
        }
    }
    /* this function checks if a cron job's next runtime is reached and returns true or false */
    protected function onBeforeRun() {
        global $app;
        print "Called onBeforeRun() for class " . get_class($this) . "\n";
        if($this->_running == true) return false; // job is still marked as running!
        print "Jobs next run is " . $this->_next_run . "\n";
        $reached = ISPConfigDateTime::compare($this->_next_run, ISPConfigDateTime::dbtime());
        print "Date compare of " . ISPConfigDateTime::to_timestamp($this->_next_run) . " and " . ISPConfigDateTime::dbtime() . " is " . $reached . "\n";
        if($reached === false) return false; // error!
        if($reached === -1) {
            // next_run time not reached
            return false;
        }
        // next_run time reached (reached === 0 or -1)
        // calculare next run time based on last_run or current time
        $app->cron->parseCronLine($this->_schedule);
        if($this->_no_skip == true) {
            // we need to calculare the next run based on the previous next_run, as we may not skip one.
            $next_run = $app->cron->getNextRun($this->_next_run);
            if($next_run === false) {
                // we could not calculate next run, try it with current time
                $next_run = $app->cron->getNextRun(ISPConfigDateTime::dbtime());
            }
        } else {
            // calculate next run based on current time
            $next_run = $app->cron->getNextRun(ISPConfigDateTime::dbtime());
        }
        print "Jobs next run is now " . $next_run . "\n";
        $app->db->query("REPLACE INTO `sys_cron` (`name`, `last_run`, `next_run`, `running`) VALUES ('" . $app->db->quote(get_class($this)) . "', NOW(), " . ($next_run === false ? "NULL" : "'" . $app->db->quote($next_run) . "'") . ", 1)");
        return true;
    }
    // child classes should override this!
    protected function onRunJob() {
        global $app;
        print "Called onRun() for class " . get_class($this) . "\n";
    }
    // child classes may override this!
    protected function onAfterRun() {
        global $app;
        print "Called onAfterRun() for class " . get_class($this) . "\n";
    }
    // child classes may NOT override this!
    private function onCompleted() {
        global $app;
        print "Called onCompleted() for class " . get_class($this) . "\n";
        $app->db->query("UPDATE `sys_cron` SET `running` = 0 WHERE `name` = '" . $app->db->quote(get_class($this)) . "'");
    }
}
?>
server/lib/classes/functions.inc.php
New file
@@ -0,0 +1,398 @@
<?php
/*
Copyright (c) 2010, Till Brehm, projektfarm Gmbh
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of ISPConfig nor the names of its contributors
      may be used to endorse or promote products derived from this software without
      specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//* The purpose of this library is to provide some general functions.
//* This class is loaded automatically by the ispconfig framework.
class functions {
    var $idn_converter = null;
    var $idn_converter_name = '';
    public function mail($to, $subject, $text, $from, $filepath = '', $filetype = 'application/pdf', $filename = '', $cc = '', $bcc = '', $from_name = '') {
        global $app,$conf;
        if($conf['demo_mode'] == true) $app->error("Mail sending disabled in demo mode.");
        $app->uses('getconf,ispcmail');
        $mail_config = $app->getconf->get_global_config('mail');
        if($mail_config['smtp_enabled'] == 'y') {
            $mail_config['use_smtp'] = true;
            $app->ispcmail->setOptions($mail_config);
        }
        $app->ispcmail->setSender($from, $from_name);
        $app->ispcmail->setSubject($subject);
        $app->ispcmail->setMailText($text);
        if($filepath != '') {
            if(!file_exists($filepath)) $app->error("Mail attachement does not exist ".$filepath);
            $app->ispcmail->readAttachFile($filepath);
        }
        if($cc != '') $app->ispcmail->setHeader('Cc', $cc);
        if($bcc != '') $app->ispcmail->setHeader('Bcc', $bcc);
        $app->ispcmail->send($to);
        $app->ispcmail->finish();
        /* left in here just for the case...
        if($filepath != '') {
            if(!file_exists($filepath)) $app->error("Mail attachement does not exist ".$filepath);
            $content = file_get_contents($filepath);
            $content = chunk_split(base64_encode($content));
            $uid = strtoupper(md5(uniqid(time())));
            $subject      = "=?utf-8?B?".base64_encode($subject)."?=";
            if($filename == '') {
                $path_parts = pathinfo($filepath);
                $filename = $path_parts["basename"];
                unset($path_parts);
            }
            $header = "Return-Path: $from\nFrom: $from\nReply-To: $from\n";
            if($cc != '') $header .= "Cc: $cc\n";
            if($bcc != '') $header .= "Bcc: $bcc\n";
            $header .= "MIME-Version: 1.0\n";
            $header .= "Content-Type: multipart/mixed; boundary=$uid\n";
            $header .= "--$uid\n";
            $header .= "Content-Type: text/plain;\n\tcharset=\"UTF-8\"\n";
            $header .= "Content-Transfer-Encoding: 8bit\n\n";
            $header .= "$text\n";
            $header .= "--$uid\n";
            $header .= "Content-Type: $filetype; name=\"$filename\"\n";
            $header .= "Content-Transfer-Encoding: base64\n";
            $header .= "Content-Disposition: attachment; filename=\"$filename\"\n\n";
            $header .= "$content\n";
            $header .= "--$uid--";
            mail($to, $subject, "", $header);
        } else {
            $header = "From: $from\nReply-To: $from\n";
            if($cc != '') $header .= "Cc: $cc\n";
            if($bcc != '') $header .= "Bcc: $bcc\n";
            $header .= "Content-Type: text/plain;\n\tcharset=\"UTF-8\"\n";
            $header .= "Content-Transfer-Encoding: 8bit\n\n";
            $subject      = "=?utf-8?B?".base64_encode($subject)."?=";
            mail($to, $subject, $text, $header);
        }
        */
        return true;
    }
    public function array_merge($array1,$array2) {
        $out = $array1;
        foreach($array2 as $key => $val) {
            $out[$key] = $val;
        }
        return $out;
    }
    public function currency_format($number, $view = '') {
        global $app;
        if($view != '') $number_format_decimals = (int)$app->lng('number_format_decimals_'.$view);
        if(!$number_format_decimals) $number_format_decimals = (int)$app->lng('number_format_decimals');
        $number_format_dec_point = $app->lng('number_format_dec_point');
        $number_format_thousands_sep = $app->lng('number_format_thousands_sep');
        if($number_format_thousands_sep == 'number_format_thousands_sep') $number_format_thousands_sep = '';
        return number_format((double)$number, $number_format_decimals, $number_format_dec_point, $number_format_thousands_sep);
    }
    public function get_ispconfig_url() {
        global $app;
        $url = (stristr($_SERVER['SERVER_PROTOCOL'],'HTTPS') || stristr($_SERVER['HTTPS'],'on'))?'https':'http';
        if($_SERVER['SERVER_NAME'] != '_') {
            $url .= '://'.$_SERVER['SERVER_NAME'];
            if($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
                $url .= ':'.$_SERVER['SERVER_PORT'];
            }
        } else {
            $app->uses("getconf");
            $server_config = $app->getconf->get_server_config(1,'server');
            $url .= '://'.$server_config['hostname'];
            if($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
                $url .= ':'.$_SERVER['SERVER_PORT'];
            }
        }
        return $url;
    }
    public function json_encode($data) {
        if(!function_exists('json_encode')){
            if(is_array($data) || is_object($data)){
                $islist = is_array($data) && (empty($data) || array_keys($data) === range(0,count($data)-1));
                if($islist){
                    $json = '[' . implode(',', array_map(array($this, "json_encode"), $data) ) . ']';
                } else {
                    $items = Array();
                    foreach( $data as $key => $value ) {
                        $items[] = $this->json_encode("$key") . ':' . $this->json_encode($value);
                    }
                    $json = '{' . implode(',', $items) . '}';
                }
            } elseif(is_string($data)){
                # Escape non-printable or Non-ASCII characters.
                # I also put the \\ character first, as suggested in comments on the 'addclashes' page.
                $string = '"'.addcslashes($data, "\\\"\n\r\t/".chr(8).chr(12)).'"';
                $json = '';
                $len = strlen($string);
                # Convert UTF-8 to Hexadecimal Codepoints.
                for($i = 0; $i < $len; $i++){
                    $char = $string[$i];
                    $c1 = ord($char);
                    # Single byte;
                    if($c1 <128){
                        $json .= ($c1 > 31) ? $char : sprintf("\\u%04x", $c1);
                        continue;
                    }
                    # Double byte
                    $c2 = ord($string[++$i]);
                    if(($c1 & 32) === 0){
                        $json .= sprintf("\\u%04x", ($c1 - 192) * 64 + $c2 - 128);
                        continue;
                    }
                    # Triple
                    $c3 = ord($string[++$i]);
                    if(($c1 & 16) === 0){
                        $json .= sprintf("\\u%04x", (($c1 - 224) <<12) + (($c2 - 128) << 6) + ($c3 - 128));
                        continue;
                    }
                    # Quadruple
                    $c4 = ord($string[++$i]);
                    if(($c1 & 8) === 0){
                        $u = (($c1 & 15) << 2) + (($c2>>4) & 3) - 1;
                        $w1 = (54<<10) + ($u<<6) + (($c2 & 15) << 2) + (($c3>>4) & 3);
                        $w2 = (55<<10) + (($c3 & 15)<<6) + ($c4-128);
                        $json .= sprintf("\\u%04x\\u%04x", $w1, $w2);
                    }
                }
            } else {
                # int, floats, bools, null
                $json = strtolower(var_export($data, true));
            }
            return $json;
        } else {
            return json_encode($data);
        }
    }
    public function suggest_ips($type = 'IPv4'){
        global $app;
        if($type == 'IPv4'){
            $regex = "/^[0-9]{1,3}(\.)[0-9]{1,3}(\.)[0-9]{1,3}(\.)[0-9]{1,3}$/";
        } else {
            // IPv6
            $regex = "/^(\:\:([a-f0-9]{1,4}\:){0,6}?[a-f0-9]{0,4}|[a-f0-9]{1,4}(\:[a-f0-9]{1,4}){0,6}?\:\:|[a-f0-9]{1,4}(\:[a-f0-9]{1,4}){1,6}?\:\:([a-f0-9]{1,4}\:){1,6}?[a-f0-9]{1,4})(\/\d{1,3})?$/i";
        }
        $ips = array();
        $results = $app->db->queryAllRecords("SELECT ip_address AS ip FROM server_ip WHERE ip_type = '".$type."'");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                if(preg_match($regex, $result['ip'])) $ips[] = $result['ip'];
            }
        }
        $results = $app->db->queryAllRecords("SELECT ip_address AS ip FROM openvz_ip");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                if(preg_match($regex, $result['ip'])) $ips[] = $result['ip'];
            }
        }
        $results = $app->db->queryAllRecords("SELECT data AS ip FROM dns_rr WHERE type = 'A' OR type = 'AAAA'");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                if(preg_match($regex, $result['ip'])) $ips[] = $result['ip'];
            }
        }
        $results = $app->db->queryAllRecords("SELECT ns AS ip FROM dns_slave");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                if(preg_match($regex, $result['ip'])) $ips[] = $result['ip'];
            }
        }
        $results = $app->db->queryAllRecords("SELECT xfer FROM dns_slave WHERE xfer != ''");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                $tmp_ips = explode(',', $result['xfer']);
                foreach($tmp_ips as $tmp_ip){
                    $tmp_ip = trim($tmp_ip);
                    if(preg_match($regex, $tmp_ip)) $ips[] = $tmp_ip;
                }
            }
        }
        $results = $app->db->queryAllRecords("SELECT xfer FROM dns_soa WHERE xfer != ''");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                $tmp_ips = explode(',', $result['xfer']);
                foreach($tmp_ips as $tmp_ip){
                    $tmp_ip = trim($tmp_ip);
                    if(preg_match($regex, $tmp_ip)) $ips[] = $tmp_ip;
                }
            }
        }
        $results = $app->db->queryAllRecords("SELECT also_notify FROM dns_soa WHERE also_notify != ''");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                $tmp_ips = explode(',', $result['also_notify']);
                foreach($tmp_ips as $tmp_ip){
                    $tmp_ip = trim($tmp_ip);
                    if(preg_match($regex, $tmp_ip)) $ips[] = $tmp_ip;
                }
            }
        }
        $results = $app->db->queryAllRecords("SELECT remote_ips FROM web_database WHERE remote_ips != ''");
        if(!empty($results) && is_array($results)){
            foreach($results as $result){
                $tmp_ips = explode(',', $result['remote_ips']);
                foreach($tmp_ips as $tmp_ip){
                    $tmp_ip = trim($tmp_ip);
                    if(preg_match($regex, $tmp_ip)) $ips[] = $tmp_ip;
                }
            }
        }
        $ips = array_unique($ips);
        sort($ips, SORT_NUMERIC);
        $result_array = array('cheader' => array(), 'cdata' => array());
        if(!empty($ips)){
            $result_array['cheader'] = array('title' => 'IPs',
                                            'total' => count($ips),
                                            'limit' => count($ips)
                                            );
            foreach($ips as $ip){
                $result_array['cdata'][] = array(    'title' => $ip,
                                                    'description' => $type,
                                                    'onclick' => '',
                                                    'fill_text' => $ip
                                                );
            }
        }
        return $result_array;
    }
    public function intval($string, $force_numeric = false) {
        if(intval($string) == 2147483647 || ($string > 0 && intval($string) < 0)) {
            if($force_numeric == true) return floatval($string);
            elseif(preg_match('/^([-]?)[0]*([1-9][0-9]*)([^0-9].*)*$/', $string, $match)) return $match[1].$match[2];
            else return 0;
        } else {
            return intval($string);
        }
    }
    /** IDN converter wrapper.
     * all converter classes should be placed in ISPC_CLASS_PATH.'/idn/'
     */
    private function _idn_encode_decode($domain, $encode = true) {
        if($domain == '') return '';
        if(preg_match('/^[0-9\.]+$/', $domain)) return $domain; // may be an ip address - anyway does not need to bee encoded
        // get domain and user part if it is an email
        $user_part = false;
        if(strpos($domain, '@') !== false) {
            $user_part = substr($domain, 0, strrpos($domain, '@'));
            $domain = substr($domain, strrpos($domain, '@') + 1);
        }
        if($encode == true) {
            if(function_exists('idn_to_ascii')) {
                $domain = idn_to_ascii($domain);
            } elseif(file_exists(ISPC_CLASS_PATH.'/idn/idna_convert.class.php')) {
                 /* use idna class:
                 * @author  Matthias Sommerfeld <mso@phlylabs.de>
                 * @copyright 2004-2011 phlyLabs Berlin, http://phlylabs.de
                 * @version 0.8.0 2011-03-11
                 */
                if(!is_object($this->idn_converter) || $this->idn_converter_name != 'idna_convert.class') {
                    include_once(ISPC_CLASS_PATH.'/idn/idna_convert.class.php');
                    $this->idn_converter = new idna_convert(array('idn_version' => 2008));
                    $this->idn_converter_name = 'idna_convert.class';
                }
                $domain = $this->idn_converter->encode($domain);
            }
        } else {
            if(function_exists('idn_to_utf8')) {
                $domain = idn_to_utf8($domain);
            } elseif(file_exists(ISPC_CLASS_PATH.'/idn/idna_convert.class.php')) {
                 /* use idna class:
                 * @author  Matthias Sommerfeld <mso@phlylabs.de>
                 * @copyright 2004-2011 phlyLabs Berlin, http://phlylabs.de
                 * @version 0.8.0 2011-03-11
                 */
                if(!is_object($this->idn_converter) || $this->idn_converter_name != 'idna_convert.class') {
                    include_once(ISPC_CLASS_PATH.'/idn/idna_convert.class.php');
                    $this->idn_converter = new idna_convert(array('idn_version' => 2008));
                    $this->idn_converter_name = 'idna_convert.class';
                }
                $domain = $this->idn_converter->decode($domain);
            }
        }
        if($user_part !== false) return $user_part . '@' . $domain;
        else return $domain;
    }
    public function idn_encode($domain) {
        $domains = explode("\n", $domain);
        for($d = 0; $d < count($domains); $d++) {
            $domains[$d] = $this->_idn_encode_decode($domains[$d], true);
        }
        return implode("\n", $domains);
    }
    public function idn_decode($domain) {
        $domains = explode("\n", $domain);
        for($d = 0; $d < count($domains); $d++) {
            $domains[$d] = $this->_idn_encode_decode($domains[$d], false);
        }
        return implode("\n", $domains);
    }
}
?>
server/lib/classes/libdatetime.inc.php
New file
@@ -0,0 +1,479 @@
<?php
/*
Copyright (c) 2013, Marius Cramer, pixcept KG
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.
*/
/**
 * Date and Time class
 *
 * provides functions related to date and time operations
 */
abstract class ISPConfigDateTime {
    /**
     * Get days, hours, minutes and seconds
     *
     * Returns an array with days, hours, minutes and seconds from a given amount of seconds
     *
     * @access public
     * @param int $seconds amount of seconds
     * @param bool $get_days if true get the days, too
     * @return array data (0 => days, 1 => hours, 2 => minutes, 3 => seconds)
     */
    public static function get_parts($seconds, $get_days = false) {
        $days = 0;
        if($get_days == true) {
            $days = floor($seconds / (3600 * 24));
            $seconds = $seconds % (3600 * 24);
        }
        $hours = floor($seconds / 3600);
        $seconds = $seconds % 3600;
        $minutes = floor($seconds / 60);
        $seconds = $seconds % 60;
        return array($days, $hours, $minutes, $seconds);
    }
    public static function dbtime() {
        global $app;
        $time = $app->db->queryOneRecord('SELECT UNIX_TIMESTAMP() as `time`');
        return $time['time'];
    }
    /**
     * Get a unix timestamp for a date
     *
     * @access public
     * @param mixed $date the date to convert. Can be
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @return int the unix timestamp
     */
    public static function to_timestamp($date) {
        if(!is_string($date) && !is_numeric($date)) return false;
        $date = trim($date);
        if(is_numeric($date)) return $date;
        if(strpos($date, '-') !== false) {
            $regex = "(\d{2,4})-(\d{1,2})-(\d{1,2})(\s+(\d{1,2}):(\d{1,2})(:(\d{1,2}))?)?";
            $ok = preg_match("'$regex'", $date, $matches);
            if($ok) {
                $year = $matches[1];
                $month = $matches[2];
                $day = $matches[3];
                $hour = isset($matches[5]) ? $matches[5] : 0;
                $minute = isset($matches[6]) ? $matches[6] : 0;
                $second = isset($matches[8]) ? $matches[8] : 0;
            }
        } else {
            $regex = "(\d{1,2})[/.](\d{1,2})[/.](\d{2,4})(\s+(\d{1,2}):(\d{1,2})(:(\d{1,2}))?)?";
            $ok = preg_match("'$regex'", $date, $matches);
            if($ok) {
                $year = $matches[3];
                $month = $matches[2];
                $day = $matches[1];
                $hour = isset($matches[5]) ? $matches[5] : 0;
                $minute = isset($matches[6]) ? $matches[6] : 0;
                $second = isset($matches[8]) ? $matches[8] : 0;
            }
        }
        if(!$ok) return false;
        if(!$day || !$month || !$year) return false;
        if(!$hour) $hour = 0;
        if(!$minute) $minute = 0;
        if(!$second) $second = 0;
        if($year < 1900) $year += 1900;
        if(!checkdate($month, $day, $year)) return false;
        $date = mktime($hour, $minute, $second, $month, $day, $year);
        return $date;
    }
    /**
     * Get a date string
     *
     * Returns a formatted date string
     *
     * @access public
     * @param mixed $date the date to convert. Can be
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @param string $format the format to get the date string in.
     * - short: dd.mm.yy
     * - veryshort: dd.mm.
     * - medium: dd.mm.yyyy
     * - long: dd. Month yyyy
     * - extra: Day, dd. Month yyyy
     * - day: dd
     * - monthnum: mm
     * - shortmonth: Short month name like Mar for March
     * - month: Month name
     * - shortyear: yy
     * - year: yyyy
     * - onlydate: dd.mm
     * - onlydatelong: dd. Month
     * - onlytime: HH:MM
     * - rss: Rss time format for XML
     * - nice: if you prepend a nice: (like nice:long) you will get results like "today" or "yesterday" if applicable
     * - custom: you can give a strftime format like %d.%m.%Y %H:%M if you prepend custom: to it
     * @param bool $time if true apped the time to the date string
     * @param bool $seconds if true append the seconds to the time
     * @return string date string
     */
    public static function to_string($date, $format = 'short', $time = false, $seconds = false) {
        global $portal;
        if(!$date) return '';
        setlocale(LC_TIME, array('de_DE.UTF-8', 'de_DE', 'de_DE.ISO-8859-1', 'de_DE.ISO-8859-15'));
        if(!is_numeric($date)) {
            $date = self::to_timestamp($date);
            if($date === false) return $date;
        }
        if($format == 'timestamp') return $date;
        $fmt = '';
        $prepend = '';
        if(substr($format, 0, 5) == 'nice:') {
            if(strftime('%d.%m.%Y', $date) == strftime('%d.%m.%Y', $portal->getTime())) {
                if($time == true) $format = 'onlytime';
                else $format = '';
                $prepend = 'Heute';
            } elseif(strftime('%d.%m.%Y', $date) == strftime('%d.%m.%Y', $portal->getTime() - 86400)) {
                if($time == true) $format = 'onlytime';
                else $format = '';
                $prepend = 'Gestern';
            } elseif(strftime('%d.%m.%Y', $date) == strftime('%d.%m.%Y', $portal->getTime() + 86400)) {
                if($time == true) $format = 'onlytime';
                else $format = '';
                $prepend = 'Morgen';
            } else {
                $format = substr($format, 5);
            }
        } elseif(substr($format, 0, 7) == 'custom:') {
            $fmt = substr($format, 7);
            $format = '';
            $time = false;
        }
        if($format == 'short') $fmt = '%d.%m.%y';
        elseif($format == 'veryshort') $fmt = '%d.%m.';
        elseif($format == 'medium') $fmt = '%d.%m.%Y';
        elseif($format == 'long') $fmt = '%d. %B %Y';
        elseif($format == 'extra') $fmt = '%A, %d. %B %Y';
        elseif($format == 'day') $fmt = '%d';
        elseif($format == 'monthnum') $fmt = '%m';
        elseif($format == 'shortmonth') $fmt = '%b';
        elseif($format == 'month') $fmt = '%B';
        elseif($format == 'shortyear') $fmt = '%y';
        elseif($format == 'year') $fmt = '%Y';
        elseif($format == 'onlydate') $fmt = '%d.%m.';
        elseif($format == 'onlydatelong') $fmt = '%d. %B';
        elseif($format == 'onlytime') {
            $fmt = '%H:%M';
            $time = false;
        } elseif($format == 'rss') {
            $ret = date(DATE_RSS, $date);
            if($prepend != '') $ret = $prepend . ' ' . $ret;
            return $ret;
        } elseif($format == 'sitemap') {
            $ret = date(DATE_ATOM, $date);
            if($prepend != '') $ret = $prepend . ' ' . $ret;
            return $ret;
        }
        if($time == true) $fmt .= ' %H:%M' . ($seconds == true ? ':%S' : '');
        if($fmt != '') $ret = strftime($fmt, $date);
        else $ret = '';
        if($prepend != '') $ret = trim($prepend . ' ' . $ret);
        return $ret;
    }
    /**
     * Get the month difference of two dates
     *
     * Gets the difference in months of two given dates.
     * The days are ignored, so the difference between 2010-01-21 and 2010-05-01 is 4!
     *
     * @access public
     * @param mixed $date_from the beginning date, either
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @param mixed $date_to the ending date, either
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @param bool $return_years if set to true, the function returns an array of years and months instead of months
     * @param bool $include_both if set to true, the starting AND ending month is included, so the month count is +1
     * @return mixed either int (months) or array of int (0 => years, 1 => months) or FALSE on invalid dates
     */
    public static function months_between($date_from, $date_to, $return_years = false, $include_both = false) {
        $date_from = self::to_string($date_from, 'custom:%Y%m');
        if($date_from === false) return $date_from;
        $date_to = self::to_string($date_to, 'custom:%Y%m');
        if($date_to === false) return $date_to;
        $date_from = intval($date_from);
        $date_to = intval($date_to);
        if($date_to < $date_from) return false;
        $result = $date_to - $date_from;
        if($include_both == true) $result++;
        $years = floor($result / 100);
        $months = $result % 100;
        if($months > 12) $months -= 88;
        elseif($months == 12) {
            $months = 0;
            $years++;
        }
        if($return_years == true) return array($years, $months);
        $months += ($years * 12);
        return $months;
    }
    /**
     * Get the day difference of two dates
     *
     * Gets the difference in days of two given dates.
     *
     * @access public
     * @param mixed $date_from the beginning date, either
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @param mixed $date_to the ending date, either
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @param bool $include_both if set to true, the starting AND ending day is included, so the day count is +1
     * @return mixed either int (days) or FALSE on invalid dates
     */
    public static function days_between($date_from, $date_to, $include_both = false) {
        $date_from = self::to_string($date_from, 'custom:%Y-%m-%d');
        if($date_from === false) return $date_from;
        list($y, $m, $d) = explode('-', $date_from);
        $ts_from = mktime(0, 0, 0, $m, $d, $y);
        $date_to = self::to_string($date_to, 'custom:%Y-%m-%d');
        if($date_to === false) return $date_to;
        list($y, $m, $d) = explode('-', $date_to);
        $ts_to = mktime(0, 0, 0, $m, $d, $y);
        $result = $ts_to - $ts_from;
        if($include_both == true) $result++;
        $days = floor($result / (3600 * 24));
        return $days;
    }
    /**
     * Check if one date is before another
     *
     * @access public
     * @param mixed $date_1 the first date, either
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @param mixed $date_2 the second date, either
     * - int (unix timestamp)
     * - date string yyyy-mm-dd[ hh:mm:ss]
     * - date string dd.mm.yyyy[ hh:mm:ss]
     * @return mixed either int (1 if the first date is earlier, -1 if the second date is earlier, 0 if both are the same) or FALSE on invalid dates
     */
    public static function compare($date_1, $date_2) {
        $ts_1 = self::to_timestamp($date_1);
        if($ts_1 === false) return false;
        $ts_2 = self::to_timestamp($date_2);
        if($ts_2 === false) return false;
        if($ts_1 < $ts_2) return 1;
        elseif($ts_1 > $ts_2) return -1;
        else return 0;
    }
    /**
     * Convert date to sql format
     *
     * Converts a date from different formats to the sql format if possible
     *
     * @access public
     * @param string $date date string in forms
     * - dd.mm.yy
     * - yyyy-mm-dd
     * - yy-mm-dd
     * - yyyy/mm/dd
     * - dd.mm.yy
     * - all formats can have time information HH:MM:SS appended
     * @param bool $no_time if true, the resulting sql date is without time part even if time was part of the input
     * @return mixed sql date string on success, error object otherwise
     */
    public static function sql_date($date = false, $no_time = false) {
        global $portal;
        $result = '';
        $time = '';
        if($date === false) $date = $portal->getTime(true);
        if(is_numeric($date)) {
            return ($no_time ? strftime('%Y-%m-%d', $date) : strftime('%Y-%m-%d %H:%M:%S', $date));
        }
        if(preg_match('/^(.*)(\d{1,2}:\d{1,2}(:\d{1,2})?)(\D|$)/', $date, $matches)) {
            $date = $matches[1];
            $time = ' ' . $matches[2];
        }
        if(preg_match('/(^|\D)(\d{4,4})-(\d{1,2})-(\d{1,2})(\D|$)/', $date, $result)) {
            $day = $result[4];
            $month = $result[3];
            $year = $result[2];
        } elseif(preg_match('/(^|\D)(\d{4,4})\/(\d{1,2})\/(\d{1,2})(\D|$)/', $date, $result)) {
            $day = $result[4];
            $month = $result[3];
            $year = $result[2];
        } elseif(preg_match('/(^|\D)(\d{2,2})-(\d{1,2})-(\d{1,2})(\D|$)/', $date, $result)) {
            $day = $result[4];
            $month = $result[3];
            $year = $result[2];
        } elseif(preg_match('/(^|\D)(\d{1,2})\.(\d{1,2})\.(\d{4,4})(\D|$)/', $date, $result)) {
            $day = $result[2];
            $month = $result[3];
            $year = $result[4];
        } elseif(preg_match('/(^|\D)(\d{1,2})\.(\d{1,2})\.(\d{2,2})(\D|$)/', $date, $result)) {
            $day = $result[2];
            $month = $result[3];
            $year = $result[4];
        } else {
            return false;
        }
        if($no_time == true) $time = '';
        $day = str_pad(intval($day), 2, '0', STR_PAD_LEFT);
        $month = str_pad(intval($month), 2, '0', STR_PAD_LEFT);
        $year = intval($year);
        $valid = checkdate($month, $day, $year);
        if(!$valid) return false;
        return $year . '-' . $month . '-' . $day . $time;
    }
    /**
     * Get information if given date is leap year
     *
     * @access public
     * @param mixed $date Date to check
     * @return bool true if leap year, false otherwise
     */
    public static function is_leap_year($date) {
        // check if only year was given
        if(is_numeric($date) && $date < 10000) $date .= '-01-01';
        $ts = self::to_timestamp($date);
        if($ts === false) return false;
        if(date('L', $ts) == 1) return true;
        else return false;
    }
    /**
     * Get the last day of the month
     *
     * @access public
     * @param int $month the month to get the last day for
     * @param int $year the corresponding year (for february in leap years)
     * @return bool true if leap year, false otherwise
     */
    public static function last_day($month, $year = false) {
        switch($month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                return 31;
                break;
            case 2:
                return ($year !== false && self::is_leap_year($year) ? 29 : 28);
                break;
            default:
                return 30;
                break;
        }
    }
    /**
     * Get age for given date
     *
     * Returns the age for a given date if possible
     *
     * @access public
     * @param string $date see ISPConfigDateTime::sql_date() for possible values
     * @return mixed int of age if successful, error object otherwise
     * @see ISPConfigDateTime::sql_date
     */
    public static function calc_age($date) {
        global $portal;
        $date = self::sql_date($date);
        if($date === false) return $date;
        list($year, $month, $day) = explode('-', $date);
        list($curyear, $curmonth, $curday) = explode('-', strftime('%Y-%m-%d', $portal->getTime()));
        $year_diff = $curyear - $year;
        $month_diff = $curmonth - $month;
        $day_diff = $curday - $day;
        if($day_diff < 0) $month_diff--;
        if($month_diff < 0) $year_diff--;
        if($year_diff < 0) $year_diff = 0;
        return $year_diff;
    }
}
server/lib/classes/monitor_tools.inc.php
@@ -226,432 +226,7 @@
        return array('name' => $distname, 'version' => $distver, 'id' => $distid, 'baseid' => $distbaseid);
    }
        //** Email Quota
        public function monitorEmailQuota() {
                global $conf, $app;
        //* Initialize data array
        $data = array();
        //* the id of the server as int
        $server_id = intval($conf['server_id']);
        //* The type of the data
        $type = 'email_quota';
        //* The state of the email_quota.
        $state = 'ok';
        $mailboxes = $app->db->queryAllRecords("SELECT email,maildir FROM mail_user WHERE server_id = $server_id");
        if(is_array($mailboxes)) {
            foreach($mailboxes as $mb) {
                $email = $mb['email'];
                $email_parts = explode('@',$mb['email']);
                $filename = $mb['maildir'].'/.quotausage';
                if(file_exists($filename) && !is_link($filename)) {
                    $quotafile = file($filename);
                    $data[$email]['used'] = trim($quotafile['1']);
                    unset($quotafile);
                } else {
                    exec('du -s '.escapeshellcmd($mb['maildir']),$out);
                    $parts = explode(' ',$out[0]);
                    $data[$email]['used'] = intval($parts[0])*1024;
                    unset($out);
                    unset($parts);
                }
            }
        }
        unset($mailboxes);
                //* Dovecot quota check Courier in progress lathama@gmail.com
        /*
                if($dir = opendir("/var/vmail")){
                        while (($quotafiles = readdir($dir)) !== false){
                                if(preg_match('/.\_quota$/', $quotafiles)){
                                        $quotafile = (file("/var/vmail/" . $quotafiles));
                                        $emailaddress = preg_replace('/_quota/',"", $quotafiles);
                                        $emailaddress = preg_replace('/_/',"@", $emailaddress);
                                        $data[$emailaddress]['used'] = trim($quotafile['1']);
                                }
                        }
                        closedir($dir);
                }
        */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
                return $res;
        }
        //** Filesystem Quota
    public function monitorHDQuota() {
        global $conf;
        //* Initialize data array
        $data = array();
        //* the id of the server as int
        $server_id = intval($conf['server_id']);
        //* The type of the data
        $type = 'harddisk_quota';
        //* The state of the harddisk_quota.
        $state = 'ok';
        //* Fetch the data for all users
        $dfData = shell_exec('repquota -au 2>/dev/null');
        //* Split into array
        $df = explode("\n", $dfData);
        //* ignore the first 5 lines, process the rest
        for ($i = 5; $i <= sizeof($df); $i++) {
            if ($df[$i] != '') {
                //* Make a array of the data
                $s = preg_split('/[\s]+/', $df[$i]);
                $username = $s[0];
                if (substr($username, 0, 3) == 'web') {
                    if (isset($data['user'][$username])) {
                        $data['user'][$username]['used'] += $s[2];
                        $data['user'][$username]['soft'] += $s[3];
                        $data['user'][$username]['hard'] += $s[4];
                        $data['user'][$username]['files'] += $s[5];
                    } else {
                        $data['user'][$username]['used'] = $s[2];
                        $data['user'][$username]['soft'] = $s[3];
                        $data['user'][$username]['hard'] = $s[4];
                        $data['user'][$username]['files'] = $s[5];
                    }
                }
            }
        }
        //** Fetch the data for all users
        $dfData = shell_exec('repquota -ag 2>/dev/null');
        //* split into array
        $df = explode("\n", $dfData);
        //* ignore the first 5 lines, process the rest
        for ($i = 5; $i <= sizeof($df); $i++) {
            if ($df[$i] != '') {
                //* Make a array of the data
                $s = preg_split('/[\s]+/', $df[$i]);
                $groupname = $s[0];
                if (substr($groupname, 0, 6) == 'client') {
                    if (isset($data['group'][$groupname])) {
                        $data['group'][$groupname]['used'] += $s[1];
                        $data['group'][$groupname]['soft'] += $s[2];
                        $data['group'][$groupname]['hard'] += $s[3];
                    } else {
                        $data['group'][$groupname]['used'] = $s[1];
                        $data['group'][$groupname]['soft'] = $s[2];
                        $data['group'][$groupname]['hard'] = $s[3];
                    }
                }
            }
        }
        //* Return the Result
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorServer() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'server_load';
        /*
            Fetch the data into a array
         */
        $procUptime = shell_exec("cat /proc/uptime | cut -f1 -d' '");
        $data['up_days'] = floor($procUptime / 86400);
        $data['up_hours'] = floor(($procUptime - $data['up_days'] * 86400) / 3600);
        $data['up_minutes'] = floor(($procUptime - $data['up_days'] * 86400 - $data['up_hours'] * 3600) / 60);
        $data['uptime'] = shell_exec('uptime');
        $tmp = explode(',', $data['uptime'], 4);
        $tmpUser = explode(' ', trim($tmp[2]));
        $data['user_online'] = intval($tmpUser[0]);
        //* New Load Average code to fix "always zero" bug in non-english distros. NEEDS TESTING
        $loadTmp = shell_exec("cat /proc/loadavg | cut -f1-3 -d' '");
        $load = explode(' ', $loadTmp);
        $data['load_1'] = floatval(str_replace(',', '.', $load[0]));
        $data['load_5'] = floatval(str_replace(',', '.', $load[1]));
        $data['load_15'] = floatval(str_replace(',', '.', $load[2]));
        /** The state of the server-load. */
        $state = 'ok';
        if ($data['load_1'] > 20)
            $state = 'info';
        if ($data['load_1'] > 50)
            $state = 'warning';
        if ($data['load_1'] > 100)
            $state = 'critical';
        if ($data['load_1'] > 150)
            $state = 'error';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorOsVer() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'os_info';
        /*
            Fetch the data into a array
         */
        $dist = $this->get_distname();
        $data['name'] = $dist['name'];
        $data['version'] = $dist['version'];
        /* the OS has no state. It is, what it is */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorIspcVer() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'ispc_info';
        /*
            Fetch the data into a array
         */
        $data['name'] = ISPC_APP_TITLE;
        $data['version'] = ISPC_APP_VERSION;
        /* the ISPC-Version has no state. It is, what it is */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorDiskUsage() {
        global $app,$conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'disk_usage';
        /** The state of the disk-usage */
        $state = 'ok';
        /** Fetch the data of ALL devices into a array (needed for monitoring!) */
        //$dfData = shell_exec('df -hT 2>/dev/null');
        $app->uses('getconf');
        $web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
        $dfData = shell_exec('df -hT|grep -v "'.$web_config['website_basedir'].'/" 2>/dev/null');
        // split into array
        $df = explode("\n", $dfData);
        /*
         * ignore the first line, process the rest
         */
        for ($i = 1; $i <= sizeof($df); $i++) {
            if ($df[$i] != '') {
                /*
                 * Make an array of the data
                 */
                $s = preg_split('/[\s]+/', $df[$i]);
                $data[$i]['fs'] = $s[0];
                $data[$i]['type'] = $s[1];
                $data[$i]['size'] = $s[2];
                $data[$i]['used'] = $s[3];
                $data[$i]['available'] = $s[4];
                $data[$i]['percent'] = $s[5];
                $data[$i]['mounted'] = $s[6];
                /*
                 * calculate the state
                 */
                $usePercent = floatval($data[$i]['percent']);
                //* get the free memsize
                if(substr($data[$i]['available'],-1) == 'G') {
                    $freesize = floatval($data[$i]['available'])*1024;
                } elseif(substr($data[$i]['available'],-1) == 'T') {
                    $freesize = floatval($data[$i]['available'])*1024*1024;
                } else {
                    $freesize = floatval($data[$i]['available']);
                }
                //* We don't want to check some filesystem which have no sensible filling levels
                switch ($data[$i]['type']) {
                    case 'iso9660':
                    case 'cramfs':
                    case 'udf':
                    case 'tmpfs':
                    case 'devtmpfs':
                    case 'udev':
                        break;
                    default:
                        if ($usePercent > 75 && $freesize < 2000)
                            $state = $this->_setState($state, 'info');
                        if ($usePercent > 80 && $freesize < 1000)
                            $state = $this->_setState($state, 'warning');
                        if ($usePercent > 90 && $freesize < 500)
                            $state = $this->_setState($state, 'critical');
                        if ($usePercent > 95 && $freesize < 100)
                            $state = $this->_setState($state, 'error');
                        break;
                }
            }
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorMemUsage() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'mem_usage';
        /*
            Fetch the data into a array
         */
        $miData = shell_exec('cat /proc/meminfo');
        $memInfo = explode("\n", $miData);
        foreach ($memInfo as $line) {
            $part = preg_split('/:/', $line);
            $key = trim($part[0]);
            $tmp = explode(' ', trim($part[1]));
            $value = 0;
            if ($tmp[1] == 'kB')
                $value = $tmp[0] * 1024;
            $data[$key] = $value;
        }
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorCpu() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'cpu_info';
        /*
            Fetch the data into a array
         */
        if (file_exists('/proc/cpuinfo')) {
            $cpuData = shell_exec('cat /proc/cpuinfo');
            $cpuInfo = explode("\n", $cpuData);
            $processor = 0;
            foreach ($cpuInfo as $line) {
                $part = preg_split('/:/', $line);
                $key = trim($part[0]);
                $value = trim($part[1]);
                if ($key == 'processor')
                    $processor = intval($value);
                if ($key != '')
                    $data[$key . ' ' . $processor] = $value;
            }
            /* the cpu has no state. It is, what it is */
            $state = 'no_state';
        } else {
            /*
             * It is not Linux, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data['output'] = '';
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    // this function remains in the tools class, because it is used by cron AND rescue
    public function monitorServices() {
        global $app;
        global $conf;
@@ -755,14 +330,15 @@
                $data['mysqlserver'] = 0;
                $state = 'error'; // because service is down
            }
            if ($this->_checkTcp('localhost', 27017)) {
                $data['mongodbserver'] = 1;
            } else {
                $data['mongodbserver'] = 0;
                $state = 'error'; // because service is down
            }
        }
        $data['mongodbserver'] = -1;
        if ($this->_checkTcp('localhost', 27017)) {
            $data['mongodbserver'] = 1;
        } else {
            $data['mongodbserver'] = 0;
            //$state = 'error'; // because service is down
            /* TODO!!! check if this is a mongodbserver at all, otherwise it will always throw an error state!!! */
        }
        /*
         * Return the Result
@@ -773,925 +349,7 @@
        $res['state'] = $state;
        return $res;
    }
    public function monitorOpenVzHost() {
        global $app;
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'openvz_veinfo';
        /*
            Fetch the data into a array
         */
        $app->load(openvz_tools);
        $openVzTools = new openvz_tools();
        $data = $openVzTools->getOpenVzVeInfo();
        /* the VE-Info has no state. It is, what it is */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorOpenVzUserBeancounter() {
        global $app;
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'openvz_beancounter';
        /*
            Fetch the data into a array
         */
        $app->load(openvz_tools);
        $openVzTools = new openvz_tools();
        $data = $openVzTools->getOpenVzVeBeanCounter();
        /* calculate the state of the beancounter */
        if ($data == '') {
            $state = 'no_state';
        } else {
            $state = 'ok';
            /* transfer this output-string into a array */
            $test = explode("\n", $data);
            /* the first list of the output is not needed */
            array_shift($test);
            /* now process all items of the rest */
            foreach ($test as $item) {
                /*
                 * eliminate all doubled spaces and spaces at the beginning and end
                 */
                while (strpos($item, '  ') !== false) {
                    $item = str_replace('  ', ' ', $item);
                }
                $item = trim($item);
                /*
                 * The failcounter is the LAST
                 */
                if ($item != '') {
                    $tmp = explode(' ', $item);
                    $failCounter = $tmp[sizeof($tmp) - 1];
                    if ($failCounter > 0)
                        $state = 'info';
                    if ($failCounter > 50)
                        $state = 'warning';
                    if ($failCounter > 200)
                        $state = 'critical';
                    if ($failCounter > 10000)
                        $state = 'error';
                }
            }
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorSystemUpdate() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'system_update';
        /* This monitoring is only available on Debian or Ubuntu */
        if (file_exists('/etc/debian_version')) {
            /*
             * first update the "apt database"
             */
            shell_exec('apt-get update');
            /*
             * Then test the upgrade.
             * if there is any output, then there is a needed update
             */
            $aptData = shell_exec('apt-get -s -qq dist-upgrade');
            if ($aptData == '') {
                /* There is nothing to update! */
                $state = 'ok';
            } else {
                /*
                 * There is something to update! this is in most cases not critical, so we can
                 * do a system-update once a month or so...
                 */
                $state = 'info';
            }
            /*
             * Fetch the output
             */
            $data['output'] = $aptData;
        } elseif (file_exists('/etc/gentoo-release')) {
            /*
             * first update the portage tree
             */
            // In keeping with gentoo's rsync policy, don't update to frequently (every four hours - taken from http://www.gentoo.org/doc/en/source_mirrors.xml)
            $do_update = true;
            if (file_exists('/usr/portage/metadata/timestamp.chk')) {
                $datetime = file_get_contents('/usr/portage/metadata/timestamp.chk');
                $datetime = trim($datetime);
                $dstamp = strtotime($datetime);
                if ($dstamp) {
                    $checkat = $dstamp + 14400; // + 4hours
                    if (mktime() < $checkat) {
                        $do_update = false;
                    }
                }
            }
            if ($do_update) {
                shell_exec('emerge --sync --quiet');
            }
            /*
             * Then test the upgrade.
             * if there is any output, then there is a needed update
             */
            $emergeData = shell_exec('glsa-check -t affected');
            if ($emergeData == '') {
                /* There is nothing to update! */
                $state = 'ok';
                $data['output'] = 'No unapplied GLSA\'s found on the system.';
            } else {
                /* There is something to update! */
                $state = 'info';
                $data['output'] = shell_exec('glsa-check -pv --nocolor affected 2>/dev/null');
            }
        } elseif (file_exists('/etc/SuSE-release')) {
            /*
             * update and find the upgrade.
             * if there is any output, then there is a needed update
             */
            $aptData = shell_exec('zypper -q lu');
            if ($aptData == '') {
                /* There is nothing to update! */
                $state = 'ok';
            } else {
                /*
                 * There is something to update! this is in most cases not critical, so we can
                 * do a system-update once a month or so...
                 */
                $state = 'info';
            }
            /*
             * Fetch the output
             */
            $data['output'] = shell_exec('zypper lu');
        } else {
            /*
             * It is not Debian/Ubuntu, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data['output'] = '';
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorMailQueue() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'mailq';
        /* Get the data from the mailq */
        $data['output'] = shell_exec('mailq');
        /*
         *  The last line has more informations
         */
        $tmp = explode("\n", $data['output']);
        $more = $tmp[sizeof($tmp) - 1];
        $res = $this->_getIntArray($more);
        $data['bytes'] = $res[0];
        $data['requests'] = $res[1];
        /** The state of the mailq. */
        $state = 'ok';
        if ($data['requests'] > 2000)
            $state = 'info';
        if ($data['requests'] > 5000)
            $state = 'warning';
        if ($data['requests'] > 8000)
            $state = 'critical';
        if ($data['requests'] > 10000)
            $state = 'error';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorRaid() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'raid_state';
        /*
         * We support several RAID types, but if we can't find any of them, we have no data
         */
        $state = 'no_state';
        $data['output'] = '';
        /*
         * Check, if Software-RAID is enabled
         */
        if (file_exists('/proc/mdstat')) {
            /*
             * Fetch the output
             */
            $data['output'] = shell_exec('cat /proc/mdstat');
            /*
             * Then calc the state.
             */
            $tmp = explode("\n", $data['output']);
            $state = 'ok';
            for ($i = 0; $i < sizeof($tmp); $i++) {
                /* fetch the next line */
                $line = $tmp[$i];
                if ((strpos($line, '[U_]') !== false) || (strpos($line, '[_U]') !== false)) {
                    /* One Disk is not working.
                     * if the next line starts with "[>" or "[=" then
                     * recovery (resync) is in state and the state is
                     * information instead of critical
                     */
                    $nextLine = $tmp[$i + 1];
                    if ((strpos($nextLine, '[>') === false) && (strpos($nextLine, '[=') === false)) {
                        $state = $this->_setState($state, 'critical');
                    } else {
                        $state = $this->_setState($state, 'info');
                    }
                }
                if (strpos($line, '[__]') !== false) {
                    /* both Disk are not working */
                    $state = $this->_setState($state, 'error');
                }
                if (strpos($line, '[UU]') !== false) {
                    /* The disks are OK.
                     * if the next line starts with "[>" or "[=" then
                     * recovery (resync) is in state and the state is
                     * information instead of ok
                     */
                    $nextLine = $tmp[$i + 1];
                    if ((strpos($nextLine, '[>') === false) && (strpos($nextLine, '[=') === false)) {
                        $state = $this->_setState($state, 'ok');
                    } else {
                        $state = $this->_setState($state, 'info');
                    }
                }
            }
        }
        /*
         * Check, if we have mpt-status installed (LSIsoftware-raid)
         */
        if (file_exists('/proc/mpt/summary')) {
            system('which mpt-status', $retval);
            if ($retval === 0) {
                /*
                 * Fetch the output
                 */
                $data['output'] = shell_exec('mpt-status --autoload');
                /*
                 * Then calc the state.
                 */
                $state = 'ok';
                if(is_array($data['output'])) {
                    foreach ($data['output'] as $item) {
                        /*
                        * The output contains information for every RAID and every HDD.
                        * We only need the state of the RAID
                        */
                        if (strpos($item, 'state ') !== false) {
                            /*
                            * We found a raid, process the state of it
                            */
                            if (strpos($item, ' ONLINE ') !== false) {
                                $this->_setState($state, 'ok');
                            } elseif (strpos($item, ' OPTIMAL ') !== false) {
                                $this->_setState($state, 'ok');
                            } elseif (strpos($item, ' INITIAL ') !== false) {
                                $this->_setState($state, 'info');
                            } elseif (strpos($item, ' INACTIVE ') !== false) {
                                $this->_setState($state, 'critical');
                            } elseif (strpos($item, ' RESYNC ') !== false) {
                                $this->_setState($state, 'info');
                            } elseif (strpos($item, ' DEGRADED ') !== false) {
                                $this->_setState($state, 'critical');
                            } else {
                                /* we don't know the state. so we set the state to critical, that the
                                * admin is warned, that something is wrong
                                */
                                $this->_setState($state, 'critical');
                            }
                        }
                    }
                }
            }
        }
        /*
        * 3ware Controller
        */
        system('which tw_cli', $retval);
        if($retval === 0) {
            $data['output'] = shell_exec('tw_cli info c0');
            $state = 'ok';
            if(is_array($data['output'])) {
            foreach ($data['output'] as $item) {
                if (strpos($item, 'RAID') !== false) {
                    if (strpos($item, ' VERIFYING ') !== false) {
                        $this->_setState($state, 'info');
                    }
                    else if (strpos($item, ' MIGRATE-PAUSED ') !== false) {
                        $this->_setState($state, 'info');
                    }
                    else if (strpos($item, ' MIGRATING ') !== false) {
                        $this->_setState($state, 'ok');
                    }
                    else if (strpos($item, ' INITIALIZING ') !== false) {
                        $this->_setState($state, 'info');
                    }
                    else if (strpos($item, ' INIT-PAUSED ') !== false) {
                        $this->_setState($state, 'info');
                    }
                    else if (strpos($item, ' REBUILDING ') !== false) {
                        $this->_setState($state, 'info');
                    }
                    else if (strpos($item, ' REBUILD-PAUSED ') !== false) {
                        $this->_setState($state, 'warning');
                    }
                    else if (strpos($item, ' RECOVERY ') !== false) {
                        $this->_setState($state, 'warning');
                    }
                    else if (strpos($item, ' DEGRADED ') !== false) {
                        $this->_setState($state, 'critical');
                    }
                    else if (strpos($item, ' UNKNOWN ') !== false) {
                        $this->_setState($state, 'critical');
                    }
                    else if (strpos($item, ' OK ') !== false) {
                        $this->_setState($state, 'ok');
                    }
                    else if (strpos($item, ' OPTIMAL ') !== false) {
                        $this->_setState($state, 'ok');
                    }
                    else {
                        $this->_setState($state, 'critical');
                    }
                }
            }
            }
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorRkHunter() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'rkhunter';
        /* This monitoring is only available if rkhunter is installed */
        system('which rkhunter', $retval);
        if ($retval === 0) {
            /*
             * Fetch the output
             */
            $data['output'] = shell_exec('rkhunter --update --checkall --nocolors --skip-keypress');
            /*
             * At this moment, there is no state (maybe later)
             */
            $state = 'no_state';
        } else {
            /*
             * rkhunter is not installed, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data['output'] = '';
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorFail2ban() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_fail2ban';
        /* This monitoring is only available if fail2ban is installed */
        system('which fail2ban-client', $retval); // Debian, Ubuntu, Fedora
        if ($retval !== 0)
            system('which fail2ban', $retval); // CentOS
        if ($retval === 0) {
            /*  Get the data of the log */
            $data = $this->_getLogData($type);
            /*
             * At this moment, there is no state (maybe later)
             */
            $state = 'no_state';
        } else {
            /*
             * fail2ban is not installed, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data = '';
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorMongoDB() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_mongodb';
        /* This monitoring is only available if MongoDB is installed */
        system('which mongod', $retval); // Debian, Ubuntu, Fedora
        if ($retval !== 0)
            system('which mongod', $retval); // CentOS
        if ($retval === 0) {
            /*  Get the data of the log */
            $data = $this->_getLogData($type);
            /*
             * At this moment, there is no state (maybe later)
             */
            $state = 'no_state';
        } else {
            /*
             * MongoDB is not installed, so there is no data and no state
             *
             * no_state, NOT unknown, because "unknown" is shown as state
             * inside the GUI. no_state is hidden.
             *
             * We have to write NO DATA inside the DB, because the GUI
             * could not know, if there is any dat, or not...
             */
            $state = 'no_state';
            $data = '';
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorIPTables() {
                global $conf;
                /* the id of the server as int */
                $server_id = intval($conf['server_id']);
                /** The type of the data */
                $type = 'iptables_rules';
                /* This monitoring is only available if fail2ban is installed */
                system('which iptables', $retval); // Debian, Ubuntu, Fedora
                if ($retval === 0) {
                        /*  Get the data of the log */
                        $data['output'] = '<h2>iptables -S (ipv4)</h2>'.shell_exec('iptables -S 2>/dev/null');
                        /*
                         * At this moment, there is no state (maybe later)
                         */
                        $state = 'no_state';
                } else {
                        $state = 'no_state';
                        $data = '';
                }
                /* This monitoring is only available if fail2ban is installed */
                system('which ip6tables', $retval); // Debian, Ubuntu, Fedora
                if ($retval === 0) {
                        /*  Get the data of the log */
                        $data['output'] .= '<br><h2>ip6tables -S (ipv6)</h2>'.shell_exec('ip6tables -S 2>/dev/null');
                        /*
                         * At this moment, there is no state (maybe later)
                         */
                        $state = 'no_state';
                } else {
                        $state = 'no_state';
                        $data = '';
                }
                /*
                 * Return the Result
                 */
                $res['server_id'] = $server_id;
                $res['type'] = $type;
                $res['data'] = $data;
                $res['state'] = $state;
                return $res;
        }
    public function monitorSysLog() {
        global $app;
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'sys_log';
        /*
         * is there any warning or error for this server?
         */
        $state = 'ok';
        $dbData = $app->dbmaster->queryAllRecords('SELECT loglevel FROM sys_log WHERE server_id = ' . $server_id . ' AND loglevel > 0');
        if (is_array($dbData)) {
            foreach ($dbData as $item) {
                if ($item['loglevel'] == 1)
                    $state = $this->_setState($state, 'warning');
                if ($item['loglevel'] == 2)
                    $state = $this->_setState($state, 'error');
            }
        }
        /** There is no monitor-data because the data is in the sys_log table */
        $data['output'] = '';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorMailLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_mail';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorMailWarnLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_mail_warn';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorMailErrLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_mail_err';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorMessagesLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_messages';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorISPCCronLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_ispc_cron';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        /*
         * actually this info has no state.
         * maybe someone knows better...???...
         */
        $state = 'no_state';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorFreshClamLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_freshclam';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        /* Get the data from the LAST log-Entry.
         * if there can be found:
         * WARNING: Your ClamAV installation is OUTDATED!
         * then the clamav is outdated. This is a warning!
         */
        $state = 'ok';
        $tmp = explode("\n", $data);
        $lastLog = array();
        if ($tmp[sizeof($tmp) - 1] == '') {
            /* the log ends with an empty line remove this */
            array_pop($tmp);
        }
        if (strpos($tmp[sizeof($tmp) - 1], '-------------') !== false) {
            /* the log ends with "-----..." remove this */
            array_pop($tmp);
        }
        for ($i = sizeof($tmp) - 1; $i > 0; $i--) {
            if (strpos($tmp[$i], '---------') === false) {
                /* no delimiter found, so add this to the last-log */
                $lastLog[] = $tmp[$i];
            } else {
                /* delimiter found, so there is no more line left! */
                break;
            }
        }
        /*
         * Now we have the last log in the array.
         * Check if the outdated-string is found...
         */
        foreach ($lastLog as $line) {
            if (strpos(strtolower($line), 'outdated') !== false) {
                /*
                 * Outdatet is only info, because if we set this to warning, the server is
                 * as long in state warning, as there is a new version of ClamAv which takes
                 * sometimes weeks!
                 */
                $state = $this->_setState($state, 'info');
            }
        }
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorClamAvLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_clamav';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        // Todo: the state should be calculated.
        $state = 'ok';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function monitorIspConfigLog() {
        global $conf;
        /* the id of the server as int */
        $server_id = intval($conf['server_id']);
        /** The type of the data */
        $type = 'log_ispconfig';
        /* Get the data of the log */
        $data = $this->_getLogData($type);
        // Todo: the state should be calculated.
        $state = 'ok';
        /*
         * Return the Result
         */
        $res['server_id'] = $server_id;
        $res['type'] = $type;
        $res['data'] = $data;
        $res['state'] = $state;
        return $res;
    }
    public function _getLogData($log) {
        global $conf;
@@ -1915,7 +573,7 @@
     * * If the actual state is critical and you call the method with error,
     *   then the state is error.
     */
    private function _setState($oldState, $newState) {
    public function _setState($oldState, $newState) {
        /*
         * Calculate the weight of the old state
         */
@@ -1965,20 +623,38 @@
        }
    }
    private function _getIntArray($line) {
        /** The array of float found */
        $res = array();
        /* First build a array from the line */
        $data = explode(' ', $line);
        /* then check if any item is a float */
        foreach ($data as $item) {
            if ($item . '' == (int) $item . '') {
                $res[] = $item;
            }
        }
        return $res;
    /**
     * Deletes Records older than 4 minutes.
     * The monitor writes new data every 5 minutes or longer (4 hour, 1 day).
     * So if i delete all Date older than 4 minutes i can be sure, that all old data
     * are deleted...
     */
    public function delOldRecords($type, $serverId) {
        global $app;
        // $now = time();
        // $old = $now - (4 * 60); // 4 minutes
        $old = 'UNIX_TIMESTAMP() - 240';
        /*
         * ATTENTION if i do NOT pay attention of the server id, i delete all data (of the type)
         * of ALL servers. This means, if i have a multiserver-environment and a server has a
         * time not synced with the others (for example, all server has 11:00 and ONE server has
         * 10:45) then the actual data of this server (with the time-stamp 10:45) get lost
         * even though it is the NEWEST data of this server. To avoid this i HAVE to include
         * the server-id!
         */
        $sql = 'DELETE FROM monitor_data ' .
                'WHERE ' .
                '  type =' . "'" . $app->dbmaster->quote($type) . "' " .
                'AND ' .
                '  created < ' . $old . ' ' .
                'AND ' .
                '  server_id = ' . $serverId;
        $app->dbmaster->query($sql);
    }
}
?>
server/mods-available/monitor_core_module.inc.php
File was deleted