diff --git a/administrator/components/com_admin/models/sysinfo.php b/administrator/components/com_admin/models/sysinfo.php index bcc95712246da..923c0bb5738b6 100644 --- a/administrator/components/com_admin/models/sysinfo.php +++ b/administrator/components/com_admin/models/sysinfo.php @@ -50,6 +50,90 @@ class AdminModelSysInfo extends JModelLegacy */ protected $php_info = null; + /** + * Array containing the phpinfo() data. + * + * @var array + * + * @since 3.5 + */ + protected $phpInfoArray; + + /** + * Private/critical data that we don't want to share + * + * @var array + * + * @since 3.5 + */ + protected $privateSettings = array( + 'phpInfoArray' => array( + 'CONTEXT_DOCUMENT_ROOT', + 'DOCUMENT_ROOT', + 'extension_dir', + 'Host', + 'HTTP_COOKIE', + 'HTTP_HOST', + 'HTTP_ORIGIN', + 'HTTP_REFERER', + 'HTTP Request', + 'include_path', + 'mysql.default_socket', + 'MYSQL_SOCKET', + 'MYSQL_INCLUDE', + 'MYSQL_LIBS', + 'mysqli.default_socket', + 'MYSQLI_SOCKET', + 'PATH', + 'Path to sendmail', + 'pdo_mysql.default_socket', + 'Referer', + 'REMOTE_ADDR', + 'SCRIPT_FILENAME', + 'sendmail_path', + 'SERVER_ADDR', + 'SERVER_ADMIN', + 'Server Administrator', + 'SERVER_NAME', + 'Server Root', + 'session.name', + 'session.save_path', + 'User/Group', + ), + 'other' => array( + 'db', + 'dbprefix', + 'fromname', + 'live_site', + 'log_path', + 'mailfrom', + 'memcache_server_host', + 'memcached_server_host', + 'open_basedir', + 'Origin', + 'proxy_host', + 'proxy_user', + 'proxy_pass', + 'secret', + 'sendmail', + 'session.save_path', + 'session_memcache_server_host', + 'session_memcached_server_host', + 'sitename', + 'smtphost', + 'tmp_path' + ) + ); + + /** + * System values that can be "safely" shared + * + * @var array + * + * @since 3.5 + */ + protected $safeData; + /** * Information about writable state of directories * @@ -66,6 +150,67 @@ class AdminModelSysInfo extends JModelLegacy */ protected $editor = null; + /** + * Remove sections of data marked as private in the privateSettings + * + * @param array $dataArray Array with data tha may contain private informati + * @param string $dataType Type of data to search for an specific section in the privateSettings array + * + * @return array + * + * @since 3.5 + */ + protected function cleanPrivateData($dataArray, $dataType = 'other') + { + $dataType = isset($this->privateSettings[$dataType]) ? $dataType : 'other'; + + $privateSettings = $this->privateSettings[$dataType]; + + if (!$privateSettings) + { + return $dataArray; + } + + foreach ($dataArray as $section => $values) + { + if (is_array($values)) + { + $dataArray[$section] = $this->cleanPrivateData($values, $dataType); + } + + if (in_array($section, $privateSettings, true)) + { + $dataArray[$section] = $this->cleanSectionPrivateData($values); + } + } + + return $dataArray; + } + + /** + * Offuscate section values + * + * @param mixed $sectionValues Section data + * + * @return mixed + * + * @since 3.5 + */ + protected function cleanSectionPrivateData($sectionValues) + { + if (!is_array($sectionValues)) + { + return strlen($sectionValues) ? 'xxxxxx' : ''; + } + + foreach ($sectionValues as $setting => $value) + { + $sectionValues[$setting] = strlen($value) ? 'xxxxxx' : ''; + } + + return $sectionValues; + } + /** * Method to get the PHP settings * @@ -175,6 +320,36 @@ public function phpinfoEnabled() return !in_array('phpinfo', explode(',', ini_get('disable_functions'))); } + /** + * Method to get filter data from the model + * + * @param string $dataType Type of data to get safely + * + * @return array + * + * @since 3.5 + */ + public function getSafeData($dataType) + { + if (isset($this->safeData[$dataType])) + { + return $this->safeData[$dataType]; + } + + $methodName = 'get' . ucfirst($dataType); + + if (!method_exists($this, $methodName)) + { + return array(); + } + + $data = $this->$methodName(); + + $this->safeData[$dataType] = $this->cleanPrivateData($data, $dataType); + + return $this->safeData[$dataType]; + } + /** * Method to get the PHP info * @@ -214,6 +389,96 @@ public function &getPHPInfo() return $this->php_info; } + /** + * Get phpinfo() output as array + * + * @return array + * + * @since 3.5 + */ + public function getPhpInfoArray() + { + // Already cached + if (null !== $this->phpInfoArray) + { + return $this->phpInfoArray; + } + + $phpInfo = $this->getPhpInfo(); + + $this->phpInfoArray = $this->parsePhpInfo($phpInfo); + + return $this->phpInfoArray; + } + + /** + * Method to get a list of installed extensions + * + * @return array installed extensions + * + * @since 3.5 + */ + public function getExtensions() + { + $installed = array(); + $db = JFactory::getDbo(); + $query = $db->getQuery(true) + ->select('*') + ->from($db->qn('#__extensions')); + $db->setQuery($query); + + try + { + $extensions = $db->loadObjectList(); + } + catch (Exception $e) + { + JLog::add(JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()), JLog::WARNING, 'jerror'); + + return $installed; + } + + if (empty($extensions)) + { + return $installed; + } + + foreach ($extensions as $extension) + { + if (strlen($extension->name) == 0) + { + continue; + } + + $installed[$extension->name] = array( + 'name' => $extension->name, + 'type' => $extension->type, + 'author' => 'unknown', + 'version' => 'unknown', + 'creationDate' => 'unknown', + 'authorUrl' => 'unknown' + ); + + $manifest = json_decode($extension->manifest_cache); + + if (!$manifest instanceof stdClass) + { + continue; + } + + $extraData = array( + 'author' => $manifest->author, + 'version' => $manifest->version, + 'creationDate' => $manifest->creationDate, + 'authorUrl' => $manifest->authorUrl + ); + + $installed[$extension->name] = array_merge($installed[$extension->name], $extraData); + } + + return $installed; + } + /** * Method to get the directory states * @@ -371,4 +636,52 @@ public function &getEditor() return $this->editor; } + + /** + * Parse phpinfo output into an array + * Source https://gist.github.com/sbmzhcn/6255314 + * + * @param string $html Output of phpinfo() + * + * @return array + * + * @since 3.5 + */ + protected function parsePhpInfo($html) + { + $html = strip_tags($html, '

'); + $html = preg_replace('/]*>([^<]+)<\/th>/', '\1', $html); + $html = preg_replace('/]*>([^<]+)<\/td>/', '\1', $html); + $t = preg_split('/(]*>[^<]+<\/h2>)/', $html, -1, PREG_SPLIT_DELIM_CAPTURE); + $r = array(); + $count = count($t); + $p1 = '([^<]+)<\/info>'; + $p2 = '/' . $p1 . '\s*' . $p1 . '\s*' . $p1 . '/'; + $p3 = '/' . $p1 . '\s*' . $p1 . '/'; + + for ($i = 1; $i < $count; $i++) + { + if (preg_match('/]*>([^<]+)<\/h2>/', $t[$i], $matchs)) + { + $name = trim($matchs[1]); + $vals = explode("\n", $t[$i + 1]); + + foreach ($vals AS $val) + { + // 3cols + if (preg_match($p2, $val, $matchs)) + { + $r[$name][trim($matchs[1])] = array(trim($matchs[2]), trim($matchs[3])); + } + // 2cols + elseif (preg_match($p3, $val, $matchs)) + { + $r[$name][trim($matchs[1])] = trim($matchs[2]); + } + } + } + } + + return $r; + } } diff --git a/administrator/components/com_admin/views/sysinfo/tmpl/default.php b/administrator/components/com_admin/views/sysinfo/tmpl/default.php index d1aa1eb6fb2c3..c4ff26354b2da 100644 --- a/administrator/components/com_admin/views/sysinfo/tmpl/default.php +++ b/administrator/components/com_admin/views/sysinfo/tmpl/default.php @@ -13,7 +13,7 @@ JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html'); ?> -
+
@@ -41,6 +41,8 @@
+ +
diff --git a/administrator/components/com_admin/views/sysinfo/view.html.php b/administrator/components/com_admin/views/sysinfo/view.html.php index b9c465c516f42..b0547719863d3 100644 --- a/administrator/components/com_admin/views/sysinfo/view.html.php +++ b/administrator/components/com_admin/views/sysinfo/view.html.php @@ -116,6 +116,8 @@ protected function _setSubMenu() protected function addToolbar() { JToolbarHelper::title(JText::_('COM_ADMIN_SYSTEM_INFORMATION'), 'info-2 systeminfo'); + JToolbarHelper::link(JRoute::_('index.php?option=com_admin&view=sysinfo&format=text'), 'COM_ADMIN_DOWNLOAD_SYSTEM_INFORMATION_TEXT', 'download'); + JToolbarHelper::link(JRoute::_('index.php?option=com_admin&view=sysinfo&format=json'), 'COM_ADMIN_DOWNLOAD_SYSTEM_INFORMATION_JSON', 'download'); JToolbarHelper::help('JHELP_SITE_SYSTEM_INFORMATION'); } } diff --git a/administrator/components/com_admin/views/sysinfo/view.json.php b/administrator/components/com_admin/views/sysinfo/view.json.php new file mode 100644 index 0000000000000..0c8022f4892ef --- /dev/null +++ b/administrator/components/com_admin/views/sysinfo/view.json.php @@ -0,0 +1,67 @@ +authorise('core.admin')) + { + return JError::raiseWarning(404, JText::_('JERROR_ALERTNOAUTHOR')); + } + + header('MIME-Version: 1.0'); + header('Content-Disposition: attachment; filename="systeminfo-' . date("c") . '.json"'); + header('Content-Transfer-Encoding: binary'); + + $data = $this->getLayoutData(); + + echo json_encode($data); + + JFactory::getApplication()->close(); + } + + /** + * Get the data for the view + * + * @return array + * + * @since 3.5 + */ + protected function getLayoutData() + { + $model = $this->getModel(); + + return array( + 'info' => $model->getSafeData('info'), + 'phpSettings' => $model->getSafeData('phpSettings'), + 'config' => $model->getSafeData('config'), + 'directories' => $model->getSafeData('directory'), + 'phpInfo' => $model->getSafeData('phpInfoArray'), + 'extensions' => $model->getSafeData('extensions') + ); + } +} diff --git a/administrator/components/com_admin/views/sysinfo/view.text.php b/administrator/components/com_admin/views/sysinfo/view.text.php new file mode 100644 index 0000000000000..78b45e4e1395e --- /dev/null +++ b/administrator/components/com_admin/views/sysinfo/view.text.php @@ -0,0 +1,175 @@ +authorise('core.admin')) + { + return JError::raiseWarning(404, JText::_('JERROR_ALERTNOAUTHOR')); + } + + header('Content-Description: File Transfer'); + header('Content-Disposition: attachment; filename="systeminfo-' . date("c") . '.txt"'); + header('Cache-Control: must-revalidate'); + + $data = $this->getLayoutData(); + + $lines = array(); + + foreach ($data as $sectionName => $section) + { + $customRenderingMethod = 'render' . ucfirst($sectionName); + + if (method_exists($this, $customRenderingMethod)) + { + $lines[] = $this->$customRenderingMethod($section['title'], $section['data']); + } + else + { + $lines[] = $this->renderSection($section['title'], $section['data']); + } + } + + echo implode("\n\n", $lines); + + JFactory::getApplication()->close(); + } + + /** + * Get the data for the view + * + * @return array + * + * @since 3.5 + */ + protected function getLayoutData() + { + $model = $this->getModel(); + + return array( + 'info' => array( + 'title' => JText::_('COM_ADMIN_SYSTEM_INFORMATION', true), + 'data' => $model->getSafeData('info') + ), + 'phpSettings' => array( + 'title' => JText::_('COM_ADMIN_PHP_SETTINGS', true), + 'data' => $model->getSafeData('phpSettings') + ), + 'config' => array( + 'title' => JText::_('COM_ADMIN_CONFIGURATION_FILE', true), + 'data' => $model->getSafeData('config') + ), + 'directories' => array( + 'title' => JText::_('COM_ADMIN_DIRECTORY_PERMISSIONS', true), + 'data' => $model->getSafeData('directory') + ), + 'phpInfo' => array( + 'title' => JText::_('COM_ADMIN_PHP_INFORMATION', true), + 'data' => $model->getSafeData('phpInfoArray') + ), + 'extensions' => array( + 'title' => JText::_('COM_ADMIN_EXTENSIONS', true), + 'data' => $model->getSafeData('extensions') + ) + ); + } + + /** + * Render a section + * + * @param string $sectionName Name of the section to render + * @param array $sectionData Data of the section to render + * @param integer $level Depth level for indentation + * + * @return string + * + * @since 3.5 + */ + protected function renderSection($sectionName, $sectionData, $level = 0) + { + $lines = array(); + + $margin = ($level > 0) ? str_repeat("\t", $level) : null; + + $lines[] = $margin . "============="; + $lines[] = $margin . $sectionName; + $lines[] = $margin . "============="; + $level++; + + foreach ($sectionData as $name => $value) + { + if (is_array($value)) + { + if ($name == 'Directive') + { + continue; + } + + $lines[] = ""; + $lines[] = $this->renderSection($name, $value, $level); + } + else + { + if (is_bool($value)) + { + $value = $value ? 'true' : 'false'; + } + + if (is_int($name) && ($name == 0 || $name == 1)) + { + $name = ($name == 0 ? 'Local Value' : 'Master Value'); + } + + $lines[] = $margin . $name . ': ' . $value; + } + } + + return implode("\n", $lines); + } + + /** + * Specific rendering for directories + * + * @param string $sectionName Name of the section + * @param array $sectionData Directories information + * @param integer $level Starting level + * + * @return string + * + * @since 3.5 + */ + protected function renderDirectories($sectionName, $sectionData, $level = -1) + { + foreach ($sectionData as $directory => $data) + { + $sectionData[$directory] = $data['writable'] ? ' writable' : ' NOT writable'; + } + + return $this->renderSection($sectionName, $sectionData, $level); + } +} diff --git a/administrator/includes/toolbar.php b/administrator/includes/toolbar.php index cba3e1c7a0870..b618ff34beab8 100644 --- a/administrator/includes/toolbar.php +++ b/administrator/includes/toolbar.php @@ -153,6 +153,24 @@ public static function back($alt = 'JTOOLBAR_BACK', $href = 'javascript:history. $bar->appendButton('Link', 'back', $alt, $href); } + /** + * Creates a button to redirect to a link + * + * @param string $url The link url + * @param string $text Button text + * @param string $name Name to be used as apart of the id + * + * @return void + * + * @since 3.5 + */ + public static function link($url, $text, $name = 'link') + { + $bar = JToolbar::getInstance('toolbar'); + + $bar->appendButton('Link', $name, $text, $url); + } + /** * Writes a media_manager button. * diff --git a/administrator/language/en-GB/en-GB.com_admin.ini b/administrator/language/en-GB/en-GB.com_admin.ini index ec969fd3a7618..0f573f0c990bf 100644 --- a/administrator/language/en-GB/en-GB.com_admin.ini +++ b/administrator/language/en-GB/en-GB.com_admin.ini @@ -15,6 +15,9 @@ COM_ADMIN_DIRECTORY="Folder" COM_ADMIN_DIRECTORY_PERMISSIONS="Folder Permissions" COM_ADMIN_DISABLED_FUNCTIONS="Disabled Functions" COM_ADMIN_DISPLAY_ERRORS="Display Errors" +COM_ADMIN_DOWNLOAD_SYSTEM_INFORMATION_TEXT="Download as text" +COM_ADMIN_DOWNLOAD_SYSTEM_INFORMATION_JSON="Download as JSON" +COM_ADMIN_EXTENSIONS="Extensions" COM_ADMIN_FILE_UPLOADS="File Uploads" COM_ADMIN_GLOSSARY="Glossary" COM_ADMIN_GO="Go"