D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
local
/
lsws
/
add-ons
/
webcachemgr
/
src
/
Panel
/
Filename :
ControlPanel.php
back
Copy
<?php /** ****************************************** * LiteSpeed Web Server Cache Manager * * @author Michael Alegre * @copyright 2017-2024 LiteSpeed Technologies, Inc. * ******************************************* */ namespace Lsc\Wp\Panel; use DirectoryIterator; use Exception; use Lsc\Wp\Logger; use Lsc\Wp\LSCMException; use Lsc\Wp\Util; use Lsc\Wp\WPInstall; abstract class ControlPanel { /** * @deprecated * * @var string */ const PANEL_CPANEL = 'whm'; /** * @deprecated * * @var string */ const PANEL_PLESK = 'plesk'; /** * @var string */ const PANEL_API_VERSION = '1.17.1.1'; /** * @since 1.9 * @var int */ const PANEL_API_VERSION_SUPPORTED = 0; /** * @since 1.9 * @var int */ const PANEL_API_VERSION_TOO_LOW = 1; /** * @since 1.9 * @var int */ const PANEL_API_VERSION_TOO_HIGH = 2; /** * @since 1.9 * @var int */ const PANEL_API_VERSION_UNKNOWN = 3; /** * @var int */ const PHP_TIMEOUT = 30; /** * @var string */ const NOT_SET = '__LSC_NOTSET__'; /** * @var string */ protected $panelName = ''; /** * @var string */ protected $phpOptions; /** * @var null|string */ protected $serverCacheRoot; /** * @var null|string */ protected $vhCacheRoot; /** * @var string */ protected $defaultSvrCacheRoot; /** * @var string */ protected $apacheConf; /** * @var string */ protected $apacheVHConf; /** * @var null|array[] 'docroots' => (index => docroots), * 'names' => (servername => index) */ protected $docRootMap = null; /** * @since 1.9.7 * @var string */ protected static $minAPIFilePath = ''; /** * @var null|ControlPanel Object that extends ControlPanel abstract class. */ protected static $instance; /** * * @throws LSCMException Thrown indirectly by $this->init2() call. */ protected function __construct() { $this->init2(); } /** * Temporary function name until existing deprecated public static init() * function is removed. * * @since 1.13.2 * * @throws LSCMException Thrown indirectly by $this->initConfPaths() call. */ protected function init2() { /** * output_handler value cleared to avoid compressed output through * 'ob_gzhandler' etc. */ $this->phpOptions = '-d disable_functions=ini_set -d opcache.enable=0 ' . '-d max_execution_time=' . static::PHP_TIMEOUT . ' -d memory_limit=512M -d register_argc_argv=1 ' . '-d zlib.output_compression=0 -d output_handler= ' . '-d safe_mode=0 -d open_basedir='; $this->initConfPaths(); } /** * Deprecated 02/04/19 as this function will be made private. * Use getClassInstance() with a fully qualified class name as a parameter * instead. * * Sets static::$instance with a new $className instance if it has not been * set already. An exception will be thrown if static::$instance has already * been set to a different class name than the one provided. * * @deprecated * * @param string $className A fully qualified control panel class name. * * @return ControlPanel|null Object that extends ControlPanel abstract * class. * * @throws LSCMException Thrown when unable to include custom panel file. * @throws LSCMException Thrown when class 'CustomPanel' does not extend * class '\Lsc\Wp\Panel\CustomPanelBase'. * @throws LSCMException Re-thrown when "new $className()" call throws an * exception. * @throws LSCMException Thrown when an instance of a different * ControlPanel/CustomPanel extending class has already been created. */ public static function initByClassName( $className ) { if ( static::$instance == null ) { if ( $className == 'custom' ) { $lsws_home = realpath(__DIR__ . '/../../../../'); $customPanelFile = "$lsws_home/admin/lscdata/custom/CustomPanel.php"; if ( ! file_exists($customPanelFile) || ! include_once $customPanelFile ) { throw new LSCMException( "Unable to include file $customPanelFile" ); } $className = '\Lsc\Wp\Panel\CustomPanel'; $isSubClass = is_subclass_of( $className, '\Lsc\Wp\Panel\CustomPanelBase' ); if ( ! $isSubClass ) { throw new LSCMException( 'Class CustomPanel must extend class ' . '\Lsc\Wp\Panel\CustomPanelBase' ); } } try{ static::$instance = new $className(); } catch ( Exception $e ){ throw new LSCMException( "Could not create object with class name $className. " . "Error: {$e->getMessage()}" ); } } else { $instanceClassName = '\\' . get_class(static::$instance); if ( $instanceClassName != $className ) { throw new LSCMException( "Could not initialize $className instance as an instance " . "of another class ($instanceClassName) has already " . 'been created.' ); } } return static::$instance; } /** * Deprecated 01/14/19. Use initByClassName() instead. * * @deprecated * * @param string $name * * @return ControlPanel Object that extends ControlPanel abstract class. * * @throws LSCMException Thrown when static::$instance is not null. * @throws LSCMException Thrown when provided $name is not recognized. * @throws LSCMException Thrown indirectly by static::initByClassName() * call. */ public static function init( $name ) { if ( static::$instance != null ) { throw new LSCMException( 'ControlPanel cannot be initialized twice.' ); } switch ($name) { case static::PANEL_CPANEL: $className = 'CPanel'; break; case static::PANEL_PLESK: $className = 'Plesk'; break; default: throw new LSCMException( "Control panel '$name' is not supported." ); } return static::initByClassName("\Lsc\Wp\Panel\\$className"); } /** * Returns current ControlPanel instance when no $className is given. When * $className is provided, an instance of $className will also be * initialized if it has not yet been initialized already. * * @param string $className Fully qualified class name. * * @return ControlPanel Object that extends ControlPanel abstract class. * * @throws LSCMException Thrown when static::$instance is null. * @throws LSCMException Thrown indirectly by static::initByClassName() * call. */ public static function getClassInstance( $className = '' ) { if ( $className != '' ) { static::initByClassName($className); } elseif ( static::$instance == null ) { throw new LSCMException( 'Could not get instance, ControlPanel not initialized. ' ); } return static::$instance; } /** * Deprecated on 02/06/19. Use getClassInstance() instead. * * @deprecated * * @return ControlPanel Object that extends ControlPanel abstract class. * * @throws LSCMException Thrown indirectly by static::getClassInstance() * call. */ public static function getInstance() { return static::getClassInstance(); } /** * * @param string $serverName * * @return string|null * * @throws LSCMException Thrown indirectly by $this->prepareDocrootMap() * call. */ public function mapDocRoot( $serverName ) { if ( $this->docRootMap == null ) { $this->prepareDocrootMap(); } if ( isset($this->docRootMap['names'][$serverName]) ) { $index = $this->docRootMap['names'][$serverName]; return $this->docRootMap['docroots'][$index]; } // error out return null; } /** * * @return bool * * @throws LSCMException Thrown indirectly by $this->getServerCacheRoot() * call. * @throws LSCMException Thrown indirectly by $this->getVHCacheRoot() call. */ public function areCacheRootsSet() { $ret = true; if ( static::NOT_SET == $this->getServerCacheRoot() ) { $ret = false; } if ( static::NOT_SET == $this->getVHCacheRoot() ) { $ret = false; } return $ret; } /** * * @throws LSCMException Thrown when cache is not enabled for the current * LiteSpeed license. * @throws LSCMException Thrown indirectly by $this->getServerCacheRoot() * call. * @throws LSCMException Thrown indirectly by $this->setServerCacheRoot() * call. * @throws LSCMException Thrown indirectly by $this->getVHCacheRoot() call. * @throws LSCMException Thrown indirectly by $this->setVHCacheRoot() call. */ public function verifyCacheSetup() { if ( !$this->isCacheEnabled() ) { throw new LSCMException( 'LSCache is not included in the current LiteSpeed license. ' . 'Please purchase the LSCache add-on or upgrade to a ' . 'license type that includes LSCache and try again.', LSCMException::E_PERMISSION ); } $restartRequired = false; if ( static::NOT_SET == $this->getServerCacheRoot() ) { $this->setServerCacheRoot(); $restartRequired = true; } if ( static::NOT_SET == $this->getVHCacheRoot() ) { $this->setVHCacheRoot(); $restartRequired = true; } if ( $restartRequired ) { Util::restartLsws(); } } /** * * @param string $vhCacheRoot * * @throws LSCMException Thrown indirectly by $this->log() call. * @throws LSCMException Thrown indirectly by $this->writeVHCacheRoot() * call. * @throws LSCMException Thrown indirectly by $this->log() call. * @throws LSCMException Thrown indirectly by $this->applyVHConfChanges() * call. */ public function setVHCacheRoot( $vhCacheRoot = 'lscache' ) { $this->log('Attempting to set VH cache root...', Logger::L_VERBOSE); if ( !file_exists($this->apacheVHConf) ) { $this->createVHConfAndSetCacheRoot( $this->apacheVHConf, $vhCacheRoot ); } else { $this->writeVHCacheRoot($this->apacheVHConf, $vhCacheRoot); } $this->vhCacheRoot = $vhCacheRoot; $this->log( "Virtual Host cache root set to $vhCacheRoot", Logger::L_INFO ); if ( $this->vhCacheRoot[0] == '/' && !file_exists($this->vhCacheRoot) ) { /** * 01/29/19: Temporarily create top virtual host cache root * directory to avoid LSWS setting incorrect owner/group and * permissions for the directory outside the cage. */ mkdir(str_replace('/$vh_user', '', $vhCacheRoot), 0755, true); } $this->applyVHConfChanges(); } /** * * @return bool * * @throws LSCMException Thrown when status file is not found. * @throws LSCMException Thrown when status file cannot be read. * */ public function isCacheEnabled() { $statusFile = '/tmp/lshttpd/.status'; if ( !file_exists($statusFile) ) { throw new LSCMException( 'Cannot determine LSCache availability. Please start/switch to ' . 'LiteSpeed Web Server before trying again.', LSCMException::E_PERMISSION ); } if ( ($f = fopen($statusFile, 'r')) === false ) { throw new LSCMException( 'Cannot determine LSCache availability.', LSCMException::E_PERMISSION ); } fseek($f, -128, SEEK_END); $line = fread($f, 128); fclose($f); if ( preg_match('/FEATURES: ([0-9.]+)/', $line, $m) && ($m[1] & 1) == 1 ) { return true; } return false; } /** * return array of docroots, can set index from and batch * * @param int $offset * @param null|int $length * * @return string[] * * @throws LSCMException Thrown indirectly by $this->prepareDocrootMap() * call. */ public function getDocRoots( $offset = 0, $length = null ) { if ( $this->docRootMap == null ) { $this->prepareDocrootMap(); } return array_slice($this->docRootMap['docroots'], $offset, $length); } /** * Used in PleskEscalate. * * @return array[] * * @noinspection PhpUnused * @noinspection PhpDocMissingThrowsInspection */ public function getDocrootMap() { if ( $this->docRootMap == null ) { /** * LSCMException not thrown in Plesk implementation. * @noinspection PhpUnhandledExceptionInspection */ $this->prepareDocrootMap(); } return $this->docRootMap; } /** * * @return string */ public function getDefaultSvrCacheRoot() { return $this->defaultSvrCacheRoot; } /** * * @return string * * @throws LSCMException Thrown indirectly by $this->initCacheRoots() call. */ public function getServerCacheRoot() { if ( $this->serverCacheRoot == null ) { $this->initCacheRoots(); } return $this->serverCacheRoot; } /** * * @return string * * @throws LSCMException Thrown indirectly by $this->initCacheRoots() call. */ public function getVHCacheRoot() { if ( $this->vhCacheRoot == null ) { $this->initCacheRoots(); } return $this->vhCacheRoot; } /** * * @return void * * @throws LSCMException Thrown in some existing implementations. */ abstract protected function initConfPaths(); /** * * @throws LSCMException Thrown in some existing implementations. */ abstract protected function prepareDocrootMap(); /** * * @param WPInstall $wpInstall * * @return string * * @throws LSCMException Thrown in some existing implementations. */ abstract public function getPhpBinary( WPInstall $wpInstall ); /** * Searches the given directories '.conf' files for CacheRoot setting. * * Note: Visibility is public to better accommodate escalation functions. * * @param string $confDir Directory to be searched. * * @return string */ public function cacheRootSearch( $confDir ) { $files = new DirectoryIterator($confDir); foreach ( $files as $file ) { $filename = $file->getFilename(); if ( strlen($filename) > 5 && substr_compare($filename, '.conf', -5) === 0 ) { $cacheRoot = $this->getCacheRootSetting($file->getPathname()); if ( $cacheRoot != '' ) { return $cacheRoot; } } } return ''; } /** * Note: Visibility is public to better accommodate escalation functions. * * @param string $file * * @return string */ public function getCacheRootSetting( $file ) { if ( file_exists($file) ) { $matchFound = preg_match( '/^\s*CacheRoot (.+)/im', file_get_contents($file), $matches ); if ( $matchFound ) { return trim($matches[1]); } } return ''; } /** * Note: Visibility is public to better accommodate escalation functions. * * @return string */ public function getLSWSCacheRootSetting() { $serverConf = __DIR__ . '/../../../../conf/httpd_config.xml'; if ( file_exists($serverConf) ) { $matchFound = preg_match( '!<cacheStorePath>(.+)</cacheStorePath>!i', file_get_contents($serverConf), $matches ); if ( $matchFound ) { return trim($matches[1]); } } return ''; } abstract protected function serverCacheRootSearch(); abstract protected function vhCacheRootSearch(); /** * Checks server and VH conf files for cacheroot settings and populates in * object if found. * * @throws LSCMException Thrown indirectly by $this->log() call. * @throws LSCMException Thrown indirectly by $this->log() call. * @throws LSCMException Thrown indirectly by $this->log() call. * @throws LSCMException Thrown indirectly by $this->log() call. */ protected function initCacheRoots() { $svrCacheRoot = $this->serverCacheRootSearch(); if ( $svrCacheRoot == '' ) { $svrCacheRoot = $this->getLSWSCacheRootSetting(); } $vhCacheRoot = $this->vhCacheRootSearch(); if ( $svrCacheRoot ) { $this->serverCacheRoot = $svrCacheRoot; $this->log( "Server level cache root is $svrCacheRoot.", Logger::L_DEBUG ); } else { $this->serverCacheRoot = static::NOT_SET; $this->log('Server level cache root is not set.', Logger::L_NOTICE); } if ( $vhCacheRoot ) { $this->vhCacheRoot = $vhCacheRoot; $this->log( "Virtual Host level cache root is $vhCacheRoot.", Logger::L_DEBUG ); } else { $this->vhCacheRoot = static::NOT_SET; $this->log( 'Virtual Host level cache root is not set.', Logger::L_INFO ); } } /** * * @param string $msg * @param int $level * * @throws LSCMException Thrown indirectly by Logger::error() call. * @throws LSCMException Thrown indirectly by Logger::warn() call. * @throws LSCMException Thrown indirectly by Logger::notice() call. * @throws LSCMException Thrown indirectly by Logger::info() call. * @throws LSCMException Thrown indirectly by Logger::verbose() call. * @throws LSCMException Thrown indirectly by Logger::debug() call. */ protected function log( $msg, $level ) { $msg = "$this->panelName - $msg"; switch ($level) { case Logger::L_ERROR: Logger::error($msg); break; case Logger::L_WARN: Logger::warn($msg); break; case Logger::L_NOTICE: Logger::notice($msg); break; case Logger::L_INFO: Logger::info($msg); break; case Logger::L_VERBOSE: Logger::verbose($msg); break; case Logger::L_DEBUG: Logger::debug($msg); break; //no default } } /** * * @param string $svrCacheRoot * * @throws LSCMException Thrown directly and indirectly. */ public function setServerCacheRoot( $svrCacheRoot = '' ) { $this->log('Attempting to set server cache root...', Logger::L_VERBOSE); if ( $svrCacheRoot != '' ) { $cacheroot = $svrCacheRoot; } else { $cacheroot = $this->defaultSvrCacheRoot; } $cacheRootLine = "<IfModule LiteSpeed>\nCacheRoot $cacheroot\n</IfModule>\n\n"; if ( !file_exists($this->apacheConf) ) { file_put_contents($this->apacheConf, $cacheRootLine); chmod($this->apacheConf, 0644); $this->log("Created file $this->apacheConf", Logger::L_VERBOSE); } else { if ( !is_writable($this->apacheConf) ) { throw new LSCMException( 'Apache Config is not writeable. No changes made.' ); } if ( !Util::createBackup($this->apacheConf) ) { throw new LSCMException( 'Could not backup Apache config. No changes made.' ); } else { $file_contents = file($this->apacheConf); $pattern = '/^\s*<IfModule +LiteSpeed *>/im'; if ( preg_grep($pattern, $file_contents) ) { if ( preg_grep('/^\s*CacheRoot +/im', $file_contents) ) { $file_contents = preg_replace( '/^\s*CacheRoot +.+/im', "CacheRoot $cacheroot", $file_contents ); } else { $file_contents = preg_replace( '/^\s*<IfModule +LiteSpeed *>/im', "<IfModule LiteSpeed>\nCacheRoot $cacheroot", $file_contents ); } } else { array_unshift($file_contents, $cacheRootLine); } file_put_contents($this->apacheConf, $file_contents); } } $this->serverCacheRoot = $cacheroot; $this->log("Server level cache root set to $cacheroot", Logger::L_INFO); if ( file_exists($cacheroot) ) { exec("/bin/rm -rf $cacheroot"); $this->log( 'Server level cache root directory removed for proper ' . 'permission.', Logger::L_DEBUG ); } } /** * * @param array $file_contents * @param string $vhCacheRoot * * @return array */ abstract protected function addVHCacheRootSection( array $file_contents, $vhCacheRoot = 'lscache' ); /** * * @param string $vhConf * * @throws LSCMException Thrown when virtual host conf file is not * writeable. * @throws LSCMException Thrown when a backup of the virtual host conf file * could not be made. * @throws LSCMException Thrown when "write to virtual host conf file" call * fails. * @throws LSCMException Thrown indirectly by $this->log() call. */ public function writeVHCacheRoot( $vhConf, $vhCacheRoot = 'lscache' ) { if ( !is_writable($vhConf) ) { throw new LSCMException( "Could not write to VH config $vhConf. No changes made.", LSCMException::E_PERMISSION ); } if ( !Util::createBackup($vhConf) ) { throw new LSCMException( "Could not backup Virtual Host config file $vhConf. No " . 'changes made.', LSCMException::E_PERMISSION ); } $file_contents = file($vhConf); if ( preg_grep('/^\s*<IfModule +LiteSpeed *>/im', $file_contents) ) { if ( preg_grep('/^\s*CacheRoot +/im', $file_contents) ) { $modified_contents = preg_replace( '/^\s*CacheRoot +.+/im', "CacheRoot $vhCacheRoot", $file_contents ); } else { $modified_contents = preg_replace( '/^\s*<IfModule +LiteSpeed *>/im', "<IfModule LiteSpeed>\nCacheRoot $vhCacheRoot", $file_contents ); } } else { $modified_contents = $this->addVHCacheRootSection($file_contents, $vhCacheRoot); } if ( file_put_contents($vhConf, $modified_contents) === false ) { throw new LSCMException( "Failed to write to file $vhConf.", LSCMException::E_PERMISSION ); } $this->log("Updated file $vhConf.", Logger::L_DEBUG); } /** * Note: Visibility is public to better accommodate escalation functions. * * @param string $vhConf * @param string $vhCacheRoot */ abstract public function createVHConfAndSetCacheRoot( $vhConf, $vhCacheRoot = 'lscache' ); /** * Note: Visibility is public to better accommodate escalation functions. * * @throws LSCMException Thrown by some implementations. */ abstract public function applyVHConfChanges(); /** * * @since 1.9.7 */ protected static function setMinAPIFilePath() { static::$minAPIFilePath = realpath(__DIR__ . '/../..') . '/MIN_VER'; } /** * * @since 1.9.7 * * @return string */ protected static function getMinAPIFilePath() { if ( static::$minAPIFilePath == '' ) { static::setMinAPIFilePath(); } return static::$minAPIFilePath; } /** * * @since 1.9.7 * @since 1.12 Changed visibility from protected to public. */ public static function populateMinAPIVerFile() { $minVerFile = static::getMinAPIFilePath(); $content = Util::get_url_contents( 'https://www.litespeed.sh/sub/shared/MIN_VER' ); if ( !empty($content) ) { file_put_contents($minVerFile, $content); } else { touch($minVerFile); } } /** * * @since 1.9.7 * * @return string */ protected static function getMinAPIVer() { $minVerFile = static::getMinAPIFilePath(); clearstatcache(); if ( !file_exists($minVerFile) || (time() - filemtime($minVerFile)) > 86400 ) { static::populateMinAPIVerFile(); } if ( ($content = file_get_contents($minVerFile)) !== false ) { return trim($content); } return ''; } /** * * @since 1.9.7 * * @return bool */ public static function meetsMinAPIVerRequirement() { $minAPIVer = static::getMinAPIVer(); if ( $minAPIVer == '' || Util::betterVersionCompare(static::PANEL_API_VERSION, $minAPIVer, '<') ) { return false; } return true; } /** * * @since 1.9 * * @param string $panelAPIVer Shared code API version used by the panel * plugin. * * @return int */ public static function checkPanelAPICompatibility( $panelAPIVer ) { $supportedAPIVers = array( '1.17.1.1', '1.17.1', '1.17.0.5', '1.17.0.4', '1.17.0.3', '1.17.0.2', '1.17.0.1', '1.17', '1.16.1', '1.16.0.2', '1.16.0.1', '1.16', '1.15.0.1', '1.15', '1.14.5', '1.14.4.1', '1.14.4', '1.14.3.2', '1.14.3.1', '1.14.3', '1.14.2', '1.14.1.2', '1.14.1.1', '1.14.1', '1.14.0.3', '1.14.0.2', '1.14.0.1', '1.14', '1.13.13.1', '1.13.13', '1.13.12', '1.13.11.1', '1.13.11', '1.13.10.2', '1.13.10.1', '1.13.10', '1.13.9', '1.13.8', '1.13.7.1', '1.13.7', '1.13.6', '1.13.5.2', '1.13.5.1', '1.13.5', '1.13.4.4', '1.13.4.3', '1.13.4.2', '1.13.4.1', '1.13.4', '1.13.3.1', '1.13.3', '1.13.2.2', '1.13.2.1', '1.13.2', '1.13.1', '1.13.0.3', '1.13.0.2', '1.13.0.1', '1.13', '1.12', '1.11', '1.10', '1.9.8', '1.9.7', '1.9.6.1', '1.9.6', '1.9.5', '1.9.4', '1.9.3', '1.9.2', '1.9.1', '1.9', '1.8', '1.7', '1.6.1', '1.6', '1.5', '1.4', '1.3', '1.2', '1.1', '1.0' ); if ( Util::betterVersionCompare($panelAPIVer, $supportedAPIVers[0], '>') ) { return static::PANEL_API_VERSION_TOO_HIGH; } elseif ( Util::betterVersionCompare($panelAPIVer, end($supportedAPIVers), '<') ) { return static::PANEL_API_VERSION_TOO_LOW; } elseif ( ! in_array($panelAPIVer, $supportedAPIVers) ) { return static::PANEL_API_VERSION_UNKNOWN; } else { return static::PANEL_API_VERSION_SUPPORTED; } } /** * * @deprecated 1.9 Use checkPanelAPICompatibility() instead. * * @param string $panelAPIVer Shared code API version used by the panel * plugin. * * @return bool */ public static function isPanelAPICompatible( $panelAPIVer ) { $apiCompatStatus = static::checkPanelAPICompatibility($panelAPIVer); if ( $apiCompatStatus != static::PANEL_API_VERSION_SUPPORTED ) { return false; } return true; } }