Skip to content
...@@ -43,7 +43,11 @@ class app { ...@@ -43,7 +43,11 @@ class app {
if($conf['start_db'] == true) { if($conf['start_db'] == true) {
$this->load('db_'.$conf['db_type']); $this->load('db_'.$conf['db_type']);
try {
$this->db = new db; $this->db = new db;
} catch (Exception $e) {
$this->db = false;
}
/* /*
Initialize the connection to the master DB, Initialize the connection to the master DB,
...@@ -51,7 +55,11 @@ class app { ...@@ -51,7 +55,11 @@ class app {
*/ */
if($conf['dbmaster_host'] != '' && ($conf['dbmaster_host'] != $conf['db_host'] || ($conf['dbmaster_host'] == $conf['db_host'] && $conf['dbmaster_database'] != $conf['db_database']))) { if($conf['dbmaster_host'] != '' && ($conf['dbmaster_host'] != $conf['db_host'] || ($conf['dbmaster_host'] == $conf['db_host'] && $conf['dbmaster_database'] != $conf['db_database']))) {
try {
$this->dbmaster = new db($conf['dbmaster_host'], $conf['dbmaster_user'], $conf['dbmaster_password'], $conf['dbmaster_database'], $conf['dbmaster_port'], $conf['dbmaster_client_flags']); $this->dbmaster = new db($conf['dbmaster_host'], $conf['dbmaster_user'], $conf['dbmaster_password'], $conf['dbmaster_database'], $conf['dbmaster_port'], $conf['dbmaster_client_flags']);
} catch (Exception $e) {
$this->dbmaster = false;
}
} else { } else {
$this->dbmaster = $this->db; $this->dbmaster = $this->db;
} }
...@@ -61,6 +69,14 @@ class app { ...@@ -61,6 +69,14 @@ class app {
} }
public function __get($prop) {
if(property_exists($this, $prop)) return $this->{$prop};
$this->uses($prop);
if(property_exists($this, $prop)) return $this->{$prop};
else return null;
}
function setCaller($caller) { function setCaller($caller) {
$this->_calling_script = $caller; $this->_calling_script = $caller;
} }
......
<?php
/**
* addon installer
*
* @author Marius Burkard
*/
class addon_installer {
private function extractPackage($package_file) {
global $app;
$ret = null;
$retval = 0;
$app->log('Extracting addon package ' . $package_file, 0, false);
$cmd = 'which unzip';
$tmp = explode("\n", exec($cmd, $ret, $retval));
if($retval != 0) {
$app->log('The unzip command was not found on the server.', 2, false);
throw new AddonInstallerException('unzip tool not found.');
}
$unzip = reset($tmp);
unset($tmp);
if(!$unzip) {
$app->log('Unzip tool was not found.', 2, false);
throw new AddonInstallerException('unzip tool not found.');
}
$temp_dir = $app->system->tempdir(sys_get_temp_dir(), 'addon_', 0700);
if(!$temp_dir) {
$app->log('Could not create the temp dir.', 2, false);
throw new AddonInstallerException('Could not create temp dir.');
}
$ret = null;
$retval = 0;
$cmd = $unzip . ' -d ' . escapeshellarg($temp_dir) . ' ' . escapeshellarg($package_file);
exec($cmd, $ret, $retval);
if($retval != 0) {
$app->log('Package extraction failed.', 2, false);
throw new AddonInstallerException('Package extraction failed.');
}
$app->log('Extracted to ' . $temp_dir, 0, false);
return $temp_dir;
}
/**
* @param string $path
* @return string
* @throws AddonInstallerValidationException
*/
private function validatePackage($path) {
global $app;
$app->log('Validating extracted addon at ' . $path, 0, false);
if(!is_dir($path)) {
$app->log('Invalid path.', 2, false);
throw new AddonInstallerValidationException('Invalid path.');
}
$ini_file = $path . '/addon.ini';
if(!is_file($ini_file)) {
$app->log('Addon ini file missing.', 2, false);
throw new AddonInstallerValidationException('Addon ini file missing.');
}
$app->log('Parsing ini ' . $ini_file, 0, false);
$ini = parse_ini_file($ini_file, true);
if(!$ini || !isset($ini['addon'])) {
$app->log('Ini file could not be read.', 2, false);
throw new AddonInstallerValidationException('Ini file is missing addon section.');
}
$addon = $ini['addon'];
if(!isset($addon['ident']) || !isset($addon['name']) || !isset($addon['version'])) {
$app->log('Addon data in ini file missing or invalid.', 2, false);
throw new AddonInstallerValidationException('Ini file is missing addon ident/name/version.');
}
$class_file = $path . '/' . $addon['ident'] . '.addon.php';
if(!is_file($class_file)) {
$app->log('Base class file in addon not found', 2, false);
throw new AddonInstallerValidationException('Package is missing main addon class.');
}
if(isset($ini['ispconfig']['version.min']) && $ini['ispconfig']['version.min'] && version_compare($ini['ispconfig']['version.min'], ISPC_APP_VERSION, '>')) {
$app->log('ISPConfig version too low for this addon.', 2, false);
throw new AddonInstallerValidationException('Addon requires at least ISPConfig version ' . $ini['ispconfig']['version.min'] . '.');
} elseif(isset($ini['ispconfig']['version.max']) && $ini['ispconfig']['version.max'] && version_compare($ini['ispconfig']['version.min'], ISPC_APP_VERSION, '<')) {
$app->log('ISPConfig version too high for this addon.', 2, false);
throw new AddonInstallerValidationException('Addon allows at max ISPConfig version ' . $ini['ispconfig']['version.max'] . '.');
}
$app->log('Loaded addon installer ' . $class_file, 0, false);
$addon['class_file'] = $class_file;
$addon['class_name'] = substr(basename($class_file), 0, -10) . '_addon_installer';
return $addon;
}
private function getInstalledAddonVersion($ident) {
global $app, $conf;
$file_version = false;
$db_version = false;
$addon_path = realpath($conf['rootpath'] . '/..') . '/addons';
// check for previous version
if(is_dir($addon_path . '/' . $ident) && is_file($addon_path . '/' . $ident . '/addon.ini')) {
$addon = parse_ini_file($addon_path . '/' . $ident . '/addon.ini', true);
if($addon && isset($addon['addon'])) {
$addon = $addon['addon']; // ini section
} else {
$addon = false;
}
if(!$addon || !isset($addon['version']) || !isset($addon['ident']) || $addon['ident'] != $ident) {
$app->log('Could not get version of installed addon.', 2, false);
throw new AddonInstallerException('Installed app ' . $ident . ' found but it is invalid.');
}
$file_version = $addon['version'];
$app->log('Installed version of addon ' . $ident . ' is ' . $file_version, 0, false);
}
$check = $app->db->queryOneRecord('SELECT `addon_version` FROM `addons` WHERE `addon_ident` = ?', $ident);
if($check && $check['addon_version']) {
$db_version = $check['addon_version'];
$app->log('Installed version of addon ' . $ident . ' (in db) is ' . $db_version . '.', 0, false);
}
if(!$file_version && !$db_version) {
return false;
} elseif($file_version != $db_version) {
$app->log('Version mismatch between ini file and database (' . $file_version . ' != ' . $db_version . ').', 0, false);
throw new AddonInstallerException('Addon version mismatch in database (' . $db_version . ') and file system (' . $file_version . ').');
}
return $file_version;
}
/**
* @param string $package_file Full path
* @param boolean $force true if previous addon with same or higher version should be overwritten
* @throws AddonInstallerException
* @throws AddonInstallerValidationException
*/
public function installAddon($package_file, $force = false) {
global $app;
$app->load('ispconfig_addon_installer_base');
if(!is_file($package_file)) {
$app->log('Package file not found: ' . $package_file, 2, false);
throw new AddonInstallerException('Package file not found.');
} elseif(substr($package_file, -4) !== '.pkg') {
$app->log('Invalid package file: ' . $package_file, 2, false);
throw new AddonInstallerException('Invalid package file.');
}
$tmp_dir = $this->extractPackage($package_file);
if(!$tmp_dir) {
// extracting failed
$app->log('Package extraction failed.', 2, false);
throw new AddonInstallerException('Package extraction failed.');
}
$addon = $this->validatePackage($tmp_dir);
if(!$addon) {
throw new AddonInstallerException('Package validation failed.');
}
$app->log('Package validated.', 0, false);
$is_update = false;
$previous = $this->getInstalledAddonVersion($addon['ident']);
if($previous !== false) {
// this is an update
if(version_compare($previous, $addon['version'], '>') && $force !== true) {
$app->log('Installed version is newer than the one to install and --force not used.', 2, false);
throw new AddonInstallerException('Installed version is newer than the one to install.');
} elseif(version_compare($previous, $addon['version'], '=') && $force !== true) {
$app->log('Installed version is the same as the one to install and --force not used.', 2, false);
throw new AddonInstallerException('Installed version is the same as the one to install.');
}
$is_update = true;
}
$app->log('Including package class file ' . $addon['class_file'], 0, false);
include $addon['class_file'];
$class_name = $addon['class_name'];
if(!class_exists($class_name)) {
$app->log('Class name ' . $class_name . ' not found in class file ' . $addon['class_file'], 2, false);
throw new AddonInstallerException('Could not find main class in addon file.');
}
/* @var $inst ispconfig_addon_installer_base */
$app->log('Instanciating installer class ' . $class_name, 0, false);
$inst = new $class_name();
$inst->setAddonName($addon['name']);
$inst->setAddonIdent($addon['ident']);
$inst->setAddonVersion($addon['version']);
$inst->setAddonTempDir($tmp_dir);
if($is_update === true) {
$inst->onBeforeUpdate();
$inst->onUpdate();
$inst->onAfterUpdate();
} else {
$inst->onBeforeInstall();
$inst->onInstall();
$inst->onAfterInstall();
}
exec('rm -rf ' . escapeshellarg($tmp_dir));
$app->log('Installation completed.', 0, false);
return true;
}
}
...@@ -95,12 +95,12 @@ class cronjob_monitor_database_size extends cronjob { ...@@ -95,12 +95,12 @@ class cronjob_monitor_database_size extends cronjob {
if(!is_numeric($quota)) continue; if(!is_numeric($quota)) continue;
if($quota < 1 || $quota > $data[$i]['size']) { if($quota < 1 || $quota > $data[$i]['size']) {
print $rec['database_name'] . ' does not exceed quota qize: ' . $quota . ' > ' . $data[$i]['size'] . "\n"; print 'database ' . $rec['database_name'] . ' size does not exceed quota: ' . ($quota < 1 ? 'unlimited' : $quota) . ' (quota) > ' . $data[$i]['size'] . " (used)\n";
if($rec['quota_exceeded'] == 'y') { if($rec['quota_exceeded'] == 'y') {
$app->dbmaster->datalogUpdate('web_database', array('quota_exceeded' => 'n'), 'database_id', $rec['database_id']); $app->dbmaster->datalogUpdate('web_database', array('quota_exceeded' => 'n'), 'database_id', $rec['database_id']);
} }
} elseif($rec['quota_exceeded'] == 'n') { } elseif($rec['quota_exceeded'] == 'n') {
print $rec['database_name'] . ' exceeds quota qize: ' . $quota . ' < ' . $data[$i]['size'] . "\n"; print 'database ' . $rec['database_name'] . ' size exceeds quota: ' . $quota . ' (quota) < ' . $data[$i]['size'] . " (used)\n";
$app->dbmaster->datalogUpdate('web_database', array('quota_exceeded' => 'y'), 'database_id', $rec['database_id']); $app->dbmaster->datalogUpdate('web_database', array('quota_exceeded' => 'y'), 'database_id', $rec['database_id']);
} }
} }
......
...@@ -74,7 +74,7 @@ class cronjob_monitor_disk_usage extends cronjob { ...@@ -74,7 +74,7 @@ class cronjob_monitor_disk_usage extends cronjob {
$app->uses('getconf'); $app->uses('getconf');
$web_config = $app->getconf->get_server_config($conf['server_id'], 'web'); $web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
$dfData = shell_exec('df -PhT -x simfs | awk \'!x[$1]++\' 2>/dev/null'); $dfData = shell_exec('df -PhT -x simfs -x squashfs | awk \'!x[$1]++\' 2>/dev/null');
// split into array // split into array
$df = explode("\n", $dfData); $df = explode("\n", $dfData);
......
...@@ -80,7 +80,7 @@ class cronjob_monitor_email_quota extends cronjob { ...@@ -80,7 +80,7 @@ class cronjob_monitor_email_quota extends cronjob {
//* with dovecot we can use doveadm instead of 'du -s' //* with dovecot we can use doveadm instead of 'du -s'
$dovecot = false; $dovecot = false;
if (isset($mail_config['pop3_imap_daemon']) && $mail_config ['pop3_imap_daemon'] = 'dovecot' && is_executable('doveadm')) { if (is_executable('doveadm')) {
exec('doveadm quota 2>&1', $tmp_output, $tmp_retval); // with dovecot 2.2.x 'doveadm quota' is unuseable exec('doveadm quota 2>&1', $tmp_output, $tmp_retval); // with dovecot 2.2.x 'doveadm quota' is unuseable
if ($retval = 64) $dovecot = true; if ($retval = 64) $dovecot = true;
} }
...@@ -110,20 +110,6 @@ class cronjob_monitor_email_quota extends cronjob { ...@@ -110,20 +110,6 @@ class cronjob_monitor_email_quota extends cronjob {
unset($mailboxes); 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 = array();
$res['server_id'] = $server_id; $res['server_id'] = $server_id;
$res['type'] = $type; $res['type'] = $type;
......
<?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 (?, ?, UNIX_TIMESTAMP(), ?, ?)';
$app->dbmaster->query($sql, $res['server_id'], $res['type'], serialize($res['data']), $res['state']);
/* 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 (?, ?, UNIX_TIMESTAMP(), ?, ?)';
$app->dbmaster->query($sql, $res['server_id'], $res['type'], serialize($res['data']), $res['state']);
/* 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();
}
}
?>
...@@ -28,10 +28,11 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, ...@@ -28,10 +28,11 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
class cronjob_openvz extends cronjob { class cronjob_send_sys_mailqueue extends cronjob {
// job schedule // job schedule
protected $_schedule = '0 0 * * *'; protected $_schedule = '* * * * *';
protected $_run_at_new = true;
/* this function is optional if it contains no custom code */ /* this function is optional if it contains no custom code */
public function onPrepare() { public function onPrepare() {
...@@ -50,20 +51,15 @@ class cronjob_openvz extends cronjob { ...@@ -50,20 +51,15 @@ class cronjob_openvz extends cronjob {
public function onRunJob() { public function onRunJob() {
global $app, $conf; global $app, $conf;
//###################################################################################################### $app->uses('getconf,ispcmail');
// deactivate virtual servers (run only on the "master-server")
//###################################################################################################### $mail_config = $app->getconf->get_global_config('mail');
if($mail_config['smtp_enabled'] == 'y') {
if ($app->dbmaster == $app->db) { $mail_config['use_smtp'] = true;
//* Check which virtual machines have to be deactivated $app->ispcmail->setOptions($mail_config);
$sql = "SELECT * FROM openvz_vm WHERE active = 'y' AND active_until_date IS NOT NULL AND active_until_date < CURDATE()";
$records = $app->db->queryAllRecords($sql); // Mail queue is supported only with SMTP...
if(is_array($records)) { $app->ispcmail->send_queue();
foreach($records as $rec) {
$app->dbmaster->datalogUpdate('openvz_vm', array("active" => 'n'), 'vm_id', $rec['vm_id']);
$app->log('Virtual machine active date expired. Disabling VM '.$rec['veid'], LOGLEVEL_DEBUG);
}
}
} }
parent::onRunJob(); parent::onRunJob();
......
...@@ -71,9 +71,10 @@ class cronjob_ftplogfiles extends cronjob { ...@@ -71,9 +71,10 @@ class cronjob_ftplogfiles extends cronjob {
} }
} }
$fp = fopen('/var/log/pure-ftpd/transfer.log.1', 'r'); $fp = @fopen('/var/log/pure-ftpd/transfer.log.1', 'r');
$ftp_traffic = array(); $ftp_traffic = array();
if ($fp) {
// cumule des stats journalière dans un tableau // cumule des stats journalière dans un tableau
while($line = fgets($fp)) while($line = fgets($fp))
{ {
...@@ -88,6 +89,7 @@ class cronjob_ftplogfiles extends cronjob { ...@@ -88,6 +89,7 @@ class cronjob_ftplogfiles extends cronjob {
} }
fclose($fp); fclose($fp);
}
// Save du tableau en BD // Save du tableau en BD
foreach($ftp_traffic as $traffic_date => $all_traffic) foreach($ftp_traffic as $traffic_date => $all_traffic)
......
...@@ -150,8 +150,8 @@ class cronjob_logfiles extends cronjob { ...@@ -150,8 +150,8 @@ class cronjob_logfiles extends cronjob {
$error_logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/error.log'); $error_logfile = escapeshellcmd($rec['document_root'].'/' . $log_folder . '/error.log');
// rename older files (move up by one) // rename older files (move up by one)
$num = $log_retention; $num = $log_retention;
while($num >= 1 && is_file($error_logfile . '.' . $num . '.gz')) { while($num >= 1) {
rename($error_logfile . '.' . $num . '.gz', $error_logfile . '.' . ($num + 1) . '.gz'); if(is_file($error_logfile . '.' . $num . '.gz')) rename($error_logfile . '.' . $num . '.gz', $error_logfile . '.' . ($num + 1) . '.gz');
$num--; $num--;
} }
// compress current logfile // compress current logfile
......
...@@ -242,7 +242,7 @@ class cronjob_backup_mail extends cronjob { ...@@ -242,7 +242,7 @@ class cronjob_backup_mail extends cronjob {
if(!is_file($mail_backup_dir.'/'.$backup['filename'])){ if(!is_file($mail_backup_dir.'/'.$backup['filename'])){
$sql = "DELETE FROM mail_backup WHERE server_id = ? AND parent_domain_id = ? AND filename = ?"; $sql = "DELETE FROM mail_backup WHERE server_id = ? AND parent_domain_id = ? AND filename = ?";
$app->db->query($sql, $conf['server_id'], $backup['parent_domain_id'], $backup['filename']); $app->db->query($sql, $conf['server_id'], $backup['parent_domain_id'], $backup['filename']);
if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql); if($app->db->dbHost != $app->dbmaster->dbHost) $app->dbmaster->query($sql, $conf['server_id'], $backup['parent_domain_id'], $backup['filename']);
} }
} }
} }
......
<?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_jkupdate extends cronjob {
// job schedule
protected $_schedule = '45 22 3 * *';
protected $_run_at_new = true;
/* 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');
$jailkit_conf = $app->getconf->get_server_config($conf['server_id'], 'jailkit');
//$jailkit_programs = explode(' ', $jailkit_conf['jailkit_chroot_app_programs']);
$jailkit_programs = preg_split("/[\s,]+/", $jailkit_conf['jailkit_chroot_app_programs']);
$jailkit_sections = trim($jailkit_conf['jailkit_chroot_app_sections']);
$sites = $app->db->queryAllRecords("SELECT domain_id, document_root, fastcgi_php_version FROM web_domain WHERE jailkit_jkupdate_cron = 'y' AND type = 'vhost' AND parent_domain_id = 0 AND document_root != '' ORDER BY domain_id");
foreach($sites as $site) {
$set_php_symlink = false;
$users = $app->db->queryOneRecord("SELECT COUNT(*) AS user_count FROM shell_user WHERE parent_domain_id = ? AND active='y' AND chroot='jailkit'", intval($site['domain_id']));
$crons = $app->db->queryOneRecord("SELECT COUNT(*) AS cron_count FROM cron WHERE parent_domain_id = ? AND active='y' AND type='chrooted'", $site['domain_id']);
if ($users['user_count'] > 0 || $crons['cron_count'] > 0) {
if (!is_dir($site['document_root'])) {
return;
}
//* Protect web folders
$app->system->web_folder_protection($site['document_root'], false);
$app->log('Running jailkit init for '.$site['document_root']);
if($jailkit_sections != '') $this->run_jk_init($site['document_root'], $jailkit_sections);
$app->log('Running jailkit updates for '.$site['document_root']);
$this->run_jk_update($site['document_root']);
if(preg_match('@(\d\d?\.\d\d?\.\d\d?)@', $site['fastcgi_php_version'], $matches)){
if(!in_array('/opt/php-'.$matches[1].'/bin/php', $jailkit_programs)) $jailkit_programs[] = '/opt/php-'.$matches[1].'/bin/php';
if(!in_array('/opt/php-'.$matches[1].'/include', $jailkit_programs)) $jailkit_programs[] = '/opt/php-'.$matches[1].'/include';
if(!in_array('/opt/php-'.$matches[1].'/lib', $jailkit_programs)) $jailkit_programs[] = '/opt/php-'.$matches[1].'/lib';
if(!in_array('/opt/th-php-libs', $jailkit_programs)) $jailkit_programs[] = '/opt/th-php-libs';
$set_php_symlink = true;
}
if(is_array($jailkit_programs) && !empty($jailkit_programs)) $this->run_jk_cp($site['document_root'], $jailkit_programs);
$this->fix_broken_symlinks($site['document_root']);
if($set_php_symlink){
// create symlink from /usr/bin/php to current PHP version
if(preg_match('@(\d\d?\.\d\d?\.\d\d?)@', $site['fastcgi_php_version'], $matches) && (!file_exists($site['document_root'].'/usr/bin/php') || is_link($site['document_root'].'/usr/bin/php'))){
@unlink($site['document_root'].'/usr/bin/php');
@symlink('/opt/php-'.$matches[1].'/bin/php', $site['document_root'].'/usr/bin/php');
}
}
//* Protect web folders
$app->system->web_folder_protection($site['document_root'], true);
}
}
if(file_exists('/dev/tty')){
chmod('/dev/tty', 0666);
}
parent::onRunJob();
}
private function run_jk_init($document_root, $sections){
global $app;
$return_var = $this->exec_log('/usr/sbin/jk_init -f -k -c /etc/jailkit/jk_init.ini -j '.escapeshellarg($document_root).' '.$sections);
if ($return_var > 0) {
$app->log('jk_init failed with -j, trying again without -j', LOGLEVEL_DEBUG);
$return_var = $this->exec_log('/usr/sbin/jk_init -f -k -c /etc/jailkit/jk_init.ini '.escapeshellarg($document_root).' '.$sections);
if ($return_var > 0) {
$app->log('jk_init failed (with and without -j parameter)', LOGLEVEL_WARN);
}
}
}
private function run_jk_update($document_root) {
global $app;
$return_var = $this->exec_log('/usr/sbin/jk_update -j '.escapeshellarg($document_root));
if ($return_var > 0) {
$app->log('jk_update failed with -j, trying again without -j', LOGLEVEL_DEBUG);
$return_var = $this->exec_log('/usr/sbin/jk_update '.escapeshellarg($document_root));
if ($return_var > 0) {
$app->log('jk_update failed (with and without -j parameter)', LOGLEVEL_WARN);
}
}
}
private function run_jk_cp($document_root, $programs) {
global $app;
foreach($programs as $program) {
$program = trim($program);
if($program == ''){
continue;
}
if (!file_exists($program)) {
continue;
}
$return_var = $this->exec_log('/usr/sbin/jk_cp '.escapeshellarg($document_root).' '.escapeshellarg($program));
if ($return_var > 0) {
$app->log('jk_cp failed with -j, trying again with -j', LOGLEVEL_DEBUG);
$return_var = $this->exec_log('/usr/sbin/jk_cp '.escapeshellarg($document_root).' '.escapeshellarg($program));
if ($return_var > 0) {
$app->log('jk_cp failed (without and with -j parameter)', LOGLEVEL_WARN);
}
}
}
if(file_exists($document_root.'/dev/tty')){
chmod($document_root.'/dev/tty', 0666);
}
}
private function fix_broken_symlinks($document_root){
global $app;
exec('cd '.escapeshellarg($document_root).' && find . -type l \( ! -name web \) -xtype l', $output, $retval);
if(is_array($output) && !empty($output)){
foreach($output as $link){
$link = trim($link);
if(preg_match('@\.so(\.\d+)*$@',$link)){
if(substr($link, 0, 1) == '.') $link = substr($link, 1);
//echo $link."\n";
$path = $document_root.$link;
//if(is_link($path)) echo "Ist Link\n";
//if(!file_exists($path)) echo "Aber Link ist kaputt!\n";
if(is_link($path) && !file_exists($path)){
//echo $path."\n";
@unlink($path);
$this->run_jk_cp($document_root, array($link));
}
}
}
}
}
private function exec_log($cmd) {
global $app;
$app->log("Running $cmd", LOGLEVEL_DEBUG);
exec($cmd, $output, $return_var);
if (count($output) > 0) {
$app->log("Output:\n" . implode("\n", $output), LOGLEVEL_DEBUG);
}
return $return_var;
}
/* this function is optional if it contains no custom code */
public function onAfterRun() {
global $app;
parent::onAfterRun();
}
}
?>
...@@ -35,15 +35,11 @@ class cronjob_letsencrypt extends cronjob { ...@@ -35,15 +35,11 @@ class cronjob_letsencrypt extends cronjob {
/* this function is optional if it contains no custom code */ /* this function is optional if it contains no custom code */
public function onPrepare() { public function onPrepare() {
global $app;
parent::onPrepare(); parent::onPrepare();
} }
/* this function is optional if it contains no custom code */ /* this function is optional if it contains no custom code */
public function onBeforeRun() { public function onBeforeRun() {
global $app;
return parent::onBeforeRun(); return parent::onBeforeRun();
} }
...@@ -52,9 +48,19 @@ class cronjob_letsencrypt extends cronjob { ...@@ -52,9 +48,19 @@ class cronjob_letsencrypt extends cronjob {
$server_config = $app->getconf->get_server_config($conf['server_id'], 'server'); $server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
if(!isset($server_config['migration_mode']) || $server_config['migration_mode'] != 'y') { if(!isset($server_config['migration_mode']) || $server_config['migration_mode'] != 'y') {
$letsencrypt = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot'));
$letsencrypt = reset($letsencrypt); $acme = $app->letsencrypt->get_acme_script();
if(is_executable($letsencrypt)) { if($acme) {
// skip letsencrypt
parent::onRunJob();
return;
}
$letsencrypt = $app->letsencrypt->get_certbot_script();
if($letsencrypt) {
$ret = null;
$val = 0;
$matches = array();
$version = exec($letsencrypt . ' --version 2>&1', $ret, $val); $version = exec($letsencrypt . ' --version 2>&1', $ret, $val);
if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $version, $matches)) { if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $version, $matches)) {
$type = strtolower($matches[1]); $type = strtolower($matches[1]);
...@@ -85,11 +91,7 @@ class cronjob_letsencrypt extends cronjob { ...@@ -85,11 +91,7 @@ class cronjob_letsencrypt extends cronjob {
/* this function is optional if it contains no custom code */ /* this function is optional if it contains no custom code */
public function onAfterRun() { public function onAfterRun() {
global $app;
parent::onAfterRun(); parent::onAfterRun();
} }
} }
?>
...@@ -86,8 +86,8 @@ class cronjob { ...@@ -86,8 +86,8 @@ class cronjob {
if($run_it == true) { if($run_it == true) {
$this->onRunJob(); $this->onRunJob();
$this->onAfterRun(); $this->onAfterRun();
}
$this->onCompleted(); $this->onCompleted();
}
return; return;
} }
......
<?php <?php
/*
* db_mysql.inc.php: ISPConfig mysql db interface
*
* Note! When making changes to this file, put a copy in both locations:
* interface/lib/classes/db_mysql.inc.php
* server/lib/classes/db_mysql.inc.php
*/
/* /*
Copyright (c) 2005, Till Brehm, projektfarm Gmbh Copyright (c) 2005, Till Brehm, projektfarm Gmbh
All rights reserved. All rights reserved.
...@@ -41,12 +49,10 @@ class db ...@@ -41,12 +49,10 @@ class db
private $dbUser = ''; // database authorized user private $dbUser = ''; // database authorized user
private $dbPass = ''; // user's password private $dbPass = ''; // user's password
private $dbCharset = 'utf8';// Database charset private $dbCharset = 'utf8';// Database charset
private $dbNewLink = false; // Return a new linkID when connect is called again
private $dbClientFlags = 0; // MySQL Client falgs private $dbClientFlags = 0; // MySQL Client falgs
/**#@-*/ /**#@-*/
public $show_error_messages = false; // false in server, true in interface public $show_error_messages = false; // false in server, interface sets true when generating templates
/* old things - unused now //// /* old things - unused now ////
private $linkId = 0; // last result of mysqli_connect() private $linkId = 0; // last result of mysqli_connect()
...@@ -54,8 +60,8 @@ class db ...@@ -54,8 +60,8 @@ class db
private $record = array(); // last record fetched private $record = array(); // last record fetched
private $autoCommit = 1; // Autocommit Transactions private $autoCommit = 1; // Autocommit Transactions
private $currentRow; // current row number private $currentRow; // current row number
public $errorNumber = 0; // last error number
*/ */
public $errorNumber = 0; // last error number
public $errorMessage = ''; // last error message public $errorMessage = ''; // last error message
/* /*
private $errorLocation = '';// last error location private $errorLocation = '';// last error location
...@@ -73,25 +79,29 @@ class db ...@@ -73,25 +79,29 @@ class db
$this->dbUser = $user ? $user : $conf['db_user']; $this->dbUser = $user ? $user : $conf['db_user'];
$this->dbPass = $pass ? $pass : $conf['db_password']; $this->dbPass = $pass ? $pass : $conf['db_password'];
$this->dbCharset = $conf['db_charset']; $this->dbCharset = $conf['db_charset'];
$this->dbNewLink = $conf['db_new_link']; $this->dbClientFlags = ($flags !== NULL) ? $flags : $conf['db_client_flags'];
$this->dbClientFlags = $flags ? $flags : $conf['db_client_flags'];
$this->_iConnId = mysqli_init(); $this->_iConnId = mysqli_init();
mysqli_real_connect($this->_iConnId, $this->dbHost, $this->dbUser, $this->dbPass, '', (int)$this->dbPort, NULL, $this->dbClientFlags); mysqli_real_connect($this->_iConnId, $this->dbHost, $this->dbUser, $this->dbPass, '', (int)$this->dbPort, NULL, $this->dbClientFlags);
for($try=0;(!is_object($this->_iConnId) || mysqli_connect_error()) && $try < 5;++$try) { for($try=0;(!is_object($this->_iConnId) || mysqli_connect_errno()) && $try < 5;++$try) {
sleep($try); sleep($try);
mysqli_real_connect($this->_iConnId, $this->dbHost, $this->dbUser, $this->dbPass, '', (int)$this->dbPort, NULL, $this->dbClientFlags); if(!is_object($this->_iConnId)) {
$this->_iConnId = mysqli_init();
}
if(!mysqli_real_connect($this->_iConnId, $this->dbHost, $this->dbUser, $this->dbPass, '', (int)$this->dbPort, NULL, $this->dbClientFlags)) {
$this->_sqlerror('Database connection failed');
}
} }
if(!is_object($this->_iConnId) || mysqli_connect_error()) { if(!is_object($this->_iConnId) || mysqli_connect_errno()) {
$this->_iConnId = null; $this->_iConnId = null;
$this->_sqlerror('Zugriff auf Datenbankserver fehlgeschlagen! / Database server not accessible!', '', true); $this->_sqlerror('Zugriff auf Datenbankserver fehlgeschlagen! / Database server not accessible!', '', true); // sets errorMessage
return false; throw new Exception($this->errorMessage);
} }
if(!((bool)mysqli_query( $this->_iConnId, 'USE `' . $this->dbName . '`'))) { if(!((bool)mysqli_query( $this->_iConnId, 'USE `' . $this->dbName . '`'))) {
$this->close(); $this->close();
$this->_sqlerror('Datenbank nicht gefunden / Database not found', '', true); $this->_sqlerror('Datenbank nicht gefunden / Database not found', '', true); // sets errorMessage
return false; throw new Exception($this->errorMessage);
} }
$this->_setCharset(); $this->_setCharset();
...@@ -106,6 +116,18 @@ class db ...@@ -106,6 +116,18 @@ class db
$this->_iConnId = null; $this->_iConnId = null;
} }
/*
* Test mysql connection.
*
* @return boolean returns true if db connection is good.
*/
public function testConnection() {
if(mysqli_connect_errno()) {
return false;
}
return (boolean)(is_object($this->_iConnId) && mysqli_ping($this->_iConnId));
}
/* This allows our private variables to be "read" out side of the class */ /* This allows our private variables to be "read" out side of the class */
public function __get($var) { public function __get($var) {
return isset($this->$var) ? $this->$var : NULL; return isset($this->$var) ? $this->$var : NULL;
...@@ -134,6 +156,7 @@ class db ...@@ -134,6 +156,7 @@ class db
if($iPos2 !== false && ($iPos === false || $iPos2 <= $iPos)) { if($iPos2 !== false && ($iPos === false || $iPos2 <= $iPos)) {
$sTxt = $this->escape($sValue); $sTxt = $this->escape($sValue);
$sTxt = str_replace('`', '', $sTxt);
if(strpos($sTxt, '.') !== false) { if(strpos($sTxt, '.') !== false) {
$sTxt = preg_replace('/^(.+)\.(.+)$/', '`$1`.`$2`', $sTxt); $sTxt = preg_replace('/^(.+)\.(.+)$/', '`$1`.`$2`', $sTxt);
$sTxt = str_replace('.`*`', '.*', $sTxt); $sTxt = str_replace('.`*`', '.*', $sTxt);
...@@ -177,14 +200,65 @@ class db ...@@ -177,14 +200,65 @@ class db
* @access private * @access private
*/ */
private function _setCharset() { private function _setCharset() {
mysqli_query($this->_iConnId, 'SET NAMES '.$this->dbCharset); $this->query('SET NAMES '.$this->dbCharset);
mysqli_query($this->_iConnId, "SET character_set_results = '".$this->dbCharset."', character_set_client = '".$this->dbCharset."', character_set_connection = '".$this->dbCharset."', character_set_database = '".$this->dbCharset."', character_set_server = '".$this->dbCharset."'"); $this->query("SET character_set_results = '".$this->dbCharset."', character_set_client = '".$this->dbCharset."', character_set_connection = '".$this->dbCharset."', character_set_database = '".$this->dbCharset."', character_set_server = '".$this->dbCharset."'");
}
private function securityScan($string) {
global $app, $conf;
// get security config
if(isset($app)) {
$app->uses('getconf');
$ids_config = $app->getconf->get_security_config('ids');
if($ids_config['sql_scan_enabled'] == 'yes') {
// Remove whitespace
$string = trim($string);
if(substr($string,-1) == ';') $string = substr($string,0,-1);
// Save original string
$string_orig = $string;
//echo $string;
$chars = array(';', '#', '/*', '*/', '--', '\\\'', '\\"');
$string = str_replace('\\\\', '', $string);
$string = preg_replace('/(^|[^\\\])([\'"])\\2/is', '$1', $string);
$string = preg_replace('/(^|[^\\\])([\'"])(.*?[^\\\])\\2/is', '$1', $string);
$ok = true;
if(substr_count($string, "`") % 2 != 0 || substr_count($string, "'") % 2 != 0 || substr_count($string, '"') % 2 != 0) {
$app->log("SQL injection warning (" . $string_orig . ")",2);
$ok = false;
} else {
foreach($chars as $char) {
if(strpos($string, $char) !== false) {
$ok = false;
$app->log("SQL injection warning (" . $string_orig . ")",2);
break;
}
}
}
if($ok == true) {
return true;
} else {
if($ids_config['sql_scan_action'] == 'warn') {
// we return false in warning level.
return false;
} else {
// if sql action = 'block' or anything else then stop here.
$app->error('Possible SQL injection. All actions have been logged.');
}
}
}
}
} }
private function _query($sQuery = '') { private function _query($sQuery = '') {
global $app; global $app;
//if($this->isConnected == false) return false;
if ($sQuery == '') { if ($sQuery == '') {
$this->_sqlerror('Keine Anfrage angegeben / No query given'); $this->_sqlerror('Keine Anfrage angegeben / No query given');
return false; return false;
...@@ -193,10 +267,13 @@ class db ...@@ -193,10 +267,13 @@ class db
$try = 0; $try = 0;
do { do {
$try++; $try++;
$ok = mysqli_ping($this->_iConnId); $ok = (is_object($this->_iConnId)) ? mysqli_ping($this->_iConnId) : false;
if(!$ok) { if(!$ok) {
if(!mysqli_real_connect(mysqli_init(), $this->dbHost, $this->dbUser, $this->dbPass, $this->dbName, (int)$this->dbPort, NULL, $this->dbClientFlags)) { if(!is_object($this->_iConnId)) {
if($this->errorNumber == '111') { $this->_iConnId = mysqli_init();
}
if(!mysqli_real_connect($this->_isConnId, $this->dbHost, $this->dbUser, $this->dbPass, $this->dbName, (int)$this->dbPort, NULL, $this->dbClientFlags)) {
if(mysqli_connect_errno() == '111') {
// server is not available // server is not available
if($try > 9) { if($try > 9) {
if(isset($app) && isset($app->forceErrorExit)) { if(isset($app) && isset($app->forceErrorExit)) {
...@@ -208,7 +285,7 @@ class db ...@@ -208,7 +285,7 @@ class db
} }
if($try > 9) { if($try > 9) {
$this->_sqlerror('DB::query -> reconnect', '', true); $this->_sqlerror('db::_query -> reconnect', '', true);
return false; return false;
} else { } else {
sleep(($try > 7 ? 5 : 1)); sleep(($try > 7 ? 5 : 1));
...@@ -222,7 +299,7 @@ class db ...@@ -222,7 +299,7 @@ class db
$aArgs = func_get_args(); $aArgs = func_get_args();
$sQuery = call_user_func_array(array(&$this, '_build_query_string'), $aArgs); $sQuery = call_user_func_array(array(&$this, '_build_query_string'), $aArgs);
$this->securityScan($sQuery);
$this->_iQueryId = mysqli_query($this->_iConnId, $sQuery); $this->_iQueryId = mysqli_query($this->_iConnId, $sQuery);
if (!$this->_iQueryId) { if (!$this->_iQueryId) {
$this->_sqlerror('Falsche Anfrage / Wrong Query', 'SQL-Query = ' . $sQuery); $this->_sqlerror('Falsche Anfrage / Wrong Query', 'SQL-Query = ' . $sQuery);
...@@ -369,13 +446,14 @@ class db ...@@ -369,13 +446,14 @@ class db
* @return int id of last inserted row or 0 if none * @return int id of last inserted row or 0 if none
*/ */
public function insert_id() { public function insert_id() {
$iRes = mysqli_query($this->_iConnId, 'SELECT LAST_INSERT_ID() as `newid`'); $oResult = $this->query('SELECT LAST_INSERT_ID() as `newid`');
if(!is_object($iRes)) return false; if(!$oResult) {
$this->_sqlerror('Unable to select last_insert_id()');
$aReturn = mysqli_fetch_assoc($iRes); return false;
mysqli_free_result($iRes); }
$aReturn = $oResult->get();
return $aReturn['newid']; $oResult->free();
return isset($aReturn['newid']) ? $aReturn['newid'] : 0;
} }
...@@ -465,8 +543,13 @@ class db ...@@ -465,8 +543,13 @@ class db
private function _sqlerror($sErrormsg = 'Unbekannter Fehler', $sAddMsg = '', $bNoLog = false) { private function _sqlerror($sErrormsg = 'Unbekannter Fehler', $sAddMsg = '', $bNoLog = false) {
global $app, $conf; global $app, $conf;
$mysql_error = (is_object($this->_iConnId) ? mysqli_error($this->_iConnId) : mysqli_connect_error()); $mysql_errno = mysqli_connect_errno();
$mysql_errno = (is_object($this->_iConnId) ? mysqli_errno($this->_iConnId) : mysqli_connect_errno()); $mysql_error = mysqli_connect_error();
if ($mysql_errno === 0 && is_object($this->_iConnId)) {
$mysql_errno = mysqli_errno($this->_iConnId);
$mysql_error = mysqli_error($this->_iConnId);
}
$this->errorNumber = $mysql_error;
$this->errorMessage = $mysql_error; $this->errorMessage = $mysql_error;
//$sAddMsg .= getDebugBacktrace(); //$sAddMsg .= getDebugBacktrace();
...@@ -510,6 +593,26 @@ class db ...@@ -510,6 +593,26 @@ class db
return $out; return $out;
} }
public function insertFromArray($tablename, $data) {
if(!is_array($data)) return false;
$k_query = '';
$v_query = '';
$params = array($tablename);
$v_params = array();
foreach($data as $key => $value) {
$k_query .= ($k_query != '' ? ', ' : '') . '??';
$v_query .= ($v_query != '' ? ', ' : '') . '?';
$params[] = $key;
$v_params[] = $value;
}
$query = 'INSERT INTO ?? (' . $k_query . ') VALUES (' . $v_query . ')';
return $this->query($query, true, array_merge($params, $v_params));
}
public function diffrec($record_old, $record_new) { public function diffrec($record_old, $record_new) {
$diffrec_full = array(); $diffrec_full = array();
$diff_num = 0; $diff_num = 0;
...@@ -550,34 +653,35 @@ class db ...@@ -550,34 +653,35 @@ class db
* @param string $database_name * @param string $database_name
* @return int - database-size in bytes * @return int - database-size in bytes
*/ */
public function getDatabaseSize($database_name) { public function getDatabaseSize($database_name) {
global $app; global $app, $conf;
static $db=null;
include 'lib/mysql_clientdb.conf'; if ( ! $db ) {
$clientdb_host = ($conf['db_host']) ? $conf['db_host'] : NULL;
$clientdb_user = ($conf['db_user']) ? $conf['db_user'] : NULL;
$clientdb_password = ($conf['db_password']) ? $conf['db_password'] : NULL;
$clientdb_port = ((int)$conf['db_port']) ? (int)$conf['db_port'] : NULL;
$clientdb_flags = ($conf['db_flags'] !== NULL) ? $conf['db_flags'] : NULL;
/* Connect to the database */ require_once 'lib/mysql_clientdb.conf';
$link = mysqli_connect($clientdb_host, $clientdb_user, $clientdb_password);
if (!$link) { $db = new db($clientdb_host, $clientdb_user, $clientdb_password, NULL, $clientdb_port, $clientdb_flags);
$app->log('Unable to connect to the database'.mysqli_connect_error(), LOGLEVEL_DEBUG);
return;
} }
/* Get database-size from information_schema */ $result = $db->_query("SELECT SUM(data_length+index_length) FROM information_schema.TABLES WHERE table_schema='".$db->escape($database_name)."'");
$result = mysqli_query($link, "SELECT SUM(data_length+index_length) FROM information_schema.TABLES WHERE table_schema='".mysqli_real_escape_string($link, $database_name)."'");
if(!$result) { if(!$result) {
$app->log('Unable to get the database-size for ' . $database_name . ': '.mysqli_error($link), LOGLEVEL_DEBUG); $db->_sqlerror('Unable to determine the size of database ' . $database_name);
return; return;
} }
$database_size = mysqli_fetch_row($result); $database_size = $result->getAsRow();
mysqli_close($link); $result->free();
return $database_size[0]; return $database_size[0] ? $database_size[0] : 0;
} }
//** Function to fill the datalog with a full differential record. //** Function to fill the datalog with a full differential record.
public function datalogSave($db_table, $action, $primary_field, $primary_id, $record_old, $record_new, $force_update = false) { public function datalogSave($db_table, $action, $primary_field, $primary_id, $record_old, $record_new, $force_update = false) {
global $app, $conf; global $app;
// Insert backticks only for incomplete table names. // Insert backticks only for incomplete table names.
if(stristr($db_table, '.')) { if(stristr($db_table, '.')) {
...@@ -626,6 +730,10 @@ class db ...@@ -626,6 +730,10 @@ class db
public function datalogInsert($tablename, $insert_data, $index_field) { public function datalogInsert($tablename, $insert_data, $index_field) {
global $app; global $app;
// Check fields
if(!preg_match('/^[a-zA-Z0-9\-\_\.]{1,64}$/',$tablename)) $app->error('Invalid table name '.$tablename);
if(!preg_match('/^[a-zA-Z0-9\-\_]{1,64}$/',$index_field)) $app->error('Invalid index field '.$index_field.' in table '.$tablename);
if(is_array($insert_data)) { if(is_array($insert_data)) {
$key_str = ''; $key_str = '';
$val_str = ''; $val_str = '';
...@@ -661,6 +769,10 @@ class db ...@@ -661,6 +769,10 @@ class db
public function datalogUpdate($tablename, $update_data, $index_field, $index_value, $force_update = false) { public function datalogUpdate($tablename, $update_data, $index_field, $index_value, $force_update = false) {
global $app; global $app;
// Check fields
if(!preg_match('/^[a-zA-Z0-9\-\_\.]{1,64}$/',$tablename)) $app->error('Invalid table name '.$tablename);
if(!preg_match('/^[a-zA-Z0-9\-\_]{1,64}$/',$index_field)) $app->error('Invalid index field '.$index_field.' in table '.$tablename);
$old_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value); $old_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value);
if(is_array($update_data)) { if(is_array($update_data)) {
...@@ -692,6 +804,10 @@ class db ...@@ -692,6 +804,10 @@ class db
public function datalogDelete($tablename, $index_field, $index_value) { public function datalogDelete($tablename, $index_field, $index_value) {
global $app; global $app;
// Check fields
if(!preg_match('/^[a-zA-Z0-9\-\_\.]{1,64}$/',$tablename)) $app->error('Invalid table name '.$tablename);
if(!preg_match('/^[a-zA-Z0-9\-\_]{1,64}$/',$index_field)) $app->error('Invalid index field '.$index_field.' in table '.$tablename);
$old_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value); $old_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value);
$this->query("DELETE FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value); $this->query("DELETE FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value);
$new_rec = array(); $new_rec = array();
...@@ -709,6 +825,26 @@ class db ...@@ -709,6 +825,26 @@ class db
return true; return true;
} }
//* get the current datalog status for the specified login (or currently logged in user)
public function datalogStatus($login = '') {
global $app;
$return = array('count' => 0, 'entries' => array());
if($login == '' && isset($_SESSION['s']['user'])) {
$login = $_SESSION['s']['user']['username'];
}
$result = $this->queryAllRecords("SELECT COUNT( * ) AS cnt, sys_datalog.action, sys_datalog.dbtable FROM sys_datalog, server WHERE server.server_id = sys_datalog.server_id AND sys_datalog.user = ? AND sys_datalog.datalog_id > server.updated GROUP BY sys_datalog.dbtable, sys_datalog.action", $login);
foreach($result as $row) {
if(!$row['dbtable'] || in_array($row['dbtable'], array('aps_instances', 'aps_instances_settings', 'mail_access', 'mail_content_filter'))) continue; // ignore some entries, maybe more to come
$return['entries'][] = array('table' => $row['dbtable'], 'action' => $row['action'], 'count' => $row['cnt'], 'text' => $app->lng('datalog_status_' . $row['action'] . '_' . $row['dbtable'])); $return['count'] += $row['cnt'];
}
unset($result);
return $return;
}
public function freeResult($query) public function freeResult($query)
{ {
...@@ -839,10 +975,10 @@ class db ...@@ -839,10 +975,10 @@ class db
function tableInfo($table_name) { function tableInfo($table_name) {
global $go_api, $go_info; global $go_api, $go_info, $app;
// Tabellenfelder einlesen // Tabellenfelder einlesen
if($rows = $go_api->db->queryAllRecords('SHOW FIELDS FROM ??', $table_name)){ if($rows = $app->db->queryAllRecords('SHOW FIELDS FROM ??', $table_name)){
foreach($rows as $row) { foreach($rows as $row) {
$name = $row['Field']; $name = $row['Field'];
$default = $row['Default']; $default = $row['Default'];
...@@ -944,7 +1080,7 @@ class db ...@@ -944,7 +1080,7 @@ class db
return 'char'; return 'char';
break; break;
case 'varchar': case 'varchar':
if($typeValue < 1) die('Database failure: Lenght required for these data types.'); if($typeValue < 1) die('Database failure: Length required for these data types.');
return 'varchar('.$typeValue.')'; return 'varchar('.$typeValue.')';
break; break;
case 'text': case 'text':
...@@ -953,6 +1089,9 @@ class db ...@@ -953,6 +1089,9 @@ class db
case 'blob': case 'blob':
return 'blob'; return 'blob';
break; break;
case 'date':
return 'date';
break;
} }
} }
......
...@@ -244,12 +244,6 @@ class functions { ...@@ -244,12 +244,6 @@ class functions {
if(preg_match($regex, $result['ip'])) $ips[] = $result['ip']; 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'"); $results = $app->db->queryAllRecords("SELECT data AS ip FROM dns_rr WHERE type = 'A' OR type = 'AAAA'");
if(!empty($results) && is_array($results)){ if(!empty($results) && is_array($results)){
foreach($results as $result){ foreach($results as $result){
...@@ -303,6 +297,12 @@ class functions { ...@@ -303,6 +297,12 @@ class functions {
} }
} }
} }
$tmp_ips = $app->plugins->raiseAction('get_server_ips', 0, true);
if(is_array($tmp_ips) && !empty($tmp_ips)) {
$ips = array_merge($ips, $tmp_ips);
}
$ips = array_unique($ips); $ips = array_unique($ips);
sort($ips, SORT_NUMERIC); sort($ips, SORT_NUMERIC);
......
...@@ -430,6 +430,9 @@ class ispcmail { ...@@ -430,6 +430,9 @@ class ispcmail {
if($text == true && $html == false && $attach == false) { if($text == true && $html == false && $attach == false) {
// only text // only text
$content_type = 'text/plain; charset="' . strtolower($this->mail_charset) . '"'; $content_type = 'text/plain; charset="' . strtolower($this->mail_charset) . '"';
if($this->mail_charset == 'UTF-8') {
$this->headers['Content-Transfer-Encoding'] = '8bit';
}
$textonly = true; $textonly = true;
} elseif($text == true && $html == false && $attach == true) { } elseif($text == true && $html == false && $attach == true) {
// text and attachment // text and attachment
...@@ -440,6 +443,9 @@ class ispcmail { ...@@ -440,6 +443,9 @@ class ispcmail {
} elseif($html == true && $text == false && $attach == false) { } elseif($html == true && $text == false && $attach == false) {
// html only (or text too) // html only (or text too)
$content_type = 'text/html; charset="' . strtolower($this->mail_charset) . '"'; $content_type = 'text/html; charset="' . strtolower($this->mail_charset) . '"';
if($this->mail_charset == 'UTF-8') {
$this->headers['Content-Transfer-Encoding'] = '8bit';
}
$htmlonly = true; $htmlonly = true;
} elseif($html == true && $attach == true) { } elseif($html == true && $attach == true) {
// html and attachments // html and attachments
...@@ -564,17 +570,14 @@ class ispcmail { ...@@ -564,17 +570,14 @@ class ispcmail {
* @access private * @access private
*/ */
private function _encodeSubject($input, $charset = 'ISO-8859-1') { private function _encodeSubject($input, $charset = 'ISO-8859-1') {
/* if(preg_match('/(?:[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})/s', $input)) {
if($charset == 'UTF-8' && function_exists('imap_8bit')) { // needs encoding
$input = "=?utf-8?Q?" . imap_8bit($input) . "?="; if(function_exists('imap_8bit')) {
$input = "=?utf-8?Q?" . str_replace("?","=3F", imap_8bit($input)) . "?=";
} else { } else {
preg_match_all('/(\s?\w*[\x80-\xFF]+\w*\s?)/', $input, $matches); $input = '=?utf-8?B?' . base64_encode($input) . '?=';
foreach ($matches[1] as $value) { }
$replacement = preg_replace('/([\x20\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
$input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
} }
}*/
$input='=?UTF-8?B?'.base64_encode($input).'?=';
return $input; return $input;
} }
...@@ -598,6 +601,10 @@ class ispcmail { ...@@ -598,6 +601,10 @@ class ispcmail {
if($this->smtp_crypt == 'tls') { if($this->smtp_crypt == 'tls') {
fputs($this->_smtp_conn, 'STARTTLS' . $this->_crlf); fputs($this->_smtp_conn, 'STARTTLS' . $this->_crlf);
fgets($this->_smtp_conn, 515); fgets($this->_smtp_conn, 515);
stream_context_set_option($this->_smtp_conn, 'ssl', 'verify_host', false);
stream_context_set_option($this->_smtp_conn, 'ssl', 'verify_peer', false);
stream_context_set_option($this->_smtp_conn, 'ssl', 'allow_self_signed', true);
stream_socket_enable_crypto($this->_smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); stream_socket_enable_crypto($this->_smtp_conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
} }
...@@ -740,32 +747,22 @@ class ispcmail { ...@@ -740,32 +747,22 @@ class ispcmail {
} }
if($this->use_smtp == true) { if($this->use_smtp == true) {
if(!$this->_logged_in || !$this->_smtp_conn) {
$result = $this->_smtp_login();
if(!$result) return false;
}
$bcc_cc_sent = false; $bcc_cc_sent = false;
foreach($recipients as $recipname => $recip) { foreach($recipients as $recipname => $recip) {
if($this->_sent_mails >= $this->smtp_max_mails) { // Build mail headers, content etc.
// close connection to smtp and reconnect
$this->_sent_mails = 0; $m_recipients = array();
$this->_smtp_close(); $m_mail_content = '';
$result = $this->_smtp_login();
if(!$result) return false;
}
$this->_sent_mails += 1;
$recipname = trim(str_replace('"', '', $recipname)); $recipname = trim(str_replace('"', '', $recipname));
$recip = $this->_encodeHeader($recip, $this->mail_charset); $recip = $this->_encodeHeader($recip, $this->mail_charset);
$recipname = $this->_encodeHeader($recipname, $this->mail_charset); $recipname = $this->_encodeHeader($recipname, $this->mail_charset);
//Email From //Email From
fputs($this->_smtp_conn, 'MAIL FROM: <' . $this->_mail_sender . '>' . $this->_crlf); $m_from = $this->_mail_sender;
$response = fgets($this->_smtp_conn, 515);
//Email To //Email To
fputs($this->_smtp_conn, 'RCPT TO: <' . $recip . '>' . $this->_crlf); $m_recipients[] = $recip;
$response = fgets($this->_smtp_conn, 515);
if($bcc_cc_sent == false) { if($bcc_cc_sent == false) {
$add_recips = array(); $add_recips = array();
...@@ -773,17 +770,12 @@ class ispcmail { ...@@ -773,17 +770,12 @@ class ispcmail {
if($this->getHeader('Bcc') != '') $add_recips = array_merge($add_recips, $this->_extract_names($this->getHeader('Bcc'))); if($this->getHeader('Bcc') != '') $add_recips = array_merge($add_recips, $this->_extract_names($this->getHeader('Bcc')));
foreach($add_recips as $add_recip) { foreach($add_recips as $add_recip) {
if(!$add_recip['mail']) continue; if(!$add_recip['mail']) continue;
fputs($this->_smtp_conn, 'RCPT TO: <' . $this->_encodeHeader($add_recip['mail'], $this->mail_charset) . '>' . $this->_crlf); $m_recipients[] = $this->_encodeHeader($add_recip['mail'], $this->mail_charset);
$response = fgets($this->_smtp_conn, 515);
} }
unset($add_recips); unset($add_recips);
$bcc_cc_sent = true; $bcc_cc_sent = true;
} }
//The Email
fputs($this->_smtp_conn, 'DATA' . $this->_crlf);
$response = fgets($this->_smtp_conn, 515);
//Construct Headers //Construct Headers
if($recipname && !is_numeric($recipname)) $this->setHeader('To', $recipname . ' <' . $recip . '>'); if($recipname && !is_numeric($recipname)) $this->setHeader('To', $recipname . ' <' . $recip . '>');
else $this->setHeader('To', $recip); else $this->setHeader('To', $recip);
...@@ -793,10 +785,32 @@ class ispcmail { ...@@ -793,10 +785,32 @@ class ispcmail {
if($this->getHeader('Cc') != '') $mail_content .= 'Cc: ' . $this->_encodeHeader($this->getHeader('Cc'), $this->mail_charset) . $this->_crlf; if($this->getHeader('Cc') != '') $mail_content .= 'Cc: ' . $this->_encodeHeader($this->getHeader('Cc'), $this->mail_charset) . $this->_crlf;
$mail_content .= implode($this->_crlf, $headers) . $this->_crlf . ($this->_is_signed == false ? $this->_crlf : '') . $this->body; $mail_content .= implode($this->_crlf, $headers) . $this->_crlf . ($this->_is_signed == false ? $this->_crlf : '') . $this->body;
fputs($this->_smtp_conn, $mail_content . $this->_crlf . '.' . $this->_crlf); $m_mail_content = $mail_content;
$response = fgets($this->_smtp_conn, 515);
// Attempt SMTP login:
if(!$this->_logged_in || !$this->_smtp_conn) {
$result = $this->_smtp_login();
}
if($this->_sent_mails >= $this->smtp_max_mails) {
// close connection to smtp and reconnect
$this->_sent_mails = 0;
$this->_smtp_close();
$result = $this->_smtp_login();
}
$this->_sent_mails += 1;
// Send mail or queue it
if ($result) {
$this->send_smtp($m_from, $m_recipients, $m_mail_content);
} else {
$this->add_to_queue($m_from, $m_recipients, $m_mail_content);
}
// hopefully message was correctly sent or queued now
// hopefully message was correctly sent now
$result = true; $result = true;
} }
} else { } else {
...@@ -824,7 +838,87 @@ class ispcmail { ...@@ -824,7 +838,87 @@ class ispcmail {
return $result; return $result;
} }
/**
* Send all mails in queue (usually called from cron)
*/
public function send_queue() {
global $app;
$mails = $app->db->queryAllRecords('SELECT * FROM sys_mailqueue');
if (is_array($mails)) {
foreach ($mails as $mail) {
// Open SMTP connections if not open:
if(!$this->_logged_in || !$this->_smtp_conn) {
$result = $this->_smtp_login();
}
if($this->_sent_mails >= $this->smtp_max_mails) {
// close connection to smtp and reconnect
$this->_sent_mails = 0;
$this->_smtp_close();
$result = $this->_smtp_login();
}
$this->_sent_mails += 1;
if (!$result) {
return false;
}
// Send mail:
$id = $mail['id'];
$from = $mail['from_address'];
$recipients = explode("\n", $mail['recipients']);
$mail_content = $mail['mail_content'];
$this->send_smtp($from, $recipients, $mail_content);
// Delete from queue:
$app->db->query('DELETE FROM sys_mailqueue WHERE id = ?', $id);
}
}
}
/**
* Send mail via SMTP
*/
private function send_smtp($from, $recipients, $mail_content) {
// from:
fputs($this->_smtp_conn, 'MAIL FROM: <' . $from . '>' . $this->_crlf);
$response = fgets($this->_smtp_conn, 515);
// recipients (To, Cc or Bcc):
foreach ($recipients as $recipient) {
fputs($this->_smtp_conn, 'RCPT TO: <' . $recipient . '>' . $this->_crlf);
$response = fgets($this->_smtp_conn, 515);
}
// mail content:
fputs($this->_smtp_conn, 'DATA' . $this->_crlf);
$response = fgets($this->_smtp_conn, 515);
fputs($this->_smtp_conn, $mail_content . $this->_crlf . '.' . $this->_crlf);
$response = fgets($this->_smtp_conn, 515);
// hopefully message was correctly sent now
$result = true;
return $result;
}
/**
* Add mail to queue for sending later
*/
private function add_to_queue($from, $recipients, $mail_content) {
global $app;
$app->db->query('INSERT INTO `sys_mailqueue` (`from_address`, `recipients`, `mail_content`) VALUES (?,?,?)', $from, implode("\n", $recipients), $mail_content);
}
/** /**
* Close mail connections * Close mail connections
......
<?php
/**
* Base class for app installer
*
* @author Marius Burkard
*/
class ispconfig_addon_installer_base {
protected $addon_name;
protected $addon_ident;
protected $addon_version;
protected $temp_dir;
public function __construct() {
$this->addon_ident = preg_replace('/_addon_installer$/', '', get_called_class());
}
public function setAddonName($name) {
$this->addon_name = $name;
}
public function setAddonIdent($ident) {
$this->addon_ident = $ident;
}
public function setAddonVersion($version) {
$this->addon_version = $version;
}
public function setAddonTempDir($path) {
$this->temp_dir = $path;
}
protected function copyInterfaceFiles() {
global $app, $conf;
$app->log('Copying interface files.', 0, false);
$install_dir = realpath($conf['rootpath'] . '/..');
if(is_dir($this->temp_dir . '/interface')) {
$ret = null;
$retval = 0;
$command = 'cp -rf ' . escapeshellarg($this->temp_dir . '/interface') . ' ' . escapeshellarg($install_dir . '/');
exec($command, $ret, $retval);
if($retval != 0) {
$app->log('Copying interface files failed.', 2, false);
throw new AddonInstallerException('Command ' . $command . ' failed with code ' . $retval);
}
return true;
} else {
return false;
}
}
protected function copyServerFiles() {
global $app, $conf;
$app->log('Copying server files.', 0, false);
$install_dir = realpath($conf['rootpath'] . '/..');
if(is_dir($this->temp_dir . '/server')) {
$ret = null;
$retval = 0;
$command = 'cp -rf ' . escapeshellarg($this->temp_dir . '/server'). ' ' . escapeshellarg($install_dir . '/');
exec($command, $ret, $retval);
if($retval != 0) {
$app->log('Copying interface files failed.', 2, false);
throw new AddonInstallerException('Command ' . $command . ' failed with code ' . $retval);
}
return true;
} else {
return false;
}
}
protected function copyAddonFiles() {
global $app, $conf;
$app->log('Copying addon files.', 0, false);
$install_dir = realpath($conf['rootpath'] . '/..') . '/addons/' . $this->addon_ident;
if(!is_dir($install_dir)) {
if(!$app->system->mkdir($install_dir, false, 0750, true)) {
$app->log('Addons dir missing and could not be created.', 2, false);
throw new AddonInstallerException('Could not create addons dir ' . $install_dir);
}
}
if(is_dir($this->temp_dir . '/install')) {
$ret = null;
$retval = 0;
$command = 'cp -rf ' . escapeshellarg($this->temp_dir . '/addon.ini') . ' ' . escapeshellarg($this->temp_dir . '/' . $this->addon_ident . '.addon.php') . ' ' . escapeshellarg($this->temp_dir . '/install'). ' ' . escapeshellarg($install_dir . '/');
exec($command, $ret, $retval);
if($retval != 0) {
/* TODO: logging */
$app->log('Warning or error on copying addon files. Returncode of command ' . $command . ' was: ' . $retval .'.', 0, false);
}
return true;
} else {
return false;
}
}
protected function executeSqlStatements() {
global $app, $conf;
$incremental = false;
$check = $app->db->queryOneRecord('SELECT `db_version` FROM `addons` WHERE `addon_ident` = ?', $this->addon_ident);
if($check) {
$incremental = 0;
if($check['db_version']) {
$incremental = $check['db_version'];
$app->log('Current db version is ' . $incremental . '.', 0, false);
}
}
include '/usr/local/ispconfig/server/lib/mysql_clientdb.conf';
$app->log('Adding addon entry to db.', 0, false);
// create addon entry if not existing
$qry = 'INSERT IGNORE INTO `addons` (`addon_ident`, `addon_version`, `addon_name`, `db_version`) VALUES (?, ?, ?, ?)';
$app->db->query($qry, $this->addon_ident, $this->addon_version, $this->addon_name, 0);
$mysql_command = 'mysql --default-character-set=' . escapeshellarg($conf['db_charset']) . ' --force -h ' . escapeshellarg($clientdb_host) . ' -u ' . escapeshellarg($clientdb_user) . ' -p' . escapeshellarg($clientdb_password) . ' -P ' . escapeshellarg($clientdb_port) . ' -D ' . escapeshellarg($conf['db_database']);
if($incremental === false) {
$sql_file = $this->temp_dir . '/install/sql/' . $this->addon_ident . '.sql';
if(is_file($sql_file)) {
$ret = null;
$retval = 0;
$app->log('Loading ' . $sql_file . ' into db.', 0, false);
exec($mysql_command . ' < ' . escapeshellarg($sql_file), $ret, $retval);
if($retval != 0) {
/* TODO: log error! */
$app->log('Loading ' . $sql_file . ' into db failed.', 1, false);
}
}
} else {
$new_db_version = $incremental;
while(true) {
$sql_file = $this->temp_dir . '/install/sql/incremental/upd_' . str_pad($new_db_version + 1, '0', 5, STR_PAD_LEFT) . '.sql';
if(!is_file($sql_file)) {
break;
} else {
$ret = null;
$retval = 0;
$app->log('Loading ' . $sql_file . ' into db.', 0, false);
exec($mysql_command . ' < ' . escapeshellarg($sql_file), $ret, $retval);
if($retval != 0) {
/* TODO: log error! */
$app->log('Loading ' . $sql_file . ' into db failed.', 1, false);
}
}
$new_db_version++;
}
$app->db->query('UPDATE `addons` SET `addon_version` = ?, `db_version` = ? WHERE `addon_ident` = ?', $this->addon_version, $new_db_version, $this->addon_ident);
$app->log('Db version of addon is now ' . $new_db_version . '.', 0, false);
}
return true;
}
public function onBeforeInstall() { }
public function onInstall() {
global $app;
$app->log('Running onInstall()', 0, false);
$this->copyAddonFiles();
$this->copyInterfaceFiles();
$this->copyServerFiles();
$this->executeSqlStatements();
$app->log('Finished onInstall()', 0, false);
}
public function onAfterInstall() { }
public function onBeforeUpdate() { }
public function onUpdate() {
global $app;
$app->log('Running onUpdate()', 0, false);
$this->copyAddonFiles();
$this->copyInterfaceFiles();
$this->copyServerFiles();
$this->executeSqlStatements();
$app->log('Finished onUpdate()', 0, false);
}
public function onAfterUpdate() { }
public function onRaisedInstallerEvent($event_name, $data = false) {
}
}
class AddonInstallerException extends Exception {
public function __construct($message = "", $code = 0, $previous = null) {
parent::__construct($message, $code, $previous);
}
}
class AddonInstallerValidationException extends AddonInstallerException { }
\ No newline at end of file
...@@ -43,9 +43,117 @@ class letsencrypt { ...@@ -43,9 +43,117 @@ class letsencrypt {
} }
public function get_acme_script() {
$acme = explode("\n", shell_exec('which /usr/local/ispconfig/server/scripts/acme.sh /root/.acme.sh/acme.sh'));
$acme = reset($acme);
if(is_executable($acme)) {
return $acme;
} else {
return false;
}
}
public function get_acme_command($domains, $key_file, $bundle_file, $cert_file) {
$letsencrypt = $this->get_acme_script();
$cmd = '';
// generate cli format
foreach($domains as $domain) {
$cmd .= (string) " -d " . $domain;
}
if($cmd == '') {
return false;
}
$cmd = 'R=0 ; C=0 ; ' . $letsencrypt . ' --issue ' . $cmd . ' -w /usr/local/ispconfig/interface/acme ; R=$? ; if [[ $R -eq 0 || $R -eq 2 ]] ; then ' . $letsencrypt . ' --install-cert ' . $cmd . ' --key-file ' . escapeshellarg($key_file) . ' --fullchain-file ' . escapeshellarg($bundle_file) . ' --cert-file ' . escapeshellarg($cert_file) . ' --reloadcmd ' . escapeshellarg($this->get_reload_command()) . '; C=$? ; fi ; if [[ $C -eq 0 ]] ; then exit $R ; else exit $C ; fi';
return $cmd;
}
public function get_certbot_script() {
$letsencrypt = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot'));
$letsencrypt = reset($letsencrypt);
if(is_executable($letsencrypt)) {
return $letsencrypt;
} else {
return false;
}
}
private function install_acme() {
$install_cmd = 'wget -O - https://get.acme.sh | sh';
$ret = null;
$val = 0;
exec($install_cmd . ' 2>&1', $ret, $val);
return ($val == 0 ? true : false);
}
private function get_reload_command() {
global $app, $conf;
$web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
$daemon = '';
switch ($web_config['server_type']) {
case 'nginx':
$daemon = $web_config['server_type'];
break;
default:
if(is_file($conf['init_scripts'] . '/' . 'httpd24-httpd') || is_dir('/opt/rh/httpd24/root/etc/httpd')) {
$daemon = 'httpd24-httpd';
} elseif(is_file($conf['init_scripts'] . '/' . 'httpd') || is_dir('/etc/httpd')) {
$daemon = 'httpd';
} else {
$daemon = 'apache2';
}
}
$cmd = $app->system->getinitcommand($daemon, 'force-reload');
return $cmd;
}
public function get_certbot_command($domains) {
$letsencrypt = $this->get_certbot_script();
$cmd = '';
// generate cli format
foreach($domains as $domain) {
$cmd .= (string) " --domains " . $domain;
}
if($cmd == '') {
return false;
}
$matches = array();
$ret = null;
$val = 0;
$letsencrypt_version = exec($letsencrypt . ' --version 2>&1', $ret, $val);
if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $letsencrypt_version, $matches)) {
$letsencrypt_version = $matches[2];
}
if (version_compare($letsencrypt_version, '0.22', '>=')) {
$acme_version = 'https://acme-v02.api.letsencrypt.org/directory';
} else {
$acme_version = 'https://acme-v01.api.letsencrypt.org/directory';
}
$cmd = $letsencrypt . " certonly -n --text --agree-tos --expand --authenticator webroot --server $acme_version --rsa-key-size 4096 --email postmaster@$domain $cmd --webroot-path /usr/local/ispconfig/interface/acme";
return $cmd;
}
public function get_letsencrypt_certificate_paths($domains = array()) { public function get_letsencrypt_certificate_paths($domains = array()) {
global $app; global $app;
if($this->get_acme_script()) {
return false;
}
if(empty($domains)) return false; if(empty($domains)) return false;
if(!is_dir($this->renew_config_path)) return false; if(!is_dir($this->renew_config_path)) return false;
...@@ -133,7 +241,10 @@ class letsencrypt { ...@@ -133,7 +241,10 @@ class letsencrypt {
} }
private function get_ssl_domain($data) { private function get_ssl_domain($data) {
global $app;
$domain = $data['new']['ssl_domain']; $domain = $data['new']['ssl_domain'];
if(!$domain) $domain = $data['new']['domain']; if(!$domain) $domain = $data['new']['domain'];
if($data['new']['ssl'] == 'y' && $data['new']['ssl_letsencrypt'] == 'y') { if($data['new']['ssl'] == 'y' && $data['new']['ssl_letsencrypt'] == 'y') {
...@@ -149,8 +260,6 @@ class letsencrypt { ...@@ -149,8 +260,6 @@ class letsencrypt {
} }
public function get_website_certificate_paths($data) { public function get_website_certificate_paths($data) {
global $app;
$ssl_dir = $data['new']['document_root'].'/ssl'; $ssl_dir = $data['new']['document_root'].'/ssl';
$domain = $this->get_ssl_domain($data); $domain = $this->get_ssl_domain($data);
...@@ -183,11 +292,17 @@ class letsencrypt { ...@@ -183,11 +292,17 @@ class letsencrypt {
$web_config = $app->getconf->get_server_config($conf['server_id'], 'web'); $web_config = $app->getconf->get_server_config($conf['server_id'], 'web');
$server_config = $app->getconf->get_server_config($conf['server_id'], 'server'); $server_config = $app->getconf->get_server_config($conf['server_id'], 'server');
$use_acme = false;
if($this->get_acme_script()) {
$use_acme = true;
} elseif(!$this->get_certbot_script()) {
// acme and le missing
$this->install_acme();
}
$tmp = $app->letsencrypt->get_website_certificate_paths($data); $tmp = $app->letsencrypt->get_website_certificate_paths($data);
$domain = $tmp['domain']; $domain = $tmp['domain'];
$key_file = $tmp['key']; $key_file = $tmp['key'];
$key_file2 = $tmp['key2'];
$csr_file = $tmp['csr'];
$crt_file = $tmp['crt']; $crt_file = $tmp['crt'];
$bundle_file = $tmp['bundle']; $bundle_file = $tmp['bundle'];
...@@ -256,40 +371,39 @@ class letsencrypt { ...@@ -256,40 +371,39 @@ class letsencrypt {
$app->log("There were " . $le_domain_count . " domains in the domain list. LE only supports 100, so we strip the rest.", LOGLEVEL_WARN); $app->log("There were " . $le_domain_count . " domains in the domain list. LE only supports 100, so we strip the rest.", LOGLEVEL_WARN);
} }
// generate cli format
foreach($temp_domains as $temp_domain) {
$cli_domain_arg .= (string) " --domains " . $temp_domain;
}
// unset useless data // unset useless data
unset($subdomains); unset($subdomains);
unset($aliasdomains); unset($aliasdomains);
$letsencrypt_cmd = ''; $letsencrypt_cmd = '';
$allow_return_codes = null;
if($use_acme) {
$letsencrypt_cmd = $this->get_acme_command($temp_domains, $key_file, $bundle_file, $crt_file);
$allow_return_codes = array(2);
} else {
$letsencrypt_cmd = $this->get_certbot_command($temp_domains);
}
$success = false; $success = false;
if(!empty($cli_domain_arg)) { if($letsencrypt_cmd) {
if(!isset($server_config['migration_mode']) || $server_config['migration_mode'] != 'y') { if(!isset($server_config['migration_mode']) || $server_config['migration_mode'] != 'y') {
$app->log("Create Let's Encrypt SSL Cert for: $domain", LOGLEVEL_DEBUG); $app->log("Create Let's Encrypt SSL Cert for: $domain", LOGLEVEL_DEBUG);
$app->log("Let's Encrypt SSL Cert domains: $cli_domain_arg", LOGLEVEL_DEBUG); $app->log("Let's Encrypt SSL Cert domains: $cli_domain_arg", LOGLEVEL_DEBUG);
$letsencrypt = explode("\n", shell_exec('which letsencrypt certbot /root/.local/share/letsencrypt/bin/letsencrypt /opt/eff.org/certbot/venv/bin/certbot')); $success = $app->system->_exec($letsencrypt_cmd, $allow_return_codes);
$letsencrypt = reset($letsencrypt);
if(is_executable($letsencrypt)) {
$letsencrypt_version = exec($letsencrypt . ' --version 2>&1', $ret, $val);
if(preg_match('/^(\S+|\w+)\s+(\d+(\.\d+)+)$/', $letsencrypt_version, $matches)) {
$letsencrypt_version = $matches[2];
}
if ($letsencrypt_version >=0.22) {
$acme_version = 'https://acme-v02.api.letsencrypt.org/directory';
} else { } else {
$acme_version = 'https://acme-v01.api.letsencrypt.org/directory'; $app->log("Migration mode active, skipping Let's Encrypt SSL Cert creation for: $domain", LOGLEVEL_DEBUG);
$success = true;
} }
$letsencrypt_cmd = $letsencrypt . " certonly -n --text --agree-tos --expand --authenticator webroot --server $acme_version --rsa-key-size 4096 --email postmaster@$domain $cli_domain_arg --webroot-path /usr/local/ispconfig/interface/acme";
$success = $app->system->_exec($letsencrypt_cmd);
} }
if($use_acme === true) {
if(!$success) {
$app->log('Let\'s Encrypt SSL Cert for: ' . $domain . ' could not be issued.', LOGLEVEL_WARN);
$app->log($letsencrypt_cmd, LOGLEVEL_WARN);
return false;
} else { } else {
$app->log("Migration mode active, skipping Let's Encrypt SSL Cert creation for: $domain", LOGLEVEL_DEBUG); return true;
$success = true;
} }
} }
...@@ -369,4 +483,3 @@ class letsencrypt { ...@@ -369,4 +483,3 @@ class letsencrypt {
} }
} }
?>
...@@ -41,6 +41,7 @@ class monitor_tools { ...@@ -41,6 +41,7 @@ class monitor_tools {
$distver = ''; $distver = '';
$distid = ''; $distid = '';
$distbaseid = ''; $distbaseid = '';
$distsupported = false;
//** Debian or Ubuntu //** Debian or Ubuntu
if(file_exists('/etc/debian_version')) { if(file_exists('/etc/debian_version')) {
...@@ -85,27 +86,35 @@ class monitor_tools { ...@@ -85,27 +86,35 @@ class monitor_tools {
$ver = explode(" ", $ver, 2); $ver = explode(" ", $ver, 2);
$ver = reset($ver); $ver = reset($ver);
$mainver = $ver; $mainver = $ver;
$mainver = explode('.', $ver);
$mainver = array_filter($mainver);
$mainver = current($mainver).'.'.next($mainver);
} }
switch ($mainver){ switch ($mainver){
case "18.04": case "18.04":
$relname = "(Bionic Beaver)"; $relname = "(Bionic Beaver)";
$distconfid = 'ubuntu1804'; $distconfid = 'ubuntu1804';
$distsupported = true;
break; break;
case "17.10": case "17.10":
$relname = "(Artful Aardvark)"; $relname = "(Artful Aardvark)";
$distconfid = 'ubuntu1710'; $distconfid = 'ubuntu1710';
$distsupported = true;
break; break;
case "17.04": case "17.04":
$relname = "(Zesty Zapus)"; $relname = "(Zesty Zapus)";
$distconfid = 'ubuntu1604'; $distconfid = 'ubuntu1604';
$distsupported = true;
break; break;
case "16.10": case "16.10":
$relname = "(Yakkety Yak)"; $relname = "(Yakkety Yak)";
$distconfid = 'ubuntu1604'; $distconfid = 'ubuntu1604';
$distsupported = true;
break; break;
case "16.04": case "16.04":
$relname = "(Xenial Xerus)"; $relname = "(Xenial Xerus)";
$distconfid = 'ubuntu1604'; $distconfid = 'ubuntu1604';
$distsupported = true;
break; break;
case "15.10": case "15.10":
$relname = "(Wily Werewolf)"; $relname = "(Wily Werewolf)";
...@@ -178,6 +187,7 @@ class monitor_tools { ...@@ -178,6 +187,7 @@ class monitor_tools {
break; break;
default: default:
$relname = "UNKNOWN"; $relname = "UNKNOWN";
$distconfid = 'ubuntu1604';
} }
$distver = $ver.$lts." ".$relname; $distver = $ver.$lts." ".$relname;
} elseif(trim(file_get_contents('/etc/debian_version')) == '4.0') { } elseif(trim(file_get_contents('/etc/debian_version')) == '4.0') {
...@@ -205,23 +215,28 @@ class monitor_tools { ...@@ -205,23 +215,28 @@ class monitor_tools {
$distver = 'Jessie'; $distver = 'Jessie';
$distid = 'debian60'; $distid = 'debian60';
$distbaseid = 'debian'; $distbaseid = 'debian';
$distsupported = true;
} elseif(strstr(trim(file_get_contents('/etc/debian_version')), '9') || substr(trim(file_get_contents('/etc/debian_version')),0,1) == '9') { } elseif(strstr(trim(file_get_contents('/etc/debian_version')), '9') || substr(trim(file_get_contents('/etc/debian_version')),0,1) == '9') {
$distname = 'Debian'; $distname = 'Debian';
$distver = 'Stretch'; $distver = 'Stretch';
$distconfid = 'debian90'; $distconfid = 'debian90';
$distid = 'debian60'; $distid = 'debian60';
$distbaseid = 'debian'; $distbaseid = 'debian';
$distsupported = true;
} elseif(strstr(trim(file_get_contents('/etc/debian_version')), '/sid')) { } elseif(strstr(trim(file_get_contents('/etc/debian_version')), '/sid')) {
$distname = 'Debian'; $distname = 'Debian';
$distver = 'Testing'; $distver = 'Testing';
$distid = 'debian60'; $distid = 'debian60';
$distconfid = 'debiantesting'; $distconfid = 'debiantesting';
$distbaseid = 'debian'; $distbaseid = 'debian';
$distsupported = true;
} else { } else {
$distname = 'Debian'; $distname = 'Debian';
$distver = 'Unknown'; $distver = 'Unknown';
$distid = 'debian40'; $distid = 'debian60';
$distconfid = 'debian90';
$distbaseid = 'debian'; $distbaseid = 'debian';
$distsupported = true;
} }
} }
...@@ -234,9 +249,10 @@ class monitor_tools { ...@@ -234,9 +249,10 @@ class monitor_tools {
$distbaseid = 'debian'; $distbaseid = 'debian';
} elseif(false !== strpos(trim(file_get_contents('/etc/devuan_version')), 'ceres')) { } elseif(false !== strpos(trim(file_get_contents('/etc/devuan_version')), 'ceres')) {
$distname = 'Devuan'; $distname = 'Devuan';
$distver = 'Testing'; $distver = 'Ceres';
$distid = 'debiantesting'; $distid = 'debiantesting';
$distbaseid = 'debian'; $distbaseid = 'debian';
$distsupported = true;
} }
} }
...@@ -257,11 +273,13 @@ class monitor_tools { ...@@ -257,11 +273,13 @@ class monitor_tools {
$distver = '11.2'; $distver = '11.2';
$distid = 'opensuse112'; $distid = 'opensuse112';
$distbaseid = 'opensuse'; $distbaseid = 'opensuse';
$distsupported = true;
} else { } else {
$distname = 'openSUSE'; $distname = 'openSUSE';
$distver = 'Unknown'; $distver = 'Unknown';
$distid = 'opensuse112'; $distid = 'opensuse112';
$distbaseid = 'opensuse'; $distbaseid = 'opensuse';
$distsupported = true;
} }
} }
...@@ -291,6 +309,7 @@ class monitor_tools { ...@@ -291,6 +309,7 @@ class monitor_tools {
$distver = '5.2'; $distver = '5.2';
$distid = 'centos52'; $distid = 'centos52';
$distbaseid = 'fedora'; $distbaseid = 'fedora';
$distsupported = true;
} elseif(stristr($content, 'CentOS release 5.3 (Final)')) { } elseif(stristr($content, 'CentOS release 5.3 (Final)')) {
$distname = 'CentOS'; $distname = 'CentOS';
$distver = '5.3'; $distver = '5.3';
...@@ -298,12 +317,12 @@ class monitor_tools { ...@@ -298,12 +317,12 @@ class monitor_tools {
$distbaseid = 'fedora'; $distbaseid = 'fedora';
} elseif(stristr($content, 'CentOS release 5')) { } elseif(stristr($content, 'CentOS release 5')) {
$distname = 'CentOS'; $distname = 'CentOS';
$distver = '5'; $distver = 'Unknown';
$distid = 'centos53'; $distid = 'centos53';
$distbaseid = 'fedora'; $distbaseid = 'fedora';
} elseif(stristr($content, 'CentOS Linux release 6') || stristr($content, 'CentOS release 6')) { } elseif(stristr($content, 'CentOS Linux release 6') || stristr($content, 'CentOS release 6')) {
$distname = 'CentOS'; $distname = 'CentOS';
$distver = '6'; $distver = 'Unknown';
$distid = 'centos53'; $distid = 'centos53';
$distbaseid = 'fedora'; $distbaseid = 'fedora';
} elseif(stristr($content, 'CentOS Linux release 7')) { } elseif(stristr($content, 'CentOS Linux release 7')) {
...@@ -313,6 +332,8 @@ class monitor_tools { ...@@ -313,6 +332,8 @@ class monitor_tools {
$var=explode(" ", $content); $var=explode(" ", $content);
$var=explode(".", $var[3]); $var=explode(".", $var[3]);
$var=$var[0].".".$var[1]; $var=$var[0].".".$var[1];
$distver = $var;
$distsupported = true;
if($var=='7.0' || $var=='7.1') { if($var=='7.0' || $var=='7.1') {
$distid = 'centos70'; $distid = 'centos70';
} else { } else {
...@@ -336,7 +357,7 @@ class monitor_tools { ...@@ -336,7 +357,7 @@ class monitor_tools {
$distver = $version[0][0].$version[0][1]; $distver = $version[0][0].$version[0][1];
$distid = 'gentoo'; $distid = 'gentoo';
$distbaseid = 'gentoo'; $distbaseid = 'gentoo';
$distsupported = true;
} else { } else {
die('Unrecognized GNU/Linux distribution'); die('Unrecognized GNU/Linux distribution');
} }
...@@ -344,7 +365,7 @@ class monitor_tools { ...@@ -344,7 +365,7 @@ class monitor_tools {
// Set $distconfid to distid, if no different id for the config is defined // Set $distconfid to distid, if no different id for the config is defined
if(!isset($distconfid)) $distconfid = $distid; if(!isset($distconfid)) $distconfid = $distid;
return array('name' => $distname, 'version' => $distver, 'id' => $distid, 'confid' => $distconfid, 'baseid' => $distbaseid); return array('name' => $distname, 'version' => $distver, 'id' => $distid, 'confid' => $distconfid, 'baseid' => $distbaseid, 'supported' => $distsupported);
} }
// this function remains in the tools class, because it is used by cron AND rescue // this function remains in the tools class, because it is used by cron AND rescue
...@@ -454,16 +475,6 @@ class monitor_tools { ...@@ -454,16 +475,6 @@ class monitor_tools {
$state = 'error'; // because service is down $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 * Return the Result
...@@ -598,9 +609,6 @@ class monitor_tools { ...@@ -598,9 +609,6 @@ class monitor_tools {
$logfile = '/var/log/fail2ban.log'; $logfile = '/var/log/fail2ban.log';
} }
break; break;
case 'log_mongodb':
$logfile = '/var/log/mongodb/mongodb.log';
break;
case 'log_ispconfig': case 'log_ispconfig':
if ($dist == 'debian') { if ($dist == 'debian') {
$logfile = $conf['ispconfig_log_dir'] . '/ispconfig.log'; $logfile = $conf['ispconfig_log_dir'] . '/ispconfig.log';
......