From 5347ecd5ddb1803ec1c43240bafc84f5c427f855 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 26 Jan 2015 20:28:49 +0100 Subject: Add configtgz class. Fix dozens of bugs --- inc/configmodule.inc.php | 67 ++++++++++++++-- inc/configmodule/adauth.inc.php | 1 + inc/configmodule/branding.inc.php | 6 +- inc/configtgz.inc.php | 160 ++++++++++++++++++++++++++++++++++++++ inc/event.inc.php | 5 +- inc/taskmanager.inc.php | 1 - inc/util.inc.php | 40 ++++++++++ 7 files changed, 267 insertions(+), 13 deletions(-) create mode 100644 inc/configtgz.inc.php (limited to 'inc') diff --git a/inc/configmodule.inc.php b/inc/configmodule.inc.php index 5eff92ba..6fd9e1b8 100644 --- a/inc/configmodule.inc.php +++ b/inc/configmodule.inc.php @@ -114,6 +114,33 @@ abstract class ConfigModule return $instance; } + /** + * Get module instances from module type. + * + * @param int $moduleType module type to get + * @return array The requested modules from DB, or false on error + */ + public static function getAll($moduleType) + { + $ret = Database::simpleQuery("SELECT moduleid, title, moduletype, filepath, contents, version FROM configtgz_module " + . " WHERE moduletype = :moduletype", array('moduletype' => $moduleType)); + if ($ret === false) + return false; + $list = array(); + while ($row = $ret->fetch(PDO::FETCH_ASSOC)) { + $instance = self::getInstance($row['moduletype']); + if ($instance === false) + return false; + $instance->currentVersion = $row['version']; + $instance->moduleArchive = $row['filepath']; + $instance->moduleData = json_decode($row['contents'], true); + $instance->moduleId = $row['moduleid']; + $instance->moduleTitle = $row['title']; + $list[] = $instance; + } + return $list; + } + /** * Get the module version. * @@ -163,6 +190,16 @@ abstract class ConfigModule return $this->moduleId; } + /** + * Get module title. + * + * @return string + */ + public final function title() + { + return $this->moduleTitle; + } + /** * Get module archive file name. * @@ -243,8 +280,12 @@ abstract class ConfigModule // Wait for generation if requested if ($timeoutMs > 0 && isset($ret['id']) && !Taskmanager::isFinished($ret)) $ret = Taskmanager::waitComplete($ret, $timeoutMs); - if ($ret === true || (isset($ret['statusCode']) && $ret['statusCode'] === TASK_FINISHED)) - return $this->markUpdated($tmpTgz); // Finished + if ($ret === true || (isset($ret['statusCode']) && $ret['statusCode'] === TASK_FINISHED)) { + // Already Finished + if (file_exists($this->moduleArchive) && !file_exists($tmpTgz)) + $tmpTgz = false; + return $this->markUpdated($tmpTgz); + } if (!is_array($ret) || !isset($ret['id']) || Taskmanager::isFailed($ret)) { if (is_array($ret)) // Failed Taskmanager::addErrorMessage($ret); @@ -271,8 +312,8 @@ abstract class ConfigModule if ($this->moduleId === 0) Util::traceError('ConfigModule::delete called with invalid module id!'); $ret = Database::exec("DELETE FROM configtgz_module WHERE moduleid = :moduleid LIMIT 1", array( - 'moduleid' => $id - )) !== false; + 'moduleid' => $this->moduleId + ), true) !== false; if ($ret !== false) { if ($this->moduleArchive) Taskmanager::submit('DeleteFile', array('file' => $this->moduleArchive), true); @@ -281,6 +322,7 @@ abstract class ConfigModule $this->moduleTitle = false; $this->moduleArchive = false; } + return $ret; } private final function markUpdated($tmpTgz) @@ -312,11 +354,17 @@ abstract class ConfigModule } } // Update DB entry - return Database::exec("UPDATE configtgz_module SET filepath = :filename, version = :version, status = 'OK' WHERE moduleid = :id LIMIT 1", array( + $retval = Database::exec("UPDATE configtgz_module SET filepath = :filename, version = :version, status = 'OK' WHERE moduleid = :id LIMIT 1", array( 'id' => $this->moduleId, 'filename' => $this->moduleArchive, 'version' => $this->moduleVersion() )) !== false; + // Update related config.tgzs + $configs = ConfigTgz::getAllForModule($this->moduleId); + foreach ($configs as $config) { + $config->generate(); + } + return $retval; } private final function markFailed() @@ -356,11 +404,18 @@ abstract class ConfigModule */ public static function serverIpChanged() { - self::loadDb(); + self::loadDb(); // Quick hack: Hard code AdAuth, should be a property of the config module class.... + $list = self::getAll('AdAuth'); + error_log('Changed: Telling ' - count($list) . ' modules'); + foreach ($list as $mod) { + $mod->event_serverIpChanged(); + } + /* // TODO: Make this work foreach (self::$moduleTypes as $module) { $instance = new $module['moduleClass']; $instance->event_serverIpChanged(); } + */ } /** diff --git a/inc/configmodule/adauth.inc.php b/inc/configmodule/adauth.inc.php index 06ac5460..828469c3 100644 --- a/inc/configmodule/adauth.inc.php +++ b/inc/configmodule/adauth.inc.php @@ -54,6 +54,7 @@ class ConfigModule_AdAuth extends ConfigModule */ public function event_serverIpChanged() { + error_log('Calling generate on ' . $this->title()); $this->generate(false); } diff --git a/inc/configmodule/branding.inc.php b/inc/configmodule/branding.inc.php index e8bd1da7..6e452a93 100644 --- a/inc/configmodule/branding.inc.php +++ b/inc/configmodule/branding.inc.php @@ -19,9 +19,7 @@ class ConfigModule_Branding extends ConfigModule protected function generateInternal($tgz, $parent) { if (!$this->validateConfig()) { - if ($this->archive() !== false && file_exists($this->archive())) - return true; // No new temp file given, old archive still exists, pretend it worked... - return false; + return $this->archive() !== false && file_exists($this->archive()); // No new temp file given, old archive still exists, pretend it worked... } $task = Taskmanager::submit('MoveFile', array( 'source' => $this->tmpFile, @@ -44,7 +42,7 @@ class ConfigModule_Branding extends ConfigModule public function setData($key, $value) { - if ($key !== 'tmpFile' || !file_exists($value)) + if ($key !== 'tmpFile' || !is_string($value) || !file_exists($value)) return false; $this->tmpFile = $value; } diff --git a/inc/configtgz.inc.php b/inc/configtgz.inc.php new file mode 100644 index 00000000..783f8b80 --- /dev/null +++ b/inc/configtgz.inc.php @@ -0,0 +1,160 @@ +configId; + } + + public function title() + { + return $this->configTitle; + } + + public static function insert($title, $moduleIds) + { + if (!is_array($moduleIds)) + return false; + $instance = new ConfigTgz; + $instance->configTitle = $title; + // Create output file name (config.tgz) + do { + $instance->file = CONFIG_TGZ_LIST_DIR . '/config-' . Util::sanitizeFilename($instance->configTitle) . '-' . mt_rand() . '-' . time() . '.tgz'; + } while (file_exists($instance->file)); + Database::exec("INSERT INTO configtgz (title, filepath, status) VALUES (:title, :filepath, :status)", array( + 'title' => $instance->configTitle, + 'filepath' => $instance->file, + 'status' => 'MISSING' + )); + $instance->configId = Database::lastInsertId(); + $instance->modules = array(); + // Get all modules to put in config + $idstr = '0'; // Passed directly in query. Make sure no SQL injection is possible + foreach ($moduleIds as $module) { + $idstr .= ',' . (int)$module; // Casting to int should make it safe + } + $res = Database::simpleQuery("SELECT moduleid, filepath, status FROM configtgz_module WHERE moduleid IN ($idstr)"); + // Make connection + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + Database::exec("INSERT INTO configtgz_x_module (configid, moduleid) VALUES (:configid, :moduleid)", array( + 'configid' => $instance->configId, + 'moduleid' => $row['moduleid'] + )); + $instance->modules[] = $row; + } + return $instance; + } + + public static function get($configId) + { + $ret = Database::queryFirst("SELECT configid, title, filepath FROM configtgz WHERE configid = :configid", array( + 'configid' => $configId + )); + if ($ret === false) + return false; + $instance = new ConfigTgz; + $instance->configId = $ret['configid']; + $instance->configTitle = $ret['title']; + $instance->file = $ret['filepath']; + $ret = Database::simpleQuery("SELECT moduleid, filepath, status FROM configtgz_x_module " + . " INNER JOIN configtgz_module USING (moduleid) " + . " WHERE configid = :configid", array('configid' => $instance->configId)); + $instance->modules = array(); + while ($row = $ret->fetch(PDO::FETCH_ASSOC)) { + $instance->modules[] = $row; + } + return $instance; + } + + public static function getAllForModule($moduleId) + { + $res = Database::simpleQuery("SELECT configid, title, filepath FROM configtgz_x_module " + . " INNER JOIN configtgz USING (configid) " + . " WHERE moduleid = :moduleid", array( + 'moduleid' => $moduleId + )); + if ($res === false) + return false; + $list = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $instance = new ConfigTgz; + $instance->configId = $row['configid']; + $instance->configTitle = $row['title']; + $instance->file = $row['filepath']; + $innerRes = Database::simpleQuery("SELECT moduleid, filepath, status FROM configtgz_x_module " + . " INNER JOIN configtgz_module USING (moduleid) " + . " WHERE configid = :configid", array('configid' => $instance->configId)); + $instance->modules = array(); + while ($innerRow = $innerRes->fetch(PDO::FETCH_ASSOC)) { + $instance->modules[] = $innerRow; + } + $list[] = $instance; + } + return $list; + } + + public function generate() + { + if (!($this->configId > 0) || !is_array($this->modules) || $this->file === false) + Util::traceError ('configId <= 0 or modules not array in ConfigTgz::rebuild()'); + $files = array(); + $successStatus = 'OK'; + foreach ($this->modules as $module) { + if (!empty($module['filepath']) && file_exists($module['filepath'])) { + $files[] = $module['filepath']; + if ($module['status'] !== 'OK') + $successStatus = 'OUTDATED'; + } else { + $successStatus = 'OUTDATED'; + } + } + // Hand over to tm + $task = Taskmanager::submit('RecompressArchive', array( + 'inputFiles' => $files, + 'outputFile' => $this->file + )); + // Wait for completion + if (!Taskmanager::isFailed($task) && !Taskmanager::isFinished($task)) + $task = Taskmanager::waitComplete($task, 5000); + // Failed... + if (Taskmanager::isFailed($task)) { + Taskmanager::addErrorMessage($task); + $successStatus = file_exists($this->file) ? 'OUTDATED' : 'MISSING'; + } + Database::exec("UPDATE configtgz SET status = :status WHERE configid = :configid LIMIT 1", array( + 'configid' => $this->configId, + 'status' => $successStatus + )); + return $successStatus; + } + + public function delete() + { + if ($this->configId === 0) + Util::traceError('ConfigTgz::delete called with invalid config id!'); + $ret = Database::exec("DELETE FROM configtgz WHERE configid = :configid LIMIT 1", array( + 'configid' => $this->configId + ), true) !== false; + if ($ret !== false) { + if ($this->file !== false) + Taskmanager::submit('DeleteFile', array('file' => $this->file), true); + $this->configId = 0; + $this->modules = false; + $this->file = false; + } + return $ret; + } + +} diff --git a/inc/event.inc.php b/inc/event.inc.php index 95d75e33..6b303493 100644 --- a/inc/event.inc.php +++ b/inc/event.inc.php @@ -73,9 +73,10 @@ class Event */ public static function serverIpChanged() { - global $tidAdModules, $tidIpxe; - $tidAdModules = Trigger::rebuildAdModules(); + error_log('Server ip changed'); + global $tidIpxe; $tidIpxe = Trigger::ipxe(); + ConfigModule::serverIpChanged(); } /** diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php index 5045fc75..2cdaf69f 100644 --- a/inc/taskmanager.inc.php +++ b/inc/taskmanager.inc.php @@ -216,7 +216,6 @@ class Taskmanager if (++$tries > 10) return false; } - //error_log(socket_strerror(socket_last_error(self::$sock))); return false; } diff --git a/inc/util.inc.php b/inc/util.inc.php index 9d0eced9..d2ecba6f 100644 --- a/inc/util.inc.php +++ b/inc/util.inc.php @@ -232,5 +232,45 @@ SADFACE; } return true; } + + /** + * Send a file to user for download. + * + * @param type $file path of local file + * @param type $name name of file to send to user agent + * @param type $delete delete the file when done? + * @return boolean false: file could not be opened. + * true: error while reading the file + * - on success, the function does not return + */ + public static function sendFile($file, $name, $delete = false) + { + while ((@ob_get_level()) > 0) + @ob_end_clean(); + $fh = @fopen($file, 'rb'); + if ($fh === false) { + Message::addError('error-read', $file); + return false; + } + Header('Content-Type: application/octet-stream', true); + Header('Content-Disposition: attachment; filename=' . str_replace(array(' ', '=', ',', '/', '\\', ':', '?'), '_', iconv('UTF-8', 'ASCII//TRANSLIT', $name))); + Header('Content-Length: ' . @filesize($file)); + while (!feof($fh)) { + $data = fread($fh, 16000); + if ($data === false) { + echo "\r\n\nDOWNLOAD INTERRUPTED!\n"; + if ($delete) + @unlink($file); + return true; + } + echo $data; + @ob_flush(); + @flush(); + } + @fclose($fh); + if ($delete) + @unlink($file); + exit(0); + } } -- cgit v1.2.3-55-g7522