/** * Monitor the backup * * @copyright Copyright (C) 2013 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ require_once('../../approot.inc.php'); require_once(APPROOT.'application/application.inc.php'); require_once(APPROOT.'application/itopwebpage.class.inc.php'); require_once(APPROOT.'application/startup.inc.php'); require_once(APPROOT.'application/loginwebpage.class.inc.php'); function TestConfig($sContents, $oP) { try { ini_set('display_errors', 1); ob_start(); // in PHP < 7.0.0 syntax errors are in output // in PHP >= 7.0.0 syntax errors are thrown as Error $sSafeContent = preg_replace(array('#^\s*<\?php#', '#\?>\s*$#'), '', $sContents); eval('if(0){'.trim($sSafeContent).'}'); $sNoise = trim(ob_get_contents()); ob_end_clean(); } catch (Error $e) { // ParseError only thrown in PHP7 throw new Exception('Error in configuration: '.$e->getMessage()); } if (strlen($sNoise) > 0) { if (preg_match("/(Error|Parse error|Notice|Warning): (.+) in \S+ : eval\(\)'d code on line (\d+)/i", strip_tags($sNoise), $aMatches)) { $sMessage = $aMatches[2]; $sLine = $aMatches[3]; $iLine = (int) $sLine; // Highlight the line $aLines = explode("\n", $sContents); $iStart = 0; for ($i = 0 ; $i < $iLine - 1; $i++) $iStart += strlen($aLines[$i]); $iEnd = $iStart + strlen($aLines[$iLine - 1]); $iTotalLines = count($aLines); $sMessage = Dict::Format('config-parse-error', $sMessage, $sLine); throw new Exception($sMessage); } else { // Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack) throw new Exception('Syntax error in configuration file: '.$sNoise.''); } } } ///////////////////////////////////////////////////////////////////// // Main program // LoginWebPage::DoLogin(true); // Check user rights and prompt if needed (must be admin) //$sOperation = utils::ReadParam('operation', 'menu'); //$oAppContext = new ApplicationContext(); $oP = new iTopWebPage(Dict::S('config-edit-title')); $oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/'); $oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/ace.js'); $oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/mode-php.js'); $oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/theme-eclipse.js'); $oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/ext-searchbox.js'); //$oP->add_linked_script(utils::GetCurrentModuleUrl().'/js/ext-textarea.js'); try { $sOperation = utils::ReadParam('operation', ''); $oP->add("

".Dict::S('config-edit-title')."

"); if (MetaModel::GetConfig()->Get('demo_mode')) { $oP->add("
Sorry, iTop is in demonstration mode: the configuration file cannot be edited.
"); } else if (MetaModel::GetModuleSetting('itop-config', 'config_editor', '') == 'disabled') { $oP->add("
iTop interactive edition of the configuration as been disabled. See 'config_editor' => 'disabled' in the configuration file.
"); } else { $sConfigFile = APPROOT.'conf/'.utils::GetCurrentEnvironment().'/config-itop.php'; $iEditorTopMargin = 9; $sConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile)); $sOrginalConfig = $sConfig; if (!empty($sOperation)) { $iEditorTopMargin = 14; $sConfig = utils::ReadParam('new_config', '', false, 'raw_data'); $sOrginalConfig = utils::ReadParam('prev_config', '', false, 'raw_data'); } if ($sOperation == 'revert') { $oP->add('
'.Dict::S('config-reverted').'
'); } if ($sOperation == 'save') { $sTransactionId = utils::ReadParam('transaction_id', ''); if (!utils::IsTransactionValid($sTransactionId, true)) { $oP->add("
Error: invalid Transaction ID. The configuration was NOT modified.
"); } else { if ($sConfig == $sOrginalConfig) { $oP->add('
'.Dict::S('config-no-change').'
'); } else { try { TestConfig($sConfig, $oP); // throws exceptions @chmod($sConfigFile, 0770); // Allow overwriting the file $sTmpFile = tempnam(SetupUtils::GetTmpDir(), 'itop-cfg-'); // Don't write the file as-is since it would allow to inject any kind of PHP code. // Instead write the interpreted version of the file // Note: // The actual raw PHP code will anyhow be interpreted exactly twice: once in TestConfig() above // and a second time during the load of the Config object below. // If you are really concerned about an iTop administrator crafting some malicious // PHP code inside the config file, then turn off the interactive configuration // editor by adding the configuration parameter: // 'itop-config' => array( // 'config_editor' => 'disabled', // ) file_put_contents($sTmpFile, $sConfig); $oTempConfig = new Config($sTmpFile, true); $oTempConfig->WriteToFile($sConfigFile); @unlink($sTmpFile); @chmod($sConfigFile, 0444); // Read-only $oP->p('
'.Dict::S('config-saved').'
'); $sOrginalConfig = str_replace("\r\n", "\n", file_get_contents($sConfigFile)); } catch (Exception $e) { $oP->p('
'.$e->getMessage().'
'); } } } } $sConfigEscaped = htmlentities($sConfig, ENT_QUOTES, 'UTF-8'); $sOriginalConfigEscaped = htmlentities($sOrginalConfig, ENT_QUOTES, 'UTF-8'); $oP->p(Dict::S('config-edit-intro')); $oP->add("
"); $oP->add(""); $oP->add(""); $oP->add(""); $oP->add(""); $oP->add(""); $oP->add("
"); $oP->add("
"); $sConfirmCancel = addslashes(Dict::S('config-confirm-cancel')); $oP->add_ready_script( <<add_script( <<p(''.$e->getMessage().''); } $oP->output();