/administrator/components/com_admintools/Model/ControlPanel.php
PHP | 611 lines | 355 code | 103 blank | 153 comment | 46 complexity | 6622d99465b2a1a7ffab62b6492634ef MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, 0BSD, MIT, Apache-2.0, LGPL-2.1
- <?php
- /**
- * @package AdminTools
- * @copyright 2010-2017 Akeeba Ltd / Nicholas K. Dionysopoulos
- * @license GNU General Public License version 3, or later
- */
- namespace Akeeba\AdminTools\Admin\Model;
- defined('_JEXEC') or die;
- use Akeeba\AdminTools\Admin\Helper\Storage;
- use Akeeba\Engine\Platform;
- use Akeeba\Engine\Util\Complexify;
- use FOF30\Database\Installer;
- use FOF30\Model\Model;
- use FOF30\Utils\CacheCleaner;
- use FOF30\Utils\Ip;
- use JText;
- class ControlPanel extends Model
- {
- /**
- * The extension ID of the System - Admin Tools plugin
- *
- * @var int
- */
- static $pluginId = null;
- /**
- * Get the extension ID of the System - Admin Tools plugin
- *
- * @return int
- */
- public function getPluginID()
- {
- if (empty(static::$pluginId))
- {
- $db = $this->container->db;
- $query = $db->getQuery(true)
- ->select($db->qn('extension_id'))
- ->from($db->qn('#__extensions'))
- ->where($db->qn('enabled') . ' >= ' . $db->q('1'))
- ->where($db->qn('folder') . ' = ' . $db->q('system'))
- ->where($db->qn('element') . ' = ' . $db->q('admintools'))
- ->where($db->qn('type') . ' = ' . $db->q('plugin'))
- ->order($db->qn('ordering') . ' ASC');
- static::$pluginId = $db->setQuery($query)->loadResult();
- }
- return static::$pluginId;
- }
- /**
- * Makes sure our system plugin is really the very first system plugin to execute
- *
- * @return void
- */
- public function reorderPlugin()
- {
- // Get our plugin's ID
- $id = $this->getPluginID();
- // The plugin is not enabled, there's no point in continuing
- if (!$id)
- {
- return;
- }
- // Get a list of ordering values per ID
- $db = $this->container->db;
- $query = $db->getQuery(true)
- ->select(array(
- $db->qn('extension_id'),
- $db->qn('ordering'),
- ))
- ->from($db->qn('#__extensions'))
- ->where($db->qn('type') . ' = ' . $db->q('plugin'))
- ->where($db->qn('folder') . ' = ' . $db->q('system'))
- ->order($db->qn('ordering') . ' ASC');
- $orderingPerId = $db->setQuery($query)->loadAssocList('extension_id', 'ordering');
- $orderings = array_values($orderingPerId);
- $orderings = array_unique($orderings);
- $minOrdering = reset($orderings);
- $myOrdering = $orderingPerId[ $id ];
- reset($orderings);
- $sharedOrderings = 0;
- foreach ($orderingPerId as $fooid => $order)
- {
- if ($order > $myOrdering)
- {
- break;
- }
- if ($order == $myOrdering)
- {
- $sharedOrderings++;
- }
- }
- // Do I need to reorder the plugin?
- if (($myOrdering > $minOrdering) || ($sharedOrderings > 1))
- {
- $query = $db->getQuery(true)
- ->update($db->qn('#__extensions'))
- ->set($db->qn('ordering') . ' = ' . $db->q($minOrdering - 1))
- ->where($db->qn('extension_id') . ' = ' . $db->q($id));
- $db->setQuery($query);
- $db->execute();
- // Reset the Joomla! plugins cache
- CacheCleaner::clearPluginsCache();
- }
- }
- /**
- * Does the user need to enter a Download ID in the component's Options page?
- *
- * @return bool
- */
- public function needsDownloadID()
- {
- // Do I need a Download ID?
- if (!ADMINTOOLS_PRO)
- {
- return false;
- }
- $dlid = $this->container->params->get('downloadid', '');
- if (!preg_match('/^([0-9]{1,}:)?[0-9a-f]{32}$/i', $dlid))
- {
- return true;
- }
- return false;
- }
- /**
- * Checks the database for missing / outdated tables using the $dbChecks
- * data and runs the appropriate SQL scripts if necessary.
- *
- * @throws \RuntimeException If the previous database update is stuck
- *
- * @return $this
- */
- public function checkAndFixDatabase()
- {
- $params = $this->container->params;
- // First of all let's check if we are already updating
- $stuck = $params->get('updatedb', 0);
- if ($stuck)
- {
- throw new \RuntimeException('Previous database update is flagged as stuck');
- }
- // Then set the flag
- $params->set('updatedb', 1);
- $params->save();
- // Install or update database
- $db = $this->container->db;
- $dbInstaller = new Installer($db, JPATH_ADMINISTRATOR . '/components/com_admintools/sql/xml');
- $dbInstaller->updateSchema();
- // Let's check and fix common tables, too
- /** @var Stats $statsModel */
- $statsModel = $this->container->factory->model('Stats')->tmpInstance();
- $statsModel->checkAndFixCommonTables();
- // And finally remove the flag if everything went fine
- $params->set('updatedb', null);
- $params->save();
- return $this;
- }
- /**
- * Checks all the available places if we just blocked our own IP?
- *
- * @param string $externalIp Additional IP address to check
- *
- * @return bool
- */
- public function isMyIPBlocked($externalIp = null)
- {
- // First let's get the current IP of the user
- $ipList[] = $this->getVisitorIP();
- if ($externalIp)
- {
- $ipList[] = $externalIp;
- }
- /** @var AutoBannedAddresses $autoban */
- $autoban = $this->container->factory->model('AutoBannedAddresses')->tmpInstance();
- /** @var IPAutoBanHistories $history */
- $history = $this->container->factory->model('IPAutoBanHistories')->tmpInstance();
- /** @var BlacklistedAddresses $black */
- $black = $this->container->factory->model('BlacklistedAddresses')->tmpInstance();
- // Then for each ip let's check if it's in any "blocked" list
- foreach ($ipList as $ip)
- {
- $autoban->reset()->setState('ip', $ip);
- $history->reset()->setState('ip', $ip);
- $black->reset()->setState('ip', $ip);
- if (count($autoban->get(true)))
- {
- return true;
- }
- if (count($history->get(true)))
- {
- return true;
- }
- if (count($black->get(true)))
- {
- return true;
- }
- }
- return false;
- }
- /**
- * Removed the current IP from all the "block" lists
- *
- * @param string $externalIp Additional IP address to check
- *
- * @return void
- */
- public function unblockMyIP($externalIp = null)
- {
- // First let's get the current IP of the user
- $ipList[] = $this->getVisitorIP();
- if ($externalIp)
- {
- $ipList[] = $externalIp;
- }
- /** @var AutoBannedAddresses $autoban */
- $autoban = $this->container->factory->model('AutoBannedAddresses')->tmpInstance();
- /** @var IPAutoBanHistories $history */
- $history = $this->container->factory->model('IPAutoBanHistories')->tmpInstance();
- /** @var BlacklistedAddresses $black */
- $black = $this->container->factory->model('BlacklistedAddresses')->tmpInstance();
- /** @var SecurityExceptions $log */
- $log = $this->container->factory->model('SecurityExceptions')->tmpInstance();
- $db = $this->container->db;
- // Let's delete all the IP. We are going to directly use the database since it would be faster
- // than loading the record and then deleting it
- foreach ($ipList as $ip)
- {
- $autoban->reset()->setState('ip', $ip);
- $history->reset()->setState('ip', $ip);
- $black->reset()->setState('ip', $ip);
- $log->reset()->setState('ip', $ip);
- if (count($autoban->get(true)))
- {
- $query = $db->getQuery(true)
- ->delete($db->qn('#__admintools_ipautoban'))
- ->where($db->qn('ip') . ' = ' . $db->q($ip));
- $db->setQuery($query)->execute();
- }
- if (count($history->get(true)))
- {
- $query = $db->getQuery(true)
- ->delete($db->qn('#__admintools_ipautobanhistory'))
- ->where($db->qn('ip') . ' = ' . $db->q($ip));
- $db->setQuery($query)->execute();
- }
- if (count($black->get(true)))
- {
- $query = $db->getQuery(true)
- ->delete($db->qn('#__admintools_ipblock'))
- ->where($db->qn('ip') . ' = ' . $db->q($ip));
- $db->setQuery($query)->execute();
- }
- // I have to delete the log of security exceptions, too. Otherwise at the next check the user will be
- // banned once again
- if (count($log->get(true)))
- {
- $query = $db->getQuery(true)
- ->delete($db->qn('#__admintools_log'))
- ->where($db->qn('ip') . ' = ' . $db->q($ip));
- $db->setQuery($query)->execute();
- }
- }
- }
- /**
- * Update the cached live site's URL for the front-end scheduling feature
- *
- * @return void
- */
- public function updateMagicParameters()
- {
- $this->container->params->set('siteurl', str_replace('/administrator', '', \JUri::base()));
- $this->container->params->save();
- }
- /**
- * Check the strength of the Secret Word for front-end and remote scans. If it is insecure return the reason it
- * is insecure as a string. If the Secret Word is secure return an empty string.
- *
- * @return string
- */
- public function getFrontendSecretWordError()
- {
- // Load the Akeeba Engine autoloader
- define('AKEEBAENGINE', 1);
- require_once JPATH_ADMINISTRATOR . '/components/com_admintools/engine/Autoloader.php';
- // Load the platform
- Platform::addPlatform('filescan', JPATH_ADMINISTRATOR . '/components/com_admintools/platform/Filescan');
- // Is frontend backup enabled?
- $febEnabled = Platform::getInstance()->get_platform_configuration_option('frontend_enable', 0) != 0;
- if (!$febEnabled)
- {
- return '';
- }
- $secretWord = Platform::getInstance()->get_platform_configuration_option('frontend_secret_word', '');
- try
- {
- Complexify::isStrongEnough($secretWord);
- }
- catch (\RuntimeException $e)
- {
- // Ah, the current Secret Word is bad. Create a new one if necessary.
- $newSecret = $this->container->platform->getSessionVar('newSecretWord', null, 'admintools.cpanel');
- if (empty($newSecret))
- {
- $random = new \Akeeba\Engine\Util\RandomValue();
- $newSecret = $random->generateString(32);
- $this->container->platform->setSessionVar('newSecretWord', $newSecret, 'admintools.cpanel');
- }
- return $e->getMessage();
- }
- return '';
- }
- /**
- * Performs some checks about Joomla configuration (log and tmp path correctly set)
- *
- * @return string|bool Warning message. Boolean FALSE if no warning is found.
- */
- public function checkJoomlaConfiguration()
- {
- // Let's get the site root using the Platform code
- if (!defined('AKEEBAENGINE'))
- {
- define('AKEEBAENGINE', 1);
- }
- require_once JPATH_ADMINISTRATOR . '/components/com_admintools/engine/Autoloader.php';
- $siteroot = Platform::getInstance()->get_site_root();
- $siteroot_real = @realpath($siteroot);
- if (!empty($siteroot_real))
- {
- $siteroot = $siteroot_real;
- }
- //First of all, do we have a VALID log folder?
- $config = $this->container->platform->getConfig();
- $log_dir = $config->get('log_path');
- if (!$log_dir || !@is_writable($log_dir))
- {
- return JText::_('COM_ADMINTOOLS_ERR_CONTROLPANEL_JCONFIG_INVALID_LOGDIR');
- }
- if ($siteroot == $log_dir)
- {
- return JText::_('COM_ADMINTOOLS_ERR_CONTROLPANEL_JCONFIG_LOGDIR_SITEROOT');
- }
- // Do we have a VALID tmp folder?
- $tmp_dir = $config->get('tmp_path');
- if (!$tmp_dir || !@is_writable($tmp_dir))
- {
- return JText::_('COM_ADMINTOOLS_ERR_CONTROLPANEL_JCONFIG_INVALID_TMPDIR');
- }
- if ($siteroot == $tmp_dir)
- {
- return JText::_('COM_ADMINTOOLS_ERR_CONTROLPANEL_JCONFIG_TMPDIR_SITEROOT');
- }
- return false;
- }
- /**
- * Do I need to show the Quick Setup Wizard?
- *
- * @return bool
- */
- public function needsQuickSetupWizard()
- {
- $params = Storage::getInstance();
- return $params->getValue('quickstart', 0) == 0;
- }
- /**
- * Get the most likely visitor IP address, reported by the server
- *
- * @return string
- */
- public function getVisitorIP()
- {
- $internalIP = Ip::getIp();
- if ((strpos($internalIP, '::') === 0) && (strstr($internalIP, '.') !== false))
- {
- $internalIP = substr($internalIP, 2);
- }
- return $internalIP;
- }
- /**
- * Checks if we have detected private network IPs AND the IP Workaround feature is turned off
- *
- * @return bool
- */
- public function needsIpWorkarounds()
- {
- $WAFparams = Storage::getInstance();
- $params = $this->container->params;
- // If IP Workarounds is disabled AND we have detected private IPs, show the warning
- if (!$WAFparams->getValue('ipworkarounds', -1) && ($params->get('detected_exceptions_from_private_network') === 1))
- {
- return true;
- }
- return false;
- }
- /**
- * Sets the IP workarounds or ignores the warning
- *
- * @param $state
- */
- public function setIpWorkarounds($state)
- {
- if ($state)
- {
- $WAFparams = Storage::getInstance();
- $WAFparams->setValue('ipworkarounds', 1, true);
- }
- else
- {
- // If we user wants to ignore the warning, let's set the flag to -1 (ignore)
- $params = $this->container->params;
- $params->set('detected_exceptions_from_private_network', -1);
- $params->save();
- }
- }
- /**
- * Is the System - Admin Tools plugin installed?
- *
- * @return bool
- *
- * @since 4.3.0
- */
- public function isPluginInstalled()
- {
- $this->getPluginID();
- return self::$pluginId != 0;
- }
- /**
- * Is the System - Admin Tools plugin currently loaded?
- *
- * @return bool
- *
- * @since 4.3.0
- */
- public function isPluginLoaded()
- {
- return class_exists('plgSystemAdmintools');
- }
- /**
- * Is the main.php file renamed?
- *
- * @return bool
- *
- * @since 4.3.0
- */
- public function isMainPhpDisabled()
- {
- $folder = JPATH_PLUGINS . '/system/admintools/admintools';
- return @is_dir($folder) && !@file_exists($folder . '/main.php');
- }
- /**
- * Rename the disabled main.php file back to its proper, main.php, name.
- *
- * @return bool
- *
- * @since 4.3.0
- */
- public function reenableMainPhp()
- {
- $altName = $this->getRenamedMainPhp();
- if (!$altName)
- {
- return false;
- }
- $folder = JPATH_PLUGINS . '/system/admintools/admintools';
- $from = $folder . '/' . $altName;
- $to = $folder . '/main.php';
- $res = @rename($from, $to);
- if (!$res)
- {
- $res = @copy($from, $to);
- if ($res)
- {
- @unlink($from);
- }
- }
- if (!$res)
- {
- $res = \JFile::copy($from, $to);
- if ($res)
- {
- \JFile::delete($from);
- }
- }
- return $res;
- }
- /**
- * Get the file name under which main.php has been renamed to
- *
- * @return string|null
- *
- * @since 4.3.0
- */
- public function getRenamedMainPhp()
- {
- $possibleNames = array(
- 'main-disable.php',
- 'main.php.bak',
- 'main.bak.php',
- 'main.bak',
- '-main.php',
- );
- $folder = JPATH_PLUGINS . '/system/admintools/admintools';
- foreach ($possibleNames as $baseName)
- {
- if (@file_exists($folder . '/' . $baseName))
- {
- return $baseName;
- }
- }
- return null;
- }
- }