Source of file api.updates.php
Size: 23,722 Bytes - Last Modified: 2024-04-27T15:26:05+03:00
/tmp/current_snapshot/api/libs/api.updates.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 | <?php /** * Performs deploy of database and config files updates */ class UbillingUpdateManager { /** * System alter.ini config as key=>value * * @var array */ protected $altCfg = array(); /** * System billing.ini config as key=>value * * @var array */ protected $billCfg = array(); /** * System mysql.ini config as key=>value * * @var array */ protected $mySqlCfg = array(); /** * System message helper object placeholder * * @var object */ protected $messages = ''; /** * Available mysql dumps for apply as release=>filename * * @var array */ protected $allDumps = array(); /** * Contains available configs updates as release=>configdata * * @var array */ protected $allConfigs = array(); /** * Contais configs filenames as shortid=>filename * * @var array */ protected $configFileNames = array(); /** * system sudo path * * @var string */ protected $sudoPath = '/usr/local/bin/sudo'; const DUMPS_PATH = 'content/updates/sql/'; const CONFIGS_PATH = 'content/updates/configs/'; const URL_ME = '?module=updatemanager'; const URL_RELNOTES = 'wiki.ubilling.net.ua/doku.php?id=relnotes#section'; /** * Creates new update manager instance * * @return void */ public function __construct() { $this->loadSystemConfigs(); $this->setOptions(); $this->initMessages(); $this->setConfigFilenames(); $this->loadDumps(); $this->loadConfigs(); $this->ConnectDB(); } /** * Loads all required system config files into protected props for further usage * * @global object $ubillingConfig * * @return void */ protected function loadSystemConfigs() { global $ubillingConfig; $this->altCfg = $ubillingConfig->getAlter(); $this->billCfg = $ubillingConfig->getBilling(); $this->mySqlCfg = rcms_parse_ini_file(CONFIG_PATH . 'mysql.ini'); } /** * Sets all required options * * @return void */ protected function setOptions() { if (!empty($this->billCfg)) { $this->sudoPath = $this->billCfg['SUDO']; } } /** * Inits system messages helper object instance * * @return void */ protected function initMessages() { $this->messages = new UbillingMessageHelper(); } /** * Loads available mysql dumps filenames into protected prop * * @return void */ protected function loadDumps() { $dumpsTmp = rcms_scandir(self::DUMPS_PATH, '*.sql'); if (!empty($dumpsTmp)) { rsort($dumpsTmp); foreach ($dumpsTmp as $io => $each) { $release = str_replace('.sql', '', $each); $this->allDumps[$release] = $each; } } } /** * Loads available configs update into protected prop * * @return void */ protected function loadConfigs() { $configsTmp = rcms_scandir(self::CONFIGS_PATH, '*.ini'); if (!empty($configsTmp)) { rsort($configsTmp); foreach ($configsTmp as $io => $each) { $release = str_replace('.ini', '', $each); $fileContent = rcms_parse_ini_file(self::CONFIGS_PATH . $each, true); $this->allConfigs[$release] = $fileContent; } } } /** * Sets shortid=>filename with path configs associations array * * @return void */ protected function setConfigFilenames() { $this->configFileNames = array( 'alter' => 'config/alter.ini', 'billing' => 'config/billing.ini', 'ymaps' => 'config/ymaps.ini', 'userstats' => 'userstats/config/userstats.ini', 'mysql' => 'config/mysql.ini' ); } /** * Initialises connection with Ubilling database server and selects needed db * * @param MySQL Connection Id $connection * * @return MySQLDB */ protected function ConnectDB() { $dbport = (empty($this->mySqlCfg['port']) ? '' : $this->mySqlCfg['port']); $this->DBConnection = new DbConnect($this->mySqlCfg['server'], $this->mySqlCfg['username'], $this->mySqlCfg['password'], $this->mySqlCfg['db'], true, false, $dbport); } /** * Returns list of files which was updated in some release * * @param string $release * * @return string */ protected function getReleaseConfigFiles($release) { $result = ''; if (isset($this->allConfigs[$release])) { if (!empty($this->allConfigs[$release])) { foreach ($this->allConfigs[$release] as $shortid => $data) { $filename = (isset($this->configFileNames[$shortid])) ? $this->configFileNames[$shortid] : $shortid; $result .= $filename . ' '; } } } return($result); } /** * Apply Mysql Dump and returns results * * @param string $release * * @return string */ protected function DoSqlDump($release) { $result = ''; if (!empty($release)) { $fileName = self::DUMPS_PATH . $this->allDumps[$release]; $file = explode(';', file_get_contents($fileName)); $sql_dumps = array_diff($file, array('')); // Delete empty data Array $sql_array = array_map('trim', $sql_dumps); // Open DB connection and set character $this->DBConnection->open(); $this->DBConnection->query("set character_set_client='" . $this->mySqlCfg['character'] . "'"); $this->DBConnection->query("set character_set_results='" . $this->mySqlCfg['character'] . "'"); $this->DBConnection->query("set collation_connection='" . $this->mySqlCfg['character'] . "_general_ci'"); foreach ($sql_array as $query) { if (!empty($query)) { $this->DBConnection->query($query); if (!$this->DBConnection->error()) { $result .= $this->messages->getStyledMessage(wf_tag('b', false) . __('Done') . ': ' . wf_tag('b', true) . wf_tag('pre', false) . $query . wf_tag('pre', true), 'success') . wf_tag('br'); } else { $result .= $this->messages->getStyledMessage(wf_tag('b', false) . __('Error') . ': ' . wf_tag('b', true) . $this->DBConnection->error() . wf_tag('pre', false) . $query . wf_tag('pre', true), 'error') . wf_tag('br'); } } } $this->DBConnection->close(); } return($result); } /** * Renders list of sql dumps available for applying * * @return string */ public function renderSqlDumpsList() { $result = ''; if (!empty($this->allDumps)) { $cells = wf_TableCell(__('Ubilling release')); $cells .= wf_TableCell(__('Details')); $cells .= wf_TableCell(__('Actions')); $rows = wf_TableRow($cells, 'row1'); foreach ($this->allDumps as $release => $filename) { $relnotesUrl = self::URL_RELNOTES . str_replace('.', '', $release); $relnotesLink = wf_Link('http://' . $relnotesUrl, __('Release notes') . ' ' . $release, false, '', 'target="_BLANK"'); $alertText = __('Are you serious') . ' ' . __('Apply') . ' Ubilling ' . $release . '?'; $actLink = wf_JSAlert(self::URL_ME . '&applysql=' . $release, wf_img('skins/icon_restoredb.png', __('Apply')), $alertText); $cells = wf_TableCell($release); $cells .= wf_TableCell($relnotesLink); $cells .= wf_TableCell($actLink); $rows .= wf_TableRow($cells, 'row5'); } $result .= wf_TableBody($rows, '100%', 0, 'sortable'); } else { $result = $this->messages->getStyledMessage(__('Nothing found'), 'info'); } return ($result); } /** * Renders list of available config files updates * * @return string */ public function renderConfigsList() { $result = ''; if (!empty($this->allConfigs)) { $cells = wf_TableCell(__('Ubilling release')); $cells .= wf_TableCell(__('Details')); $cells .= wf_TableCell(__('Files')); $cells .= wf_TableCell(__('Actions')); $rows = wf_TableRow($cells, 'row1'); foreach ($this->allConfigs as $release => $filename) { $relnotesUrl = self::URL_RELNOTES . str_replace('.', '', $release); $relnotesLink = wf_Link('http://' . $relnotesUrl, __('Release notes') . ' ' . $release, false, '', 'target="_BLANK"'); $alertText = __('Are you serious') . ' ' . __('Apply') . ' Ubilling ' . $release . '?'; $actLink = wf_JSAlert(self::URL_ME . '&showconfigs=' . $release, wf_img('skins/icon_addrow.png', __('Apply')), $alertText); $cells = wf_TableCell($release); $cells .= wf_TableCell($relnotesLink); $cells .= wf_TableCell($this->getReleaseConfigFiles($release)); $cells .= wf_TableCell($actLink); $rows .= wf_TableRow($cells, 'row5'); } $result .= wf_TableBody($rows, '100%', 0, 'sortable'); } else { $result = $this->messages->getStyledMessage(__('Nothing found'), 'info'); } return ($result); } /** * Applies mysql dump to current database * * @param string $release * * @return string */ public function applyMysqlDump($release) { $result = ''; $release = trim($release); $release = vf($release); if (isset($this->allDumps[$release])) { if (wf_CheckPost(array('applyconfirm', 'applysqldump'))) { $result .= $this->messages->getStyledMessage(__('MySQL dump applying result below'), 'info'); $result .= wf_CleanDiv(); log_register('UPDMGR APPLY SQL RELEASE `' . $release . '`'); $result .= $this->DoSqlDump($release); $result .= wf_BackLink(self::URL_ME); } else { if ((!wf_CheckPost(array('applyconfirm'))) AND ( wf_CheckPost(array('applysqldump')))) { $result .= $this->messages->getStyledMessage(__('You are not mentally prepared for this'), 'error'); $result .= wf_delimiter(); $result .= wf_BackLink(self::URL_ME . '&applysql=' . $release); } else { $result .= $this->messages->getStyledMessage(__('Caution: these changes can not be undone.'), 'warning'); $result .= wf_tag('br'); $inputs = __('Apply changes for Ubilling release') . ' ' . $release . '?'; $inputs .= wf_tag('br'); $inputs .= wf_tag('br'); $inputs .= wf_HiddenInput('applysqldump', 'true'); $inputs .= wf_CheckInput('applyconfirm', __('I`m ready'), true, false); $inputs .= wf_tag('br'); $inputs .= wf_Submit(__('Apply')); $result .= wf_Form('', 'POST', $inputs, 'glamour'); $result .= wf_CleanDiv(); $result .= wf_delimiter(); $result .= wf_BackLink(self::URL_ME); } } } else { $result = $this->messages->getStyledMessage(__('Wrong release'), 'error'); log_register('UPDMGR FAIL SQL RELEASE `' . $release . '`'); } return ($result); } /** * Changes access rights for some path to be writable * * @param string $path * * @return void */ protected function fixAccessRights($path) { $command = $this->sudoPath . ' chmod -R 777 ' . $path; shell_exec($command); } /** * Renders interface and applies new options to some config files * * @param string $release * * @return string */ public function applyConfigOptions($release) { $result = ''; $release = trim($release); $release = vf($release); $newOptsCount = 0; if (isset($this->allConfigs[$release])) { $releaseData = $this->allConfigs[$release]; if (!empty($releaseData)) { foreach ($releaseData as $configId => $configOptions) { @$configName = $this->configFileNames[$configId]; $canUpdate = false; if (!empty($configName)) { if (file_exists($configName)) { $currentConfigOptions = rcms_parse_ini_file($configName); $result .= $this->messages->getStyledMessage(__('Existing config file') . ': ' . $configName, 'success'); //some logging if (wf_CheckPost(array('applyconfigoptions', 'applyconfirm'))) { //Initial line break and update header $configUpdateHeader = "\n"; $configUpdateHeader .= ';release ' . $release . ' update' . "\n"; if (is_writable($configName)) { $canUpdate = true; } else { $canUpdate = false; $result .= $this->messages->getStyledMessage(__('Permission denied') . ': ' . $configName . '.', 'error'); $result .= $this->messages->getStyledMessage(__('Trying to set write permissions for') . ' ' . $configName . ' ' . __('to fix this issue') . '.', 'warning'); $this->fixAccessRights($configName); if (is_writable($configName)) { $canUpdate = true; $result .= $this->messages->getStyledMessage(__('Success! Config file') . ' ' . $configName . ' ' . __('now is writable') . '.', 'success'); } else { $canUpdate = false; $result .= $this->messages->getStyledMessage(__('Seems like we failed with making this file writable') . '.', 'error'); } } //now real put update header if ($canUpdate) { file_put_contents($configName, $configUpdateHeader, FILE_APPEND); log_register('UPDMGR APPLY CONFIG `' . $configId . '` RELEASE `' . $release . '`'); } } if (!empty($configOptions)) { foreach ($configOptions as $optionName => $optionContent) { if (!isset($currentConfigOptions[$optionName])) { $newOptsCount++; $result .= $this->messages->getStyledMessage(__('New option') . ': ' . $optionName . ' ' . __('will be added with value') . ' ' . $optionContent, 'info'); if (wf_CheckPost(array('applyconfigoptions', 'applyconfirm'))) { $saveOptions = $optionName . '=' . $optionContent . "\n"; if (is_writable($configName)) { file_put_contents($configName, $saveOptions, FILE_APPEND); $result .= $this->messages->getStyledMessage(__('Option added') . ': ' . $optionName . '= ' . $optionContent, 'success'); $newOptsCount--; } else { $result .= $this->messages->getStyledMessage(__('New option') . ' ' . $optionName . ' ' . __('not created') . '. ' . __('Permission denied') . '.', 'error'); } } } else { $result .= $this->messages->getStyledMessage(__('Option already exists, will be ignored') . ': ' . $optionName, 'warning'); } } } } else { $result .= $this->messages->getStyledMessage(__('Wrong config path') . ': ' . $configName, 'error'); } } else { $result .= $this->messages->getStyledMessage(__('Unknown config') . ': ' . $configId, 'error'); } } //confirmation checkbox notice if ((wf_CheckPost(array('applyconfigoptions'))) AND ( !wf_CheckPost(array('applyconfirm')))) { $result .= $this->messages->getStyledMessage(__('You are not mentally prepared for this'), 'error'); } //apply form assembly if ($newOptsCount > 0) { $result .= wf_tag('br'); $inputs = __('Apply changes for Ubilling release') . ' ' . $release . '?'; $inputs .= wf_tag('br'); $inputs .= wf_tag('br'); $inputs .= wf_HiddenInput('applyconfigoptions', 'true'); $inputs .= wf_CheckInput('applyconfirm', __('I`m ready'), true, false); $inputs .= wf_tag('br'); $inputs .= wf_Submit(__('Apply')); $result .= wf_Form('', 'POST', $inputs, 'glamour'); $result .= wf_CleanDiv(); } else { $result .= $this->messages->getStyledMessage(__('Everything is fine. All required options for release') . ' ' . $release . ' ' . __('is on their places.'), 'success'); } } $result .= wf_CleanDiv(); $result .= wf_delimiter(); $result .= wf_BackLink(self::URL_ME); } else { $result .= $this->messages->getStyledMessage(__('Wrong release'), 'error'); log_register('UPDMGR FAIL CONF RELEASE `' . $release . '`'); } return ($result); } /** * Renders current release info data and update check controls * * @return string */ public function renderVersionInfo() { $currentRelease = file_get_contents("RELEASE"); $updatechecker = wf_tag('br') . wf_tag('div', false, '', 'style="margin-left: 3%;"'); $updatechecker .= wf_AjaxLink('?module=updatemanager&checkupdates=true', wf_img('skins/question.png') . ' ' . __('Check updates'), 'lastrelease', false, 'ubButton'); $updatechecker .= wf_tag('div', true); $updatechecker .= wf_CleanDiv(); $releaseInfo = wf_tag('style') . '#ubajaxloaderanim { margin-left: 3%; margin-top: 10px; }' . wf_tag('style', true); $releaseInfo .= $updatechecker; $releaseInfo .= $this->messages->getStyledMessage(__('Current Ubilling version') . ': ' . $currentRelease, 'info'); $releaseInfo .= wf_AjaxContainer('lastrelease', '', ''); $releaseInfo .= wf_AjaxLoader(); return ($releaseInfo); } } /** * Ubilling updates deployment routines */ class UbillingUpdateStuff { /** * Contains system billing.ini as key=>value * * @var array */ protected $billingCfg = array(); /** * Wget path * * @var string */ protected $wgetPath = '/usr/local/bin/wget'; /** * Tar archiver path * * @var string */ protected $tarPath = '/usr/bin/tar'; /** * system sudo path * * @var string */ protected $sudoPath = '/usr/local/bin/sudo'; /** * Gzip archiver path * * @var gzip */ protected $gzipPath = '/usr/bin/gzip'; public function __construct() { $this->loadConfig(); $this->setOptions(); } /** * Loads all required configs * * @global object $ubillingConfig * * @return void */ protected function loadConfig() { global $ubillingConfig; $this->billingCfg = $ubillingConfig->getBilling(); } /** * Sets custom paths to required software * * @return void */ protected function setOptions() { if (isset($this->billingCfg['SUDO'])) { $this->sudoPath = $this->billingCfg['SUDO']; } if (isset($this->billingCfg['WGET_PATH'])) { $this->wgetPath = $this->billingCfg['WGET_PATH']; } if (isset($this->billingCfg['TAR_PATH'])) { $this->tarPath = $this->billingCfg['TAR_PATH']; } if (isset($this->billingCfg['GZIP_PATH'])) { $this->gzipPath = $this->billingCfg['GZIP_PATH']; } } /** * Changes access rights for some directory to be writable * * @param string $directory * * @return void */ public function fixAccessRights($directory) { $command = $this->sudoPath . ' chmod -R 777 ' . $directory; shell_exec($command); } /** * Downloads file from remote host * * @param string $url * @param string $directory * @param string $filename * * @return void */ public function downloadRemoteFile($url, $directory, $filename = '') { if ($filename) { $wgetOptions = '--output-document=' . $directory . $filename . ' '; } else { $wgetOptions = '--directory-prefix=' . $directory . basename($url) . ' '; } $wgetOptions .= '--no-check-certificate '; if (file_exists($directory)) { if (!is_writable($directory)) { throw new Exception('DOWNLOAD_DIRECTORY_NOT_WRITABLE'); } $command = $this->wgetPath . ' ' . $wgetOptions . ' ' . $url; shell_exec($command); } else { throw new Exception('DOWNLOAD_DIRECTORY_NOT_EXISTS'); } } /** * Extracts tar.gz archive to some path * * @param string $archivePath * @param string $extractPath * * @return void */ public function extractTgz($archivePath, $extractPath) { if (file_exists($archivePath)) { if (is_readable($archivePath)) { if (file_exists($extractPath)) { if (!is_writable($extractPath)) { $this->fixAccessRights($extractPath); } //unpacking archive $command = $this->tarPath . ' zxvf ' . $archivePath . ' -C ' . $extractPath; shell_exec($command); } else { throw new Exception('EXTRACT_DIRECTORY_NOT_EXISTS'); } } } } } ?> |