Viewing file: WPInstallStorage.php (26.7 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/** ********************************************* * LiteSpeed Web Server Cache Manager * * @author LiteSpeed Technologies, Inc. (https://www.litespeedtech.com) * @copyright (c) 2018-2021 * ******************************************* */
namespace Lsc\Wp;
use Lsc\Wp\Context\Context; use Lsc\Wp\Panel\ControlPanel;
/** * map to data file */ class WPInstallStorage {
/** * @var string */ const CMD_ADD_CUST_WPINSTALLS = 'addCustWPInstalls';
/** * @since 1.13.3 * @var string */ const CMD_ADD_NEW_WPINSTALL = 'addNewWPInstall';
/** * @deprecated 1.13.3 Use CMD_DISCOVER_NEW2 instead. * @var string */ const CMD_DISCOVER_NEW = 'discoverNew';
/** * @since 1.13.3 * @var string */ const CMD_DISCOVER_NEW2 = 'discoverNew2';
/** * @var string */ const CMD_FLAG = 'flag';
/** * @var string */ const CMD_MASS_UNFLAG = 'mass_unflag';
/** * @deprecated 1.13.3 Use CMD_SCAN2 instead for now. * @var string */ const CMD_SCAN = 'scan';
/** * @since 1.13.3 * @var string */ const CMD_SCAN2 = 'scan2';
/** * @var string */ const CMD_UNFLAG = 'unflag';
/** * @var string */ const DATA_VERSION = '1.5';
/** * @var int */ const ERR_NOT_EXIST = 1;
/** * @var int */ const ERR_CORRUPTED = 2;
/** * @var int */ const ERR_VERSION_HIGH = 3;
/** * @var int */ const ERR_VERSION_LOW = 4;
/** * @var string */ protected $dataFile;
/** * @var string */ protected $customDataFile;
/** * @var null|WPInstall[] Key is path */ protected $wpInstalls = null;
/** * @var null|WPInstall[] Key is path */ protected $custWpInstalls = null;
/** * @var int */ protected $error;
/** * @var WPInstall[] */ protected $workingQueue = array();
/** * * @param string $dataFile * @param string $custDataFile * @throws LSCMException Thrown indirectly. */ public function __construct( $dataFile, $custDataFile = '' ) { $this->dataFile = $dataFile; $this->customDataFile = $custDataFile; $this->error = $this->init(); }
/** * * @return int * @throws LSCMException Thrown indirectly. */ protected function init() { $dataExists = false;
try { if ( file_exists($this->dataFile) ) { $dataExists = true; $this->wpInstalls = $this->getDataFileData($this->dataFile); }
if ( $this->customDataFile != '' && file_exists($this->customDataFile) ) {
$dataExists = true; $this->custWpInstalls = $this->getDataFileData($this->customDataFile); } } catch ( LSCMException $e ) { Logger::debug($e->getMessage()); return $e->getCode(); }
if ( !$dataExists ) { return self::ERR_NOT_EXIST; }
return 0; }
/** * * @param string $dataFile * @return WPInstall[] * @throws LSCMException Thrown indirectly. */ protected function getDataFileData( $dataFile ) { $content = file_get_contents($dataFile);
if ( ($data = json_decode($content, true)) === null ) { /* * Data file may be in old serialized format. Try unserializing. */ $data = unserialize($content); }
if ( $data === false || !is_array($data) || !isset($data['__VER__']) ) { throw new LSCMException( "$dataFile - Data is corrupt.", self::ERR_CORRUPTED ); }
if ( ($err = $this->verifyDataFileVer($dataFile, $data['__VER__'])) ) { throw new LSCMException( "$dataFile - Data file version issue.", $err ); }
unset($data['__VER__']);
$wpInstalls = array();
foreach ( $data as $utf8Path => $idata ) { $path = utf8_decode($utf8Path); $i = new WPInstall($path);
$idata[WPInstall::FLD_SITEURL] = urldecode($idata[WPInstall::FLD_SITEURL]); $idata[WPInstall::FLD_SERVERNAME] = urldecode($idata[WPInstall::FLD_SERVERNAME]);
$i->initData($idata); $wpInstalls[$path] = $i; }
return $wpInstalls; }
/** * * @return int */ public function getError() { return $this->error; }
/** * * @param bool $nonFatalOnly * @return int */ public function getCount( $nonFatalOnly = false ) { $count = 0;
if ( !$nonFatalOnly ) {
if ( $this->wpInstalls != null ) { $count += count($this->wpInstalls); }
if ( $this->custWpInstalls != null ) { $count += count($this->custWpInstalls); } } else {
foreach ( $this->wpInstalls as $install ) {
if ( !$install->hasFatalError() ) { $count++; } }
foreach ( $this->custWpInstalls as $custInstall ) {
if ( !$custInstall->hasFatalError() ) { $count++; } } }
return $count; }
/** * * @return null|WPInstall[] * * @noinspection PhpUnused */ public function getWPInstalls() { return $this->wpInstalls; }
/** * * @return null|WPInstall[] * * @noinspection PhpUnused */ public function getCustWPInstalls() { return $this->custWpInstalls; }
/** * * @return null|WPInstall[] */ public function getAllWPInstalls() { if ( $this->wpInstalls != null ) {
if ( $this->custWpInstalls != null ) { return array_merge($this->wpInstalls, $this->custWpInstalls); } else { return $this->wpInstalls; } } elseif ( $this->custWpInstalls != null ) { return $this->custWpInstalls; } else { return null; } }
/** * Get all known WPInstall paths. * * @return string[] */ public function getPaths() { $paths = array();
if ( $this->wpInstalls != null ) { $paths = array_keys($this->wpInstalls); }
if ( $this->custWpInstalls != null ) { $paths = array_merge($paths, array_keys($this->custWpInstalls)); }
return $paths; }
/** * * @param string $path * @return WPInstall|null */ public function getWPInstall( $path ) { if ( ($realPath = realpath($path)) === false ) { $index = $path; } else { $index = $realPath; }
if ( isset($this->wpInstalls[$index]) ) { return $this->wpInstalls[$index]; } elseif ( isset($this->custWpInstalls[$index]) ) { return $this->custWpInstalls[$index]; }
return null; }
/** * * @return WPInstall[] */ public function getWorkingQueue() { return $this->workingQueue; }
/** * * @param WPInstall $wpInstall */ public function addWPInstall( WPInstall $wpInstall ) { $this->wpInstalls[$wpInstall->getPath()] = $wpInstall; }
/** * * @throws LSCMException Thrown indirectly. */ public function syncToDisk() { $this->saveDataFile($this->dataFile, $this->wpInstalls);
if ( $this->customDataFile != '' ) { $this->saveDataFile($this->customDataFile, $this->custWpInstalls); } }
/** * * @param string $dataFile * @param WPInstall[] $wpInstalls * @throws LSCMException Thrown indirectly. */ protected function saveDataFile( $dataFile, $wpInstalls ) { $data = array( '__VER__' => self::DATA_VERSION );
if ( !empty($wpInstalls) ) {
foreach ( $wpInstalls as $path => $install ) {
if ( !$install->shouldRemove() ) { $utf8Path = utf8_encode($path);
$data[$utf8Path] = $install->getData();
$siteUrl = &$data[$utf8Path][WPInstall::FLD_SITEURL]; $siteUrl = urlencode($siteUrl);
$serverName = &$data[$utf8Path][WPInstall::FLD_SERVERNAME]; $serverName = urlencode($serverName); } }
ksort($data); }
$file_str = json_encode($data); file_put_contents($dataFile, $file_str, LOCK_EX); chmod($dataFile, 0600);
$this->log("Data file saved $dataFile", Logger::L_DEBUG); }
/** * * @param string $dataFile * @param string $dataFileVer * @return int * @throws LSCMException Thrown indirectly. */ protected function verifyDataFileVer( $dataFile, $dataFileVer ) { $res = version_compare($dataFileVer, self::DATA_VERSION);
if ( $res == 1 ) { Logger::info( 'Data file version is higher than expected and cannot be used.' );
return self::ERR_VERSION_HIGH; }
if ( $res == -1 && !$this->updateDataFile($dataFile, $dataFileVer) ) { return self::ERR_VERSION_LOW; }
return 0; }
/** * * @param string $dataFile * @param string $dataFileVer * @return bool * @throws LSCMException Thrown indirectly. */ public static function updateDataFile( $dataFile, $dataFileVer ) { Logger::info( "$dataFile - Old data file version detected. Attempting to " . 'update...' );
/** * Currently no versions are upgradeable to 1.5 */ $updatableVersions = array();
if ( ! in_array($dataFileVer, $updatableVersions) || ! Util::createBackup($dataFile) ) {
Logger::error( "$dataFile - Data file could not be updated to version " . self::DATA_VERSION );
return false; }
/** * Upgrade funcs will be called here. */
return true; }
/** * * @param string $action * @return string[] * @throws LSCMException Thrown indirectly. */ protected function prepareActionItems( $action ) { switch ($action) { case self::CMD_SCAN: case self::CMD_SCAN2: case self::CMD_DISCOVER_NEW: case self::CMD_DISCOVER_NEW2:
try { return ControlPanel::getClassInstance()->getDocRoots(); } catch ( LSCMException $e ) { throw new LSCMException( $e->getMessage() . " Could not prepare $action action items." ); }
case UserCommand::CMD_MASS_ENABLE: case UserCommand::CMD_MASS_DISABLE: case UserCommand::CMD_MASS_UPGRADE: case UserCommand::CMD_MASS_DASH_NOTIFY: case UserCommand::CMD_MASS_DASH_DISABLE: case self::CMD_MASS_UNFLAG: return $this->getPaths(); default: throw new LSCMException('Missing parameter(s).'); } }
/** * * @param string $action * @param string $path * @param string[] $extraArgs * @throws LSCMException Thrown indirectly. */ protected function doWPInstallAction( $action, $path, $extraArgs ) { if ( ($wpInstall = $this->getWPInstall($path)) == null ) { $wpInstall = new WPInstall($path); $this->addWPInstall($wpInstall); }
switch ($action) {
case self::CMD_FLAG:
if ( !$wpInstall->hasValidPath() ) { return; }
if ( $wpInstall->addUserFlagFile(false) ) { $wpInstall->setCmdStatusAndMsg( UserCommand::EXIT_SUCC, 'Flag file set' ); } else { $wpInstall->setCmdStatusAndMsg( UserCommand::EXIT_FAIL, 'Could not create flag file' ); }
$this->workingQueue[$path] = $wpInstall;
return;
case self::CMD_UNFLAG: case self::CMD_MASS_UNFLAG:
if ( !$wpInstall->hasValidPath() ) { return; }
$wpInstall->removeFlagFile();
$wpInstall->setCmdStatusAndMsg( UserCommand::EXIT_SUCC, 'Flag file unset' );
$this->workingQueue[$path] = $wpInstall;
return;
case UserCommand::CMD_ENABLE: case UserCommand::CMD_DISABLE: case UserCommand::CMD_DASH_NOTIFY: case UserCommand::CMD_DASH_DISABLE:
if ( $wpInstall->hasFatalError() ) { $wpInstall->refreshStatus();
if ( $wpInstall->hasFatalError() ) { $wpInstall->addUserFlagFile(false);
$wpInstall->setCmdStatusAndMsg( UserCommand::EXIT_FAIL, 'Install skipped and flagged due to Error status.' );
$this->workingQueue[$path] = $wpInstall;
return; } }
break;
case UserCommand::CMD_MASS_UPGRADE: $allowedVers = PluginVersion::getInstance()->getAllowedVersions();
if ( !in_array($extraArgs[1], $allowedVers) ) { throw new LSCMException( 'Selected LSCWP version (' . htmlspecialchars($extraArgs[1]) . ') is invalid.' ); } break;
//no default }
if ( UserCommand::issue($action, $wpInstall, $extraArgs) ) {
if ( $action == UserCommand::CMD_MASS_UPGRADE && ($wpInstall->getCmdStatus() & UserCommand::EXIT_FAIL) && preg_match('/Download failed. Not Found/', $wpInstall->getCmdMsg()) ) {
$this->syncToDisk();
throw new LSCMException( 'Could not download version ' . htmlspecialchars($extraArgs[1]) . '.' ); }
if ( $action == UserCommand::CMD_MASS_ENABLE && ($wpInstall->getCmdStatus() & UserCommand::EXIT_FAIL) && preg_match('/Source Package not available/', $wpInstall->getCmdMsg()) ) {
$this->syncToDisk();
throw new LSCMException($wpInstall->getCmdMsg()); }
$this->workingQueue[$path] = $wpInstall; } }
/** * * @param string $action * @param null|string[] $list * @param string[]|string[][] $extraArgs * @return string[] * @throws LSCMException Thrown indirectly. */ public function doAction( $action, $list, $extraArgs = array() ) { if ( $list === null ) { $list = $this->prepareActionItems($action); }
$count = count($list); $this->log("doAction {$action} for {$count} items", Logger::L_VERBOSE); $endTime = $count > 1 ? Context::getActionTimeout() : 0; $finishedList = array();
switch ( $action ) {
case self::CMD_SCAN: case self::CMD_DISCOVER_NEW: $forceRefresh = ($action == self::CMD_SCAN);
foreach ( $list as $path ) { $this->scan($path, $forceRefresh);
$finishedList[] = $path;
if ( $endTime && time() >= $endTime ) { break; } }
break;
case self::CMD_ADD_NEW_WPINSTALL:
foreach ( $list as $path) { $this->addNewWPInstall($path);
$finishedList[] = $path;
if ( $endTime && time() >= $endTime ) { break; } }
break;
case self::CMD_ADD_CUST_WPINSTALLS: $wpInstallsInfo = $extraArgs[0];
$this->addCustomInstallations($wpInstallsInfo); break;
default:
if ( $action == UserCommand::CMD_ENABLE || $action == UserCommand::CMD_MASS_ENABLE ) {
/** * Ensure that current version is locally downloaded. */ $currVer = PluginVersion::getCurrentVersion(); PluginVersion::getInstance()->setActiveVersion($currVer); }
foreach ( $list as $path ) { $this->doWPInstallAction($action, $path, $extraArgs);
$finishedList[] = $path;
if ( $endTime && time() >= $endTime ) { break; } } }
$this->syncToDisk();
if ( $action == self::CMD_SCAN || $action == self::CMD_SCAN2 ) { /** * Explicitly clear any data file errors after scanning in case of * multiple actions performed in the same process (cli). */ $this->error = 0; }
return $finishedList; }
/** * * @deprecated 1.13.3 Use $this->scan2() instead. * * @param string $docroot * @param bool $forceRefresh * @return void * @throws LSCMException Thrown indirectly. */ protected function scan( $docroot, $forceRefresh = false ) { $depth = Context::getScanDepth(); $cmd = "find -L $docroot -maxdepth $depth -name wp-admin -print"; $directories = shell_exec($cmd);
/** * Example: * /home/user/public_html/wordpress/wp-admin * /home/user/public_html/blog/wp-admin * /home/user/public_html/wp/wp-admin */ $hasMatches = preg_match_all( "|$docroot(.*)(?=/wp-admin)|", $directories, $matches );
if ( ! $hasMatches ) { /** * Nothing found. */ return; }
foreach ( $matches[1] as $path ) { $wp_path = realpath($docroot . $path); $refresh = $forceRefresh;
if ( !isset($this->wpInstalls[$wp_path]) ) { $this->wpInstalls[$wp_path] = new WPInstall($wp_path); $refresh = true; $this->log( "New installation found: $wp_path", Logger::L_INFO );
if ( $this->custWpInstalls != null && isset($this->custWpInstalls[$wp_path]) ) {
unset($this->custWpInstalls[$wp_path]);
$this->log( "Installation removed from custom data file: $wp_path", Logger::L_INFO ); } } else { $this->log( "Installation already found: $wp_path", Logger::L_DEBUG ); }
if ( $refresh ) { $this->wpInstalls[$wp_path]->refreshStatus(); $this->workingQueue[$wp_path] = $this->wpInstalls[$wp_path]; } } }
/** * * @since 1.13.3 * * @param string $docroot * @return string[] * @throws LSCMException Thrown indirectly. */ public function scan2( $docroot ) { $depth = Context::getScanDepth(); $cmd = "find -L $docroot -maxdepth $depth -name wp-admin -print"; $directories = shell_exec($cmd);
/** * Example: * /home/user/public_html/wordpress/wp-admin * /home/user/public_html/blog/wp-admin * /home/user/public_html/wp/wp-admin */ $hasMatches = preg_match_all( "|$docroot(.*)(?=/wp-admin)|", $directories, $matches );
if ( ! $hasMatches ) { /** * Nothing found. */ return array(); }
$wpPaths = array();
foreach ( $matches[1] as $path ) { $wpPath = realpath($docroot . $path); $wpPaths[] = $wpPath; }
return $wpPaths; }
/** * Add a new WPInstall object to WPInstallStorage's $wpInstalls[] given a * path to a WordPress installation and refresh it's status . If a WPInstall * object already exists for the given path, refresh it's status. * * @since 1.13.3 * * @param string $wpPath * @throws LSCMException Thrown indirectly. */ protected function addNewWPInstall( $wpPath ) { if ( ($realPath = realpath($wpPath)) !== false ) { $wpPath = $realPath; }
if ( !isset($this->wpInstalls[$wpPath]) ) { $this->wpInstalls[$wpPath] = new WPInstall($wpPath); $this->log("New installation found: $wpPath", Logger::L_INFO);
if ( $this->custWpInstalls != null && isset($this->custWpInstalls[$wpPath]) ) {
unset($this->custWpInstalls[$wpPath]);
$this->log( "Installation removed from custom data file: $wpPath", Logger::L_INFO ); } } else { $this->log( "Installation already found: $wpPath", Logger::L_DEBUG ); }
$this->wpInstalls[$wpPath]->refreshStatus(); $this->workingQueue[$wpPath] = $this->wpInstalls[$wpPath]; }
/** * * @param string[] $wpInstallsInfo * @return void * @throws LSCMException Thrown indirectly. */ protected function addCustomInstallations( $wpInstallsInfo ) { if ( $this->customDataFile == '' ) { $this->log( 'No custom data file set, could not add custom Installation.', Logger::L_INFO );
return; }
if ( $this->custWpInstalls == null ) { $this->custWpInstalls = array(); }
$count = count($wpInstallsInfo);
for ( $i = 0; $i < $count; $i++ ) { $infoString = $wpInstallsInfo[$i]; $info = preg_split('/\s+/', trim($infoString));
$line = $i + 1;
if ( count($info) != 4 ) { $this->log( 'Incorrect number of values for custom installation input ' . "string on line $line. Skipping.", Logger::L_INFO );
continue; }
$wpPath = $info[0]; $docroot = $info[1]; $serverName = $info[2]; $siteUrl = $info[3];
if ( !file_exists("$wpPath/wp-admin") ) { $this->log( "No 'wp-admin' directory found for $wpPath on line " . "$line. Skipping.", Logger::L_INFO );
continue; }
if ( !(substr($wpPath, 0, strlen($docroot)) === $docroot) ) { $this->log( "docroot not contained in $wpPath on line $line. " . 'Skipping.', Logger::L_INFO );
continue; }
if ( !isset($this->wpInstalls[$wpPath]) ) { $this->custWpInstalls[$wpPath] = new WPInstall($wpPath); $this->custWpInstalls[$wpPath]->setDocRoot($docroot); $this->custWpInstalls[$wpPath]->setServerName($serverName); $this->custWpInstalls[$wpPath]->setSiteUrlDirect($siteUrl); $this->custWpInstalls[$wpPath]->refreshStatus();
$this->log( "New installation added to custom data file: $wpPath", Logger::L_INFO ); } else { $this->log( "Installation already found during scan: $wpPath. " . 'Skipping.', Logger::L_INFO ); } } }
/** * Get all WPInstall command messages as a key=>value array. * * @return string[][] */ public function getAllCmdMsgs() { $succ = $fail = $err = array();
foreach ( $this->workingQueue as $WPInstall ) { $cmdStatus = $WPInstall->getCmdStatus();
if ( $cmdStatus & UserCommand::EXIT_SUCC ) { $msgType = &$succ; } elseif ( $cmdStatus & UserCommand::EXIT_FAIL ) { $msgType = &$fail; } elseif ( $cmdStatus & UserCommand::EXIT_ERROR ) { $msgType = &$err; } else { continue; }
if ( $msg = $WPInstall->getCmdMsg() ) { $msgType[] = "{$WPInstall->getPath()} - $msg"; } }
return array( 'succ' => $succ, 'fail' => $fail, 'err' => $err ); }
/** * * @param string $msg * @param int $level * @throws LSCMException Thrown indirectly. */ protected function log( $msg, $level ) { $msg = "WPInstallStorage - {$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 } }
}
|