Commit c6d00873 authored by Marius Burkard's avatar Marius Burkard

- do not install/update addons during install/update of ISPconfig. This only...

- do not install/update addons during install/update of ISPconfig. This only leads to lots of trouble
parent e35182f6
This empty directory is needed by ISPConfig.
...@@ -145,46 +145,6 @@ if(is_file('dist/lib/'.$dist['baseid'].'.lib.php')) include_once 'dist/lib/'.$di ...@@ -145,46 +145,6 @@ if(is_file('dist/lib/'.$dist['baseid'].'.lib.php')) include_once 'dist/lib/'.$di
include_once 'dist/lib/'.$dist['id'].'.lib.php'; include_once 'dist/lib/'.$dist['id'].'.lib.php';
include_once 'dist/conf/'.$dist['confid'].'.conf.php'; include_once 'dist/conf/'.$dist['confid'].'.conf.php';
//** Include addon lib config files
if(is_dir('dist/lib.d')) {
// scheme is: <addon-name>.<distconfid>.conf.php
if(($dir = opendir('dist/lib.d'))) {
while(false !== ($cur = readdir($dir))) {
$curpath = 'dist/lib.d/' . $cur;
if(strpos($curpath, '..') !== false
|| !is_file($curpath)
|| !preg_match('/\.(?:' . preg_quote($dist['id'], '/') . '|' . preg_quote($dist['baseid'], '/') . ')\.lib\.php$/', $cur)) {
// invalid entry or entry not for current distribution
continue;
}
// valid file name and either generic or for current distribution
include_once $curpath;
}
closedir($dir);
}
}
//** Include addon dist config files
if(is_dir('dist/conf.d')) {
// scheme is: <addon-name>.<distconfid>.conf.php
if(($dir = opendir('dist/conf.d'))) {
while(false !== ($cur = readdir($dir))) {
$curpath = 'dist/conf.d/' . $cur;
if(strpos($curpath, '..') !== false
|| !is_file($curpath)
|| !preg_match('/\.' . preg_quote($dist['confid'], '/') . '\.conf\.php$/', $cur)) {
// invalid entry or entry not for current distribution
continue;
}
// valid file name and either generic or for current distribution
include_once $curpath;
}
closedir($dir);
}
}
//**************************************************************************************************** //****************************************************************************************************
//** Installer Interface //** Installer Interface
//**************************************************************************************************** //****************************************************************************************************
......
<?php
/**
* Base class for app installer
* This is a stripped down class with only the event method. The full class is only used in /server/lib/classes
*
* @author Marius Burkard
*/
class ispconfig_addon_installer_base {
protected $addon_ident;
public function __construct() {
$this->addon_ident = preg_replace('/_addon_installer$/', '', get_called_class());
}
public function onRaisedInstallerEvent($event_name, $data) {
}
}
...@@ -28,6 +28,8 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, ...@@ -28,6 +28,8 @@ 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.
*/ */
require_once realpath(dirname(__FILE__)) . '/classes/ispconfig_addon_installer.inc.php';
class installer_base { class installer_base {
var $wb = array(); var $wb = array();
...@@ -397,8 +399,6 @@ class installer_base { ...@@ -397,8 +399,6 @@ class installer_base {
$this->db->query($sql, $conf['hostname'], $mail_server_enabled, $web_server_enabled, $dns_server_enabled, $file_server_enabled, $db_server_enabled, $server_ini_content, $current_db_version, $proxy_server_enabled, $firewall_server_enabled); $this->db->query($sql, $conf['hostname'], $mail_server_enabled, $web_server_enabled, $dns_server_enabled, $file_server_enabled, $db_server_enabled, $server_ini_content, $current_db_version, $proxy_server_enabled, $firewall_server_enabled);
$conf['server_id'] = $this->db->insertID(); $conf['server_id'] = $this->db->insertID();
} }
} }
public function detect_ips(){ public function detect_ips(){
...@@ -2183,7 +2183,7 @@ class installer_base { ...@@ -2183,7 +2183,7 @@ class installer_base {
// TODO: Implement a selector which modules and plugins shall be enabled. // TODO: Implement a selector which modules and plugins shall be enabled.
$dir = $install_dir.'/server/mods-available/'; $dir = $install_dir.'/server/mods-available/';
if (is_dir($dir)) { if (is_dir($dir)) {
if ($dh = opendir($dir)) { if (($dh = opendir($dir))) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
if($file != '.' && $file != '..' && substr($file, -8, 8) == '.inc.php') { if($file != '.' && $file != '..' && substr($file, -8, 8) == '.inc.php') {
include_once $install_dir.'/server/mods-available/'.$file; include_once $install_dir.'/server/mods-available/'.$file;
...@@ -2210,7 +2210,7 @@ class installer_base { ...@@ -2210,7 +2210,7 @@ class installer_base {
$dir = $install_dir.'/server/plugins-available/'; $dir = $install_dir.'/server/plugins-available/';
if (is_dir($dir)) { if (is_dir($dir)) {
if ($dh = opendir($dir)) { if (($dh = opendir($dir))) {
while (($file = readdir($dh)) !== false) { while (($file = readdir($dh)) !== false) {
if($conf['apache']['installed'] == true && $file == 'nginx_plugin.inc.php') continue; if($conf['apache']['installed'] == true && $file == 'nginx_plugin.inc.php') continue;
if($conf['nginx']['installed'] == true && $file == 'apache2_plugin.inc.php') continue; if($conf['nginx']['installed'] == true && $file == 'apache2_plugin.inc.php') continue;
...@@ -2828,7 +2828,7 @@ class installer_base { ...@@ -2828,7 +2828,7 @@ class installer_base {
} }
private function loadAddonClasses($path) { private function loadAddonClasses($path) {
$libpath = $conf['ispconfig_install_dir'] . '/addons'; $libpath = $path;
if(($dir = opendir($libpath))) { if(($dir = opendir($libpath))) {
while(false !== ($cur = readdir($dir))) { while(false !== ($cur = readdir($dir))) {
if($cur === '.' || $cur === '..' || strpos($cur, '..') !== false || !is_dir($libpath . '/' . $cur)) { if($cur === '.' || $cur === '..' || strpos($cur, '..') !== false || !is_dir($libpath . '/' . $cur)) {
...@@ -2867,15 +2867,12 @@ class installer_base { ...@@ -2867,15 +2867,12 @@ class installer_base {
if(is_null($this->addon_classes)) { if(is_null($this->addon_classes)) {
// load addon libs // load addon libs
$this->addon_classes = array(); $this->addon_classes = array();
$addonpath = $conf['ispconfig_install_dir'] . '/addons';
$this->loadAddonClasses($addonpath);
// check for addon libs in install dir $addonpath = $conf['ispconfig_install_dir'] . '/addons';
$addonpath = realpath(dirname(__FILE__) . '/..') . '/addons';
$this->loadAddonClasses($addonpath); $this->loadAddonClasses($addonpath);
} }
$call_method = 'onRaisedEvent'; $call_method = 'onRaisedInstallerEvent';
reset($this->addon_classes); reset($this->addon_classes);
foreach($this->addon_classes as $cl) { foreach($this->addon_classes as $cl) {
if(method_exists($cl, $call_method)) { if(method_exists($cl, $call_method)) {
......
...@@ -142,3 +142,12 @@ UPDATE `spamfilter_policy` SET `rspamd_spam_kill_level` = '8.00' WHERE id = 6; ...@@ -142,3 +142,12 @@ UPDATE `spamfilter_policy` SET `rspamd_spam_kill_level` = '8.00' WHERE id = 6;
UPDATE `spamfilter_policy` SET `rspamd_spam_kill_level` = '20.00' WHERE id = 7; UPDATE `spamfilter_policy` SET `rspamd_spam_kill_level` = '20.00' WHERE id = 7;
-- end of rspamd -- end of rspamd
CREATE TABLE IF NOT EXISTS `addons` (
`addon_id` int(11) NOT NULL AUTO_INCREMENT,
`addon_ident` VARCHAR(100) NOT NULL DEFAULT '',
`addon_version` VARCHAR(20) NOT NULL DEFAULT '',
`addon_name` VARCHAR(255) NOT NULL DEFAULT '',
`db_version` INT(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`addon_id`),
UNIQUE KEY `ident` (`addon_ident`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
...@@ -53,6 +53,22 @@ SET FOREIGN_KEY_CHECKS = 0; ...@@ -53,6 +53,22 @@ SET FOREIGN_KEY_CHECKS = 0;
-- -------------------------------------------------------- -- --------------------------------------------------------
-- -------------------------------------------------------- -- --------------------------------------------------------
--
-- Table structure for table `addons`
--
CREATE TABLE IF NOT EXISTS `addons` (
`addon_id` int(11) NOT NULL AUTO_INCREMENT,
`addon_ident` VARCHAR(100) NOT NULL DEFAULT '',
`addon_version` VARCHAR(20) NOT NULL DEFAULT '',
`addon_name` VARCHAR(255) NOT NULL DEFAULT '',
`db_version` INT(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`addon_id`),
UNIQUE KEY `ident` (`addon_ident`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
-- --------------------------------------------------------
-- --
-- Table structure for table `aps_instances` -- Table structure for table `aps_instances`
-- --
......
...@@ -148,45 +148,11 @@ if(is_file('dist/lib/'.$dist['baseid'].'.lib.php')) include_once 'dist/lib/'.$di ...@@ -148,45 +148,11 @@ if(is_file('dist/lib/'.$dist['baseid'].'.lib.php')) include_once 'dist/lib/'.$di
include_once 'dist/lib/'.$dist['id'].'.lib.php'; include_once 'dist/lib/'.$dist['id'].'.lib.php';
include_once 'dist/conf/'.$dist['confid'].'.conf.php'; include_once 'dist/conf/'.$dist['confid'].'.conf.php';
//** Include addon lib config files $inst = new installer();
if(is_dir('dist/lib.d')) { if (!$inst->get_php_version()) die('ISPConfig requieres PHP '.$inst->min_php."\n");
// scheme is: <addon-name>.<distconfid>.conf.php $inst->is_update = true;
if(($dir = opendir('dist/lib.d'))) {
while(false !== ($cur = readdir($dir))) {
$curpath = 'dist/lib.d/' . $cur;
if(strpos($curpath, '..') !== false
|| !is_file($curpath)
|| !preg_match('/\.(?:' . preg_quote($dist['id'], '/') . '|' . preg_quote($dist['baseid'], '/') . ')\.lib\.php$/', $cur)) {
// invalid entry or entry not for current distribution
continue;
}
// valid file name and either generic or for current distribution
include_once $curpath;
}
closedir($dir);
}
}
//** Include addon dist config files $inst->raiseEvent('set_dist_config', $dist);
if(is_dir('dist/conf.d')) {
// scheme is: <addon-name>.<distconfid>.conf.php
if(($dir = opendir('dist/conf.d'))) {
while(false !== ($cur = readdir($dir))) {
$curpath = 'dist/conf.d/' . $cur;
if(strpos($curpath, '..') !== false
|| !is_file($curpath)
|| !preg_match('/\.' . preg_quote($dist['confid'], '/') . '\.conf\.php$/', $cur)) {
// invalid entry or entry not for current distribution
continue;
}
// valid file name and either generic or for current distribution
include_once $curpath;
}
closedir($dir);
}
}
//** tRNG dependencies //** tRNG dependencies
$conf['tRNG']=''; $conf['tRNG']='';
...@@ -228,10 +194,6 @@ if(!$conf['mysql']['ip'] = gethostbyname($conf['mysql']['host'])) die('Unable to ...@@ -228,10 +194,6 @@ if(!$conf['mysql']['ip'] = gethostbyname($conf['mysql']['host'])) die('Unable to
$conf['server_id'] = intval($conf_old["server_id"]); $conf['server_id'] = intval($conf_old["server_id"]);
$conf['ispconfig_log_priority'] = $conf_old["log_priority"]; $conf['ispconfig_log_priority'] = $conf_old["log_priority"];
$inst = new installer();
if (!$inst->get_php_version()) die('ISPConfig requieres PHP '.$inst->min_php."\n");
$inst->is_update = true;
echo "This application will update ISPConfig 3 on your server.\n\n"; echo "This application will update ISPConfig 3 on your server.\n\n";
//* Make a backup before we start the update //* Make a backup before we start the update
......
...@@ -599,6 +599,19 @@ class page_action extends tform_actions { ...@@ -599,6 +599,19 @@ class page_action extends tform_actions {
} }
$msg .= '<br>'; $msg .= '<br>';
} }
$entries = $app->plugin->raiseEvent('tools:resync:get_resync_entries', $this->dataRecord, true);
if(is_array($entries) && !empty($entries)) {
foreach($entries as $entry) {
if(!isset($entry['db_table']) || !isset($entry['db_table']) || !isset($entry['db_table']) || !isset($entry['db_table']) || !isset($entry['db_table']) || !isset($entry['db_table'])) {
continue;
}
if(!isset($entry['active'])) {
$entry['active'] = true;
}
$msg .= $this->do_resync($entry['db_table'], $entry['index_field'], $entry['server_type'], $entry['server_id'], $entry['msg_field'], $entry['wordbook'], $entry['active']);
}
}
echo $msg; echo $msg;
} //* end onSumbmit } //* end onSumbmit
......
...@@ -122,6 +122,8 @@ ...@@ -122,6 +122,8 @@
<div class="col-sm-3"><select name="firewall_server_id" id="firewall_server_id" class="form-control">{tmpl_var name='firewall_server_id'}</select></div> <div class="col-sm-3"><select name="firewall_server_id" id="firewall_server_id" class="form-control">{tmpl_var name='firewall_server_id'}</select></div>
</div> </div>
</tmpl_if> </tmpl_if>
{tmpl_hook name="end_form"}
<div class="form-group"> <div class="form-group">
<label for="resync_client" class="col-sm-2 control-label control-label-plain">{tmpl_var name="resync_client_txt"}</label> <label for="resync_client" class="col-sm-2 control-label control-label-plain">{tmpl_var name="resync_client_txt"}</label>
......
<?php
/**
* addon installer
*
* @author Marius Burkard
*/
class addon_installer {
private function extractPackage($package_file) {
global $app;
$ret = null;
$retval = 0;
$cmd = 'which unzip';
$tmp = explode("\n", exec($cmd, $ret, $retval));
if($retval != 0) {
throw new AddonInstallerException('unzip tool not found.');
}
$unzip = reset($tmp);
unset($tmp);
if(!$unzip) {
throw new AddonInstallerException('unzip tool not found.');
}
$temp_dir = $app->system->tempdir(sys_get_temp_dir(), 'addon_', 0700);
if(!$temp_dir) {
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) {
throw new AddonInstallerException('Package extraction failed.');
}
return $temp_dir;
}
/**
* @param string $path
* @return string
* @throws AddonInstallerValidationException
*/
private function validatePackage($path) {
if(!is_dir($path)) {
throw new AddonInstallerValidationException('Invalid path.');
}
$ini_file = $path . '/addon.ini';
if(!is_file($ini_file)) {
throw new AddonInstallerValidationException('Addon ini file missing.');
}
$ini = parse_ini_file($ini_file, true);
if(!$ini || !isset($ini['addon'])) {
throw new AddonInstallerValidationException('Ini file is missing addon section.');
}
$addon = $ini['addon'];
if(!isset($addon['ident']) || !isset($addon['name']) || !isset($addon['version'])) {
throw new AddonInstallerValidationException('Ini file is missing addon ident/name/version.');
}
$class_file = $path . '/' . $addon['ident'] . '.addon.php';
if(!is_file($class_file)) {
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, '>')) {
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, '<')) {
throw new AddonInstallerValidationException('Addon allows at max ISPConfig version ' . $ini['ispconfig']['version.max'] . '.');
}
$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['version']) || !isset($addon['ident']) || $addon['ident'] != $ident) {
throw new AddonInstallerException('Installed app found but it is invalid.');
}
$file_version = $addon['version'];
}
$check = $app->db->queryOneRecord('SELECT `addon_version` FROM `addons` WHERE `addon_ident` = ?', $ident);
if($check && $check['addon_version']) {
$db_version = $check['addon_version'];
}
if(!$file_version && !$db_version) {
return false;
} elseif($file_version != $db_version) {
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) {
if(!is_file($package_file)) {
throw new AddonInstallerException('Package file not found.');
} elseif(substr($package_file, -4) !== '.pkg') {
throw new AddonInstallerException('Invalid package file.');
}
$tmp_dir = $this->extractPackage($package_file);
if(!$tmp_dir) {
// extracting failed
throw new AddonInstallerException('Package extraction failed.');
}
$addon = $this->validatePackage($tmp_dir);
if(!$addon) {
throw new AddonInstallerException('Package validation failed.');
}
$is_update = false;
$previous = $this->getInstalledAddonVersion($addon['ident']);
if($previous !== false) {
// this is an update
if(version_compare($previous, $addon['version'], '>') && $force !== true) {
throw new AddonInstallerException('Installed version is newer than the one to install.');
} elseif(version_compare($previous, $addon['version'], '=') && $force !== true) {
throw new AddonInstallerException('Installed version is the same as the one to install.');
}
$is_update = true;
}
include $addon['class_file'];
if(!class_exists($addon['class_name'])) {
throw new AddonInstallerException('Could not find main class in addon file.');
}
$class_name = $addon['class_name'];
/* @var $inst ispconfig_addon_installer_base */
$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();
}
return true;
}
}
...@@ -297,6 +297,12 @@ class functions { ...@@ -297,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);
......
<?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 $conf;
$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) {
throw new AddonInstallerException('Command ' . $command . ' failed with code ' . $retval);
}
return true;
} else {
return false;
}
}
protected function copyServerFiles() {
global $conf;
$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) {
throw new AddonInstallerException('Command ' . $command . ' failed with code ' . $retval);
}
return true;
} else {
return false;
}
}
protected function executeSqlStatements() {
global $app, $conf;
// 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);
$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'];
}
}
$mysql_command = 'mysql --default-character-set=' . escapeshellarg($conf['db_charset']) . ' --force -h ' . escapeshellarg($conf['db_host']) . ' -u ' . escapeshellarg($conf['db_user']) . ' -p' . escapeshellarg($conf['db_password']) . ' -P ' . escapeshellarg($conf['db_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;
exec($mysql_command . ' < ' . escapeshellarg($sql_file), $ret, $retval);
if($retval != 0) {
/* TODO: log error! */
}
}
} 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;
exec($mysql_command . ' < ' . escapeshellarg($sql_file), $ret, $retval);
if($retval != 0) {
/* TODO: log error! */
}
}
$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);
}
return true;
}
public function onBeforeInstall() { }
public function onInstall() {
$this->copyInterfaceFiles();
$this->copyServerFiles();
$this->executeSqlStatements();
}
public function onAfterInstall() { }
public function onBeforeUpdate() { }
public function onUpdate() {
$this->copyInterfaceFiles();
$this->copyServerFiles();
$this->executeSqlStatements();
}
public function onAfterUpdate() { }
public function onRaisedInstallerEvent($event_name) {
}
}
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
...@@ -2044,7 +2044,32 @@ class system{ ...@@ -2044,7 +2044,32 @@ class system{
return true; return true;
} }
public function tempdir($parent_path = null, $prefix = 'tmp_', $mode = 0700) {
if(is_null($parent_path)) {
$parent_path = sys_get_temp_dir();
}
$parent_path = rtrim($parent_path, '/');
if(!is_dir($parent_path) || !is_writable($parent_path)) {
return false;
}
if(strpbrk($prefix, '\\/:*?"<>|') !== false) {
return false;
}
$path = false;
$tries = 0;
while($tries < 1000) {
$tries++;
$path = $parent_path . FS_DIV . uniqid($prefix, true);
if(mkdir($path, $mode)) {
break;
}