diff --git a/server/lib/classes/addon_installer.inc.php b/server/lib/classes/addon_installer.inc.php index 0b76ebf9ffcf1342877d91dfc811b18eea9e22c9..07301f34f73e46a45effbab6a5fc76b23d9cc7a9 100644 --- a/server/lib/classes/addon_installer.inc.php +++ b/server/lib/classes/addon_installer.inc.php @@ -13,19 +13,24 @@ class addon_installer { $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.'); } @@ -34,9 +39,12 @@ class addon_installer { $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; } @@ -46,36 +54,48 @@ class addon_installer { * @throws AddonInstallerValidationException */ private function validatePackage($path) { + $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'; @@ -92,21 +112,30 @@ class addon_installer { // 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) { - throw new AddonInstallerException('Installed app found but it is invalid.'); + $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 . ').'); } @@ -126,14 +155,17 @@ class addon_installer { $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.'); } @@ -141,25 +173,34 @@ class addon_installer { 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']; - if(!class_exists($addon['class_name'])) { + $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.'); } - $class_name = $addon['class_name']; /* @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']); @@ -178,6 +219,7 @@ class addon_installer { exec('rm -rf ' . escapeshellarg($tmp_dir)); + $app->log('Installation completed.', 0, false); return true; } diff --git a/server/lib/classes/ispconfig_addon_installer_base.inc.php b/server/lib/classes/ispconfig_addon_installer_base.inc.php index 1e3cf2ec97553937d28d519c803fc9226350e5a9..66b7b51315f79cb1b3dc1f67a6380d5be229071a 100644 --- a/server/lib/classes/ispconfig_addon_installer_base.inc.php +++ b/server/lib/classes/ispconfig_addon_installer_base.inc.php @@ -36,6 +36,7 @@ class ispconfig_addon_installer_base { protected function copyInterfaceFiles() { global $conf; + $app->log('Copying interface files.', 0, false); $install_dir = realpath($conf['rootpath'] . '/..'); if(is_dir($this->temp_dir . '/interface')) { @@ -44,6 +45,7 @@ class ispconfig_addon_installer_base { $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); } @@ -56,6 +58,8 @@ class ispconfig_addon_installer_base { protected function copyServerFiles() { global $conf; + $app->log('Copying server files.', 0, false); + $install_dir = realpath($conf['rootpath'] . '/..'); if(is_dir($this->temp_dir . '/server')) { @@ -64,6 +68,7 @@ class ispconfig_addon_installer_base { $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; @@ -75,9 +80,12 @@ class ispconfig_addon_installer_base { 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); } } @@ -89,6 +97,7 @@ class ispconfig_addon_installer_base { 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; @@ -100,6 +109,7 @@ class ispconfig_addon_installer_base { protected function executeSqlStatements() { global $app, $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); @@ -110,6 +120,7 @@ class ispconfig_addon_installer_base { $incremental = 0; if($check['db_version']) { $incremental = $check['db_version']; + $app->log('Current db version is ' . $incremental . '.', 0, false); } } @@ -121,9 +132,11 @@ class ispconfig_addon_installer_base { 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 { @@ -135,9 +148,11 @@ class ispconfig_addon_installer_base { } 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); } } @@ -145,6 +160,7 @@ class ispconfig_addon_installer_base { } $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; @@ -153,10 +169,12 @@ class ispconfig_addon_installer_base { public function onBeforeInstall() { } public function onInstall() { + $app->log('Running onInstall()', 0, false); $this->copyAddonFiles(); $this->copyInterfaceFiles(); $this->copyServerFiles(); $this->executeSqlStatements(); + $app->log('Finished onInstall()', 0, false); } public function onAfterInstall() { } @@ -164,10 +182,12 @@ class ispconfig_addon_installer_base { public function onBeforeUpdate() { } public function onUpdate() { + $app->log('Running onUpdate()', 0, false); $this->copyAddonFiles(); $this->copyInterfaceFiles(); $this->copyServerFiles(); $this->executeSqlStatements(); + $app->log('Finished onUpdate()', 0, false); } public function onAfterUpdate() { }