D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
www
/
oems
/
wp-content
/
plugins
/
.akeebabackupwp
/
helpers
/
Platform
/
Wordpress
/
Filename :
Platform.php
back
Copy
<?php /** * @package solo * @copyright Copyright (c)2014-2024 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\Engine\Platform; use Akeeba\Engine\Factory; use Akeeba\Engine\Platform; use Akeeba\Engine\Psr\Log\LogLevel; use Awf\Container\ContainerAwareInterface; use Awf\Container\ContainerAwareTrait; // Protection against direct access defined('AKEEBAENGINE') or die(); if (!defined('DS')) { define('DS', DIRECTORY_SEPARATOR); // Sometimes required (legacy code) } /** * Akeeba Solo for WordPress platform class */ class Wordpress extends Base implements ContainerAwareInterface { use ContainerAwareTrait; const SHOW_120_DAYS_WARNING = false; /** @var integer Platform class priority */ public $priority = 60; /** @var string The platform name */ public $platformName = 'wordpress'; /** * Override profile ID, for use in automated testing only * * @var int|null */ public static $profile_id = null; /** * Performs heuristics to determine if this platform object is the ideal * candidate for the environment Akeeba Engine is running in. * * @return boolean True if this platform applies to this environment */ public function isThisPlatform() { if (!defined('WPINC') && !defined('ABSPATH')) { return false; } return true; } /** * Returns an associative array of stock platform directories * * @return array Platform directories */ public function get_stock_directories() { static $stock_directories = []; if (empty($stock_directories)) { $host = $this->get_host(); $contentFolder = rtrim( (defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR : (rtrim(ABSPATH, '/') . '/wp-content')), '/' ); $defaultOutput = $contentFolder . '/backups'; if (!@is_dir($defaultOutput)) { $this->conditionallyCreateDefaultOutputDirectory($defaultOutput); } $contentFolder = rtrim( (defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR : (rtrim(ABSPATH, '/') . '/wp-content')), '/' ); $pluginsDir = rtrim( defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : ($contentFolder . '/plugins'), '/' ); $pluginSlug = (class_exists('AkeebaBackupWP') && !empty(\AkeebaBackupWP::$pluginBaseName)) ? \AkeebaBackupWP::$pluginBaseName : 'akeebabackupwp/akeebabackupwp.php'; $abwpDir = dirname($pluginsDir . '/' . $pluginSlug); $stock_directories['[SITEROOT]'] = $this->get_site_root(); $stock_directories['[ROOTPARENT]'] = @realpath($this->get_site_root() . '/..'); $stock_directories['[SITETMP]'] = APATH_BASE . '/tmp'; $stock_directories['[DEFAULT_OUTPUT]'] = $defaultOutput; $stock_directories['[LEGACY_DEFAULT_OUTPUT]'] = APATH_BASE . '/backups'; $stock_directories['[LEGACY_DEFAULT_OUTPUT_TESTING]'] = $abwpDir . '/app/backups'; $stock_directories['[HOST]'] = empty($host) ? 'unknown_host' : $host; } return $stock_directories; } /** * Returns the absolute path to the site's root * * @return string The absolute path to our own directory */ public function get_site_root() { static $root = null; if (empty($root) || is_null($root)) { $root = ABSPATH; if (empty($root) || ($root == DIRECTORY_SEPARATOR) || ($root == '/')) { // Try to get the current root in a different way if (function_exists('getcwd')) { $root = getcwd(); } if (empty($root)) { $root = '../'; } else { $adminPos = strpos($root, 'wp-admin'); if ($adminPos !== false) { $root = substr($root, 0, $adminPos); } else { $root = '../'; } // Degenerate case where $root = 'wp-admin' // without a leading slash before entering this // if-block if (empty($root)) { $root = '../'; } } } if (!in_array(substr($root, -1), ['/', '\\'])) { $root .= DIRECTORY_SEPARATOR; } } return $root; } /** * Returns the absolute path to the installer images directory * * @return string The absolute path to the installer images directory */ public function get_installer_images_path() { return $this->getContainer()->basePath . '/assets/installers'; } /** * Returns the active profile number * * @return integer The active profile number */ public function get_active_profile() { // Automated testing override if (!is_null(self::$profile_id) && (self::$profile_id > 0)) { return self::$profile_id; } if (defined('AKEEBA_PROFILE')) { return AKEEBA_PROFILE; } $session = $this->getContainer()->segment; if (!isset($session->profile)) { return 1; } $profile = (int)$session->profile; if (empty($profile) || ($profile < 1)) { $profile = 1; } return $profile; } /** * Returns the selected profile's name. If no ID is specified, the current * profile's name is returned. * * @param integer $id The profile number for which to get the name * * @return string The profile's name */ public function get_profile_name($id = null) { if (empty($id)) { $id = $this->get_active_profile(); } $id = (int)$id; if (empty($id)) { $id = 1; } $db = $this->getContainer()->db; $query = $db->getQuery(true) ->select($db->qn('description')) ->from($db->qn('#__ak_profiles')) ->where($db->qn('id') . ' = ' . (int) $id); $db->setQuery($query); return $db->loadResult(); } /** * Returns the backup origin as set in the AKEEBA_BACKUP_ORIGIN constant, otherwise "backend" * * @return string The backup origin */ public function get_backup_origin() { if (defined('AKEEBA_BACKUP_ORIGIN')) { return AKEEBA_BACKUP_ORIGIN; } return 'backend'; } /** * Returns a MySQL-formatted timestamp out of the current date * * @param string $date [optional] The timestamp to use. Omit to use current timestamp. * * @return string */ public function get_timestamp_database($date = 'now') { $date = $this->getContainer()->dateFactory($date); return $date->toSql(); } /** * Returns the current timestamp, taking into account any TZ information, * in the format specified by $format. * * @param string $format Timestamp format string (standard PHP format string) * * @return string */ public function get_local_timestamp($format = 'Y-m-d H:i:s') { // Do I have a forced timezone? $tz = $this->get_platform_configuration_option('forced_backup_timezone', 'AKEEBA/DEFAULT'); // No forced timezone set? Use the default Joomla! behavior. if (empty($tz) || ($tz == 'AKEEBA/DEFAULT')) { $tz = $this->getContainer()->appConfig->get('timezone', 'UTC'); } // Get the current date/time and apply the preferred timezone $utcTimeZone = new \DateTimeZone('UTC'); $dateNow = $this->getContainer()->dateFactory('now', $utcTimeZone); $timezone = new \DateTimeZone($tz); $dateNow->setTimezone($timezone); return $dateNow->format($format, true); } /** * Returns the current host name * * @return string */ public function get_host() { static $deadLockTest = false; if (!$deadLockTest) { $deadLockTest = true; $overrideURL = Factory::getConfiguration()->get('akeeba.platform.site_url', ''); $overrideURL = trim($overrideURL); } else { $overrideURL = null; } if (defined('WPINC') && (function_exists('home_url') || function_exists('network_home_url'))) { if (function_exists('is_multisite') && function_exists('network_home_url') && is_multisite()) { $overrideURL = network_home_url('/', 'https'); } elseif (function_exists('home_url')) { $overrideURL = home_url('/', 'https'); } } if (!empty($overrideURL)) { // An override URL is already specified; use it $oURI = new \Awf\Uri\Uri($overrideURL); } elseif (!array_key_exists('REQUEST_METHOD', $_SERVER)) { // Running under CLI or a broken server $url = Platform::getInstance()->get_platform_configuration_option('siteurl', ''); $oURI = new \Awf\Uri\Uri($url); } else { // Running under the web server $oURI = \Awf\Uri\Uri::getInstance(); } return $oURI->getHost(); } /** * Returns the site's name. This doesn't apply for Akeeba Solo so we have to fake it. * * @return string */ public function get_site_name() { if (function_exists('get_bloginfo')) { return get_bloginfo('name', 'raw'); } else { return "Akeeba Backup"; } } /** * Gets the best matching database driver class. $use_platform is ignored in Akeeba Solo. * * @param boolean $use_platform If set to false, it will forcibly try to assign one of the primitive types * (Mysql/Mysqli) and NEVER tell you to use a platform driver class * * @return string */ public function get_default_database_driver($use_platform = true) { if ($use_platform && defined('WPINC')) { return '\\Akeeba\\Engine\\Driver\\Wordpress'; } $driver = function_exists('mysqli_connect') ? 'Mysqli' : 'Mysql'; return '\\Akeeba\\Engine\\Driver\\' . $driver; } /** * Returns a set of options to connect to the default database * * @return array Database connection options */ public function get_platform_database_options() { static $options; if (empty($options)) { if (!defined('WPINC')) { global $table_prefix; } else { global $wpdb; $table_prefix = $wpdb->prefix; } $options = array( 'host' => DB_HOST, 'user' => DB_USER, 'password' => DB_PASSWORD, 'database' => DB_NAME, 'prefix' => $table_prefix ); } return $options; } /** * Provides a platform-specific translation function * * @param string $key The translation key * * @return string The translated string */ public function translate($key) { return \Awf\Text\Text::_($key); } /** * Populates global constants holding the Akeeba application version */ public function load_version_defines() { $fileName = APATH_BASE . '/version.php'; if (file_exists($fileName)) { require_once $fileName; } if (!defined('AKEEBABACKUP_VERSION')) { define("AKEEBABACKUP_VERSION", "dev"); } if (!defined('AKEEBABACKUP_PRO')) { define('AKEEBABACKUP_PRO', true); } if (!defined('AKEEBABACKUP_DATE')) { $date = $this->getContainer()->dateFactory(); define("AKEEBABACKUP_DATE", $date->format('Y-m-d')); } } /** * Returns the platform name and version * * @return array An array containing the platform name and version, in this order */ public function getPlatformVersion() { global $wp_version; // Play safe in case the WordPress version is not correctly detected $version = $wp_version ?? '0.0.0'; return array( 'name' => 'WordPress', 'version' => $version ); } /** * Logs platform-specific directories with INFO log level * * @return array|null */ public function log_platform_special_directories() { $site_root = $this->get_site_root(); Factory::getLog()->log(LogLevel::INFO, "APATH_BASE :" . APATH_BASE, ['translate_root' => false]); Factory::getLog()->log(LogLevel::INFO, "Application Path :" . $this->getContainer()->basePath, ['translate_root' => false]); Factory::getLog()->log(LogLevel::INFO, "Site root :" . $this->get_site_root(), ['translate_root' => false]); // If the release is older than 3 months, issue a warning if (self::SHOW_120_DAYS_WARNING && defined('AKEEBABACKUP_DATE')) { $releaseDate = $this->getContainer()->dateFactory(AKEEBABACKUP_DATE); if (time() - $releaseDate->toUnix() > 10368000) { if (!isset($ret['warnings'])) { $ret['warnings'] = array(); $ret['warnings'] = array_merge($ret['warnings'], array( 'Your version of Akeeba Backup is more than 120 days old and most likely already out of date. Please check if a newer version is published and install it.' )); } } } // Detect UNC paths and warn the user if(DIRECTORY_SEPARATOR == '\\') { if( (substr(ABSPATH, 0, 2) == '\\\\') || (substr(ABSPATH, 0, 2) == '//') ) { if (!isset($ret['warnings'])) { $ret['warnings'] = array(); } $ret['warnings'] = array_merge($ret['warnings'], array( 'Your site\'s root is using a UNC path (e.g. \\\\SERVER\\path\\to\\root). PHP has known bugs which may', 'prevent it from working properly on a site like this. Please take a look at', 'https://bugs.php.net/bug.php?id=40163 and https://bugs.php.net/bug.php?id=52376. As a result your', 'backup may fail.' )); } } if (empty($ret)) { $ret = null; } return $ret; } /** * Loads a platform-specific software configuration option * * @param string $key The configuration option's key * @param mixed $default The default value to use * * @return mixed */ public function get_platform_configuration_option($key, $default) { $config = $this->getContainer()->appConfig; $altValue = $config->get($key, $default); $value = $config->get('options.' . $key, $altValue); // Some configuration options may have to be decrypted switch ($key) { case 'frontend_secret_word': $secureSettings = Factory::getSecureSettings(); $value = $secureSettings->decryptSettings($value); break; } return $value; } /** * Returns a list of emails to the Super Administrators * * @return array */ public function get_administrator_emails() { $ret = array(); $emails = $this->get_platform_configuration_option('frontend_email_address', ''); if (!empty($emails)) { $emails = explode(',', $emails); foreach ($emails as $email) { $ret[] = trim($email); } } return $ret; } /** * Sends a very simple email using the platform's mailer facility * * @param string $to The recipient's email address * @param string $subject The subject of the email * @param string $body The body of the email * @param string $attachFile The file to attach (null to not attach any files) * * @return boolean */ public function send_email($to, $subject, $body, $attachFile = null) { Factory::getLog()->log(LogLevel::DEBUG, "-- Fetching mailer object"); $mailer = $this->getContainer()->mailer(); if (!is_object($mailer)) { Factory::getLog()->log(LogLevel::WARNING, "Could not send email to $to - Reason: Mailer object is not an object; please check your system settings"); return false; } Factory::getLog()->log(LogLevel::DEBUG, "-- Creating email message"); $recipient = array($to); $mailer->addRecipient($recipient); $mailer->setSubject($subject); $mailer->setBody($body); if (!empty($attachFile)) { Factory::getLog()->log(LogLevel::INFO, "-- Attaching $attachFile"); if (!file_exists($attachFile) || !(is_file($attachFile) || is_link($attachFile))) { Factory::getLog()->log(LogLevel::WARNING, "The file does not exist, or it's not a file; no email sent"); return false; } if (!is_readable($attachFile)) { Factory::getLog()->log(LogLevel::WARNING, "The file is not readable; no email sent"); return false; } $fileSize = @filesize($attachFile); if ($fileSize) { // Check that we have AT LEAST 2.5 times free RAM as the filesize (that's how much we'll need) if (!function_exists('ini_get')) { // Assume 8Mb of PHP memory limit (worst case scenario) $totalRAM = 8388608; } else { $totalRAM = ini_get('memory_limit'); if (strstr($totalRAM, 'M')) { $totalRAM = (int)$totalRAM * 1048576; } elseif (strstr($totalRAM, 'K')) { $totalRAM = (int)$totalRAM * 1024; } elseif (strstr($totalRAM, 'G')) { $totalRAM = (int)$totalRAM * 1073741824; } else { $totalRAM = (int)$totalRAM; } if ($totalRAM <= 0) { // No memory limit? Cool! Assume 1Gb of available RAM (which is absurdly abundant as of March 2011...) $totalRAM = 1086373952; } } if (!function_exists('memory_get_usage')) { $usedRAM = 8388608; } else { $usedRAM = memory_get_usage(); } $availableRAM = $totalRAM - $usedRAM; if ($availableRAM < 2.5 * $fileSize) { Factory::getLog()->log(LogLevel::WARNING, "The file is too big to be sent by email. Please use a smaller Part Size for Split Archives setting."); Factory::getLog()->log(LogLevel::DEBUG, "Memory limit $totalRAM bytes -- Used memory $usedRAM bytes -- File size $fileSize -- Attachment requires approx. " . (2.5 * $fileSize) . " bytes"); return false; } } else { Factory::getLog()->log(LogLevel::WARNING, "Your server fails to report the file size of $attachFile. If the backup crashes, please use a smaller Part Size for Split Archives setting"); } $mailer->addAttachment($attachFile); } Factory::getLog()->log(LogLevel::DEBUG, "-- Sending message"); try { $result = $mailer->Send(); } catch (\Exception $e) { Factory::getLog()->log(LogLevel::WARNING, "Could not email $to:"); Factory::getLog()->log(LogLevel::WARNING, $e->getMessage()); $ret = $e->getMessage(); unset($result); unset($mailer); return $ret; } Factory::getLog()->log(LogLevel::DEBUG, "-- Email sent"); return true; } /** * Deletes a file from the local server using direct file access or FTP * * @param string $file File path to delete * * @return boolean True on success */ public function unlink($file) { try { $fs = $this->getContainer()->fileSystem; $result = $fs->delete($file); } catch (\RuntimeException $e) { $result = false; } if (is_null($result)) { $result = true; } return $result; } /** * Moves a file around within the local server using direct file access or FTP * * @param string $from File path to copy from * @param string $to File path to copy to * * @return boolean True on success */ public function move($from, $to) { try { $fs = $this->getContainer()->fileSystem; $result = $fs->move($from, $to); } catch (\RuntimeException $e) { $result = false; } if (is_null($result)) { $result = true; } return $result; } /** * Stores a flash (temporary) variable in the session. * * @param string $name The name of the variable to store * @param string $value The value of the variable to store * * @return void */ public function set_flash_variable($name, $value) { $session = $this->getContainer()->segment; $session->setFlash($name, $value); } /** * Return the value of a flash (temporary) variable from the session and * immediately removes it. * * @param string $name The name of the flash variable * @param mixed $default Default value, if the variable is not defined * * @return mixed The value of the variable or $default if it's not set */ public function get_flash_variable($name, $default = null) { $session = $this->getContainer()->segment; $value = $session->getFlash($name); if (is_null($value)) { $value = $default; } return $value; } /** * Perform an immediate redirection to the defined URL * * @param string $url The URL to redirect to * * @return void */ public function redirect($url) { $this->getContainer()->application->redirect($url); } /** * Applies the quirk definitions for Akeeba Solo * * @return void */ public function apply_quirk_definitions() { Factory::getConfigurationChecks()->clearConfigurationCheckDefinitions(); // Output directory unwritable Factory::getConfigurationChecks()->addConfigurationCheckDefinition('001', 'critical'); // Free memory too low Factory::getConfigurationChecks()->addConfigurationCheckDefinition('004', 'critical'); // Output folder within plugin folder Factory::getConfigurationChecks()->addConfigurationCheckDefinition('013', 'critical', 'COM_AKEEBA_CPANEL_WARNING_Q013', array('\\Akeeba\\Engine\\Platform\\Wordpress', 'quirk_013')); // Cannot create default output directory Factory::getConfigurationChecks()->addConfigurationCheckDefinition('014', 'critical', 'COM_AKEEBA_CPANEL_WARNING_Q014', array('\\Akeeba\\Engine\\Platform\\Wordpress', 'quirk_014')); // open_basedir on output directory Factory::getConfigurationChecks()->addConfigurationCheckDefinition('101', 'high'); // Less than 10" of max_execution_time with PHP Safe Mode enabled Factory::getConfigurationChecks()->addConfigurationCheckDefinition('103', 'high'); // CRC problems with hash extension not present Factory::getConfigurationChecks()->addConfigurationCheckDefinition('202', 'medium'); // Default output directory in use Factory::getConfigurationChecks()->addConfigurationCheckDefinition('203', 'medium'); // Disabled functions may affect operation Factory::getConfigurationChecks()->addConfigurationCheckDefinition('204', 'medium'); // ZIP format selected Factory::getConfigurationChecks()->addConfigurationCheckDefinition('401', 'low'); } public function getPlatformDirectories() { return array(__DIR__); } /** * Critical error detection: Output folder within plugin folder * * @return bool * @since 8.1.0 */ public static function quirk_013() { if (defined('AKEEBA_BACKUP_TESTING_DISABLE_Q013') && AKEEBA_BACKUP_TESTING_DISABLE_Q013) { return false; } $registry = Factory::getConfiguration(); $outDir = $registry->get('akeeba.basic.output_directory'); if (empty($outDir)) { return false; } $realOutDir = @realpath($outDir); if (!@is_dir($realOutDir) || $realOutDir === false) { return false; } $contentFolder = rtrim( (defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR : (rtrim(ABSPATH, '/') . '/wp-content')), '/' ); $pluginsDir = rtrim( defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : ($contentFolder . '/plugins'), '/' ); $pluginSlug = (class_exists('AkeebaBackupWP') && !empty(\AkeebaBackupWP::$pluginBaseName)) ? \AkeebaBackupWP::$pluginBaseName : 'akeebabackupwp/akeebabackupwp.php'; $abwpDir = dirname($pluginsDir . '/' . $pluginSlug); $abwpDirReal = @realpath($abwpDir); if (!@is_dir($abwpDir) || empty($abwpDirReal)) { return false; } return stripos($realOutDir, $abwpDirReal) === 0 || stripos($realOutDir, $abwpDir) === 0 || stripos($outDir, $abwpDirReal) === 0 || stripos($outDir, $abwpDir) === 0; } /** * Critical error detection: Cannot create default output directory * * @return bool * @since 8.1.0 */ public static function quirk_014() { $stock_dirs = Platform::getInstance()->get_stock_directories(); $defaultOutput = $stock_dirs['[DEFAULT_OUTPUT]'] ?? ''; if (empty($defaultOutput)) { return false; } if (!@is_dir($defaultOutput)) { return true; } if (@realpath($defaultOutput) === false) { return true; } return false; } protected function detectProxySettings() { $host = defined('WP_PROXY_HOST') ? WP_PROXY_HOST : ''; $port = defined('WP_PROXY_PORT') ? WP_PROXY_PORT : 8080; $user = defined('WP_PROXY_USERNAME') ? WP_PROXY_USERNAME : ''; $pass = defined('WP_PROXY_PASSWORD') ? WP_PROXY_PASSWORD : ''; $enabled = !empty($host) && !empty($port); $this->setProxySettings($enabled, $host, $port, $user, $pass); } /** * Tries to create the default output directory if it does not already exist. * * @param string $pathName The path to the output directory * * @return void */ protected function conditionallyCreateDefaultOutputDirectory(string $pathName) { // If it already exists if (@is_dir($pathName)) { return; } $success = @mkdir($pathName, 0755, true); if (!$success || !@is_dir($pathName)) { /** * Why return instead of using WordPress' WP_Filesystem? Because WordPress does not, in fact, save the FTP * information anywhere. Therefore, even using WordPress' WP_Filesystem would try to use mkdir() which has * already failed at this point. We can just display a message instead. */ return; } // Create files to protect against direct access Factory::getFilesystemTools()->ensureNoAccess($pathName, true); } }