/home/suroeste/public_html/wp-content/plugins/akeebabackupwp/app/Solo
Edit: /home/suroeste/public_html/wp-content/plugins/akeebabackupwp/app/Solo/PostUpgradeScript.php (33913B)
container = $container;
}
/**
* Execute the post-upgrade actions
*/
public function execute()
{
// Do not execute the post-upgrade script in the development environment
$realPath = realpath(__DIR__);
if (@file_exists($realPath . '/../../.nopostupgrade'))
{
return;
}
// Invalidate the Composer files' OPCache.
if (function_exists('opcache_invalidate'))
{
@opcache_invalidate(APATH_BASE . '/../vendor/autoload.php', true);
@opcache_invalidate(APATH_BASE . '/../vendor/composer/autoload_classmap.php', true);
@opcache_invalidate(APATH_BASE . '/../vendor/composer/autoload_namespaces.php', true);
@opcache_invalidate(APATH_BASE . '/../vendor/composer/autoload_psr4.php', true);
@opcache_invalidate(APATH_BASE . '/../vendor/composer/autoload_real.php', true);
@opcache_invalidate(APATH_BASE . '/../vendor/composer/autoload_static.php', true);
}
// Migrate secretkey.php
$this->migrateSecretKeyFile();
// Special handling for running the Solo application inside WordPress.
if ($this->container->segment->get('insideCMS', false))
{
if (defined('WPINC'))
{
$this->WordPressActions();
}
}
// Remove obsolete files
$this->processRemoveFiles();
// Remove obsolete folders
$this->processRemoveFolders();
// Migrate profiles
$this->migrateProfiles();
// Migrate front-end API activation options
$this->upgradeFrontendEnable();
}
/**
* Upgrades the frontend_enable option into the two separate legacyapi_enabled and jsonapi_enabled options.
*
* Before version 7 we had a single option to control both frontend backup APIs. Starting version 7 we can enable
* and disable them separately.
*/
public function upgradeFrontendEnable()
{
$currentValue = $this->container->appConfig->get('options.frontend_enable', null);
if (is_null($currentValue))
{
return;
}
$this->container->appConfig->set('options.frontend_enable', null);
$this->container->appConfig->set('options.legacyapi_enabled', $currentValue);
$this->container->appConfig->set('options.jsonapi_enabled', $currentValue);
$this->container->appConfig->saveConfiguration();
}
/**
* Removes obsolete files, depending on the edition (core or pro)
*/
protected function processRemoveFiles()
{
if (defined('WPINC'))
{
$basePath = rtrim($this->container->filesystemBase, '/' . DIRECTORY_SEPARATOR) . '/../';
foreach ($this->removeWordPressOnlyFilesAll as $file)
{
$filePath = $basePath . $file;
if (file_exists($filePath))
{
@unlink($filePath);
}
}
}
$removeFiles = $this->removeFilesAllVersions;
if (defined('AKEEBABACKUP_PRO') && AKEEBABACKUP_PRO)
{
$removeFiles = array_merge($removeFiles, $this->removeFilesPro);
}
else
{
$removeFiles = array_merge($removeFiles, $this->removeFilesCore);
}
$this->_removeFiles($removeFiles);
}
/**
* Removes obsolete folders, depending on the edition (core or pro)
*/
protected function processRemoveFolders()
{
$removeFolders = $this->removeFoldersAllVersions;
if (defined('AKEEBABACKUP_PRO') && AKEEBABACKUP_PRO)
{
$removeFolders = array_merge($removeFolders, $this->removeFoldersPro);
}
else
{
$removeFolders = array_merge($removeFolders, $this->removeFoldersCore);
}
$this->_removeFolders($removeFolders);
}
/**
* Specific actions to execute when we are running inside WordPress
*/
private function WordPressActions()
{
$this->WordPressUpgradeToUtf8mb4();
$this->WordPressRemoveFolders();
$this->WordPressRemoveFiles();
}
/**
* Remove obsolete folders from the WordPress installation
*
* @return void
*/
private function WordPressRemoveFolders()
{
$removeFolders = [
// Standalone platform
'app/Solo/Platform',
// Obsolete folders after the introduction of Akeeba Engine 2
'helpers/platform/solowp',
];
// Remove WordPress-specific features from the Core release
if (defined('AKEEBABACKUP_PRO') && !AKEEBABACKUP_PRO)
{
$removeFolders = array_merge(
[
'helpers/assets/mu-plugins',
'wpcli',
], $removeFolders
);
}
$fsBase = rtrim($this->container->filesystemBase, '/' . DIRECTORY_SEPARATOR) . '/../';
$fs = $this->container->fileSystem;
foreach ($removeFolders as $folder)
{
$fs->rmdir($fsBase . $folder, true);
}
}
/**
* Remove obsolete files from the WordPress installation
*
* @return void
*/
private function WordPressRemoveFiles()
{
$removeFiles = [
// Migrating INI files to .json files
"helpers/Platform/Wordpress/Config/04.quota.ini",
"helpers/Platform/Wordpress/Config/02.advanced.ini",
"helpers/Platform/Wordpress/Config/Pro/04.quota.ini",
"helpers/Platform/Wordpress/Config/Pro/02.advanced.ini",
"helpers/Platform/Wordpress/Config/Pro/01.basic.ini",
"helpers/Platform/Wordpress/Config/Pro/02.platform.ini",
"helpers/Platform/Wordpress/Config/Pro/03.filters.ini",
"helpers/Platform/Wordpress/Config/Pro/05.tuning.ini",
"helpers/Platform/Wordpress/Config/01.basic.ini",
"helpers/Platform/Wordpress/Config/02.platform.ini",
"helpers/Platform/Wordpress/Config/05.tuning.ini",
];
// Remove WordPress-specific features from the Core release
if (defined('AKEEBABACKUP_PRO') && !AKEEBABACKUP_PRO)
{
$additionalFiles = [
'helpers/boot_wpcli.php',
];
$removeFiles = array_merge($removeFiles, $additionalFiles);
}
if (empty($removeFiles))
{
return;
}
$fsBase = rtrim($this->container->filesystemBase, '/' . DIRECTORY_SEPARATOR) . '/../';
$fs = $this->container->fileSystem;
foreach ($removeFiles as $file)
{
$fs->delete($fsBase . $file);
}
}
/**
* Update WordPress tables to utf8mb4 if required
*/
private function WordPressUpgradeToUtf8mb4()
{
/** @var wpdb $wpdb */ global $wpdb;
// Is it really WordPress?
if (!is_object($wpdb))
{
return;
}
// Is it really WordPress?
if (!method_exists($wpdb, 'has_cap'))
{
return;
}
// Does the database support utf8mb4 at all?
if (!$wpdb->has_cap('utf8mb4'))
{
return;
}
// Is the actual charset set to utf8mb4?
$charset = strtolower($wpdb->charset);
if ($charset != 'utf8mb4')
{
return;
}
// OK, all conditions met, let's upgrade the tables to utf8mb4
$dbInstaller = new Installer($this->container);
$dbInstaller->setForcedFile($this->container->basePath . '/assets/sql/xml/utf8mb4_update.xml');
$dbInstaller->updateSchema();
return;
}
/**
* Removes obsolete files given on a list
*
* @param array $removeFiles List of files to remove
*
* @return void
*/
private function _removeFiles(array $removeFiles)
{
if (empty($removeFiles))
{
return;
}
$fsBase = rtrim($this->container->filesystemBase, '/' . DIRECTORY_SEPARATOR) . '/';
$fs = $this->container->fileSystem;
foreach ($removeFiles as $file)
{
$fs->delete($fsBase . $file);
}
}
/**
* Removes obsolete folders given on a list
*
* @param array $removeFolders List of folders to remove
*
* @return void
*/
private function _removeFolders(array $removeFolders)
{
if (empty($removeFolders))
{
return;
}
$fsBase = rtrim($this->container->filesystemBase, '/' . DIRECTORY_SEPARATOR) . '/';
$fs = $this->container->fileSystem;
foreach ($removeFolders as $folder)
{
$fs->rmdir($fsBase . $folder, true);
}
}
/**
* Migrates existing backup profiles. The changes currently made are:
* * Change post-processing from "s3" (legacy) to "amazons3" (current version).
* * Fix profiles with invalid embedded installer settings.
* * Migrate to separate local and remote quota settings.
*
* @return void
*/
private function migrateProfiles()
{
// Get a list of backup profiles
$db = $this->container->db;
$query = $db->getQuery(true)->select($db->qn('id'))->from($db->qn('#__ak_profiles'));
$profiles = $db->setQuery($query)->loadColumn();
// Normally this should never happen as we're supposed to have at least profile #1
if (empty($profiles))
{
return;
}
// Migrate each profile
foreach ($profiles as $profile)
{
// Initialization
$dirty = false;
// Load the profile configuration
Platform::getInstance()->load_configuration($profile);
$config = Factory::getConfiguration();
// Remove key protection
$protected = $config->getProtectedKeys();
$config->setProtectedKeys([]);
// -- Migrate obsolete "s3" engine to "amazons3"
if ($config->get('akeeba.advanced.postproc_engine', '') == 's3')
{
$dirty = true;
$config->setKeyProtection('akeeba.advanced.postproc_engine', false);
$config->setKeyProtection('engine.postproc.amazons3.signature', false);
$config->setKeyProtection('engine.postproc.amazons3.accesskey', false);
$config->setKeyProtection('engine.postproc.amazons3.secretkey', false);
$config->setKeyProtection('engine.postproc.amazons3.usessl', false);
$config->setKeyProtection('engine.postproc.amazons3.bucket', false);
$config->setKeyProtection('engine.postproc.amazons3.directory', false);
$config->setKeyProtection('engine.postproc.amazons3.rrs', false);
$config->setKeyProtection('engine.postproc.amazons3.customendpoint', false);
$config->setKeyProtection('engine.postproc.amazons3.legacy', false);
$config->set('akeeba.advanced.postproc_engine', 'amazons3');
$config->set('engine.postproc.amazons3.signature', 's3');
$config->set('engine.postproc.amazons3.accesskey', $config->get('engine.postproc.s3.accesskey'));
$config->set('engine.postproc.amazons3.secretkey', $config->get('engine.postproc.s3.secretkey'));
$config->set('engine.postproc.amazons3.usessl', $config->get('engine.postproc.s3.usessl'));
$config->set('engine.postproc.amazons3.bucket', $config->get('engine.postproc.s3.bucket'));
$config->set('engine.postproc.amazons3.directory', $config->get('engine.postproc.s3.directory'));
$config->set('engine.postproc.amazons3.rrs', $config->get('engine.postproc.s3.rrs'));
$config->set(
'engine.postproc.amazons3.customendpoint', $config->get('engine.postproc.s3.customendpoint')
);
$config->set('engine.postproc.amazons3.legacy', $config->get('engine.postproc.s3.legacy'));
}
// Fix profiles with invalid embedded installer settings
$embeddedInstaller = $config->get('akeeba.advanced.embedded_installer');
if (empty($embeddedInstaller) || ($embeddedInstaller == 'angie-joomla')
|| ((substr(
$embeddedInstaller, 0, 5
) != 'angie')
&& ($embeddedInstaller != 'none')))
{
$dirty = true;
$config->setKeyProtection('akeeba.advanced.embedded_installer', false);
$config->set('akeeba.advanced.embedded_installer', 'angie');
}
// Transcribe the local quota to remote quota settings if the legacy "Enable remote quotas" option is on.
if ($config->get('akeeba.quota.remote', 0) == 1)
{
$dirty = true;
$config->set('akeeba.quota.remote', null);
$config->set('akeeba.quota.remotely.maxage.enable', $config->get('akeeba.quota.maxage.enable', 0));
$config->set('akeeba.quota.remotely.maxage.maxdays', $config->get('akeeba.quota.maxage.maxdays', 31));
$config->set('akeeba.quota.remotely.maxage.keepday', $config->get('akeeba.quota.maxage.keepday', 1));
$config->set('akeeba.quota.remotely.enable_size_quota', $config->get('akeeba.quota.enable_size_quota', 0));
$config->set('akeeba.quota.remotely.size_quota', $config->get('akeeba.quota.size_quota', 15728640));
$config->set('akeeba.quota.remotely.enable_count_quota', $config->get('akeeba.quota.enable_count_quota', 1));
$config->set('akeeba.quota.remotely.count_quota', $config->get('akeeba.quota.count_quota', 3));
}
// Restore key protection
$config->setProtectedKeys($protected);
// Save dirty records
if ($dirty)
{
Platform::getInstance()->save_configuration($profile);
}
}
}
/**
* Migrate the settings encryption key file, if needed
*
* @return void
* @since 8.0.0
*/
private function migrateSecretKeyFile(): void
{
$oldFile = $this->findBestSecretKeyFile();
$newFile = APATH_BASE . '/Solo/secretkey.php';
// Different secretkey.php when using WordPress
if (defined('ABSPATH'))
{
$newFile = rtrim(
(defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR : (rtrim(ABSPATH, '/') . '/wp-content')),
'/'
) . '/akeebabackup_secretkey.php';
}
if (empty($oldFile) || !file_exists($oldFile))
{
return;
}
if ($oldFile !== $newFile)
{
if (@copy($oldFile, $newFile))
{
@unlink($oldFile);
}
}
Factory::getSecureSettings()->setKeyFilename($newFile);
}
/**
* Find the secret key file which decrypts the most backup profiles.
*
* @return string|null The path the best key file. NULL if there's no file, or encryption is disabled.
* @since 8.1.0
*/
private function findBestSecretKeyFile(): ?string
{
// If encryption is not supported, or disabled, there's nothing I need to do. Right?
$secureSettings = Factory::getSecureSettings();
if (!$secureSettings->supportsEncryption())
{
return null;
}
// Extract the keys from the different possible files
$files = [
APATH_BASE . '/Solo/engine/secretkey.php',
APATH_BASE . '/Solo/secretkey.php',
];
// Different secretkey.php when using WordPress
if (defined('ABSPATH'))
{
$files[] = rtrim(
(defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR : (rtrim(ABSPATH, '/') . '/wp-content')),
'/'
) . '/akeebabackup_secretkey.php';
}
$keys = array_combine(
$files,
array_map([$this, 'getSecretKeyFromFile'], $files)
);
// Remove empty keys (file cannot be read, or does not exist).
$keys = array_filter($keys);
// Get the backup profile configuration from the database
$platform = Platform::getInstance();
$db = $this->container->db;
try
{
$sql = $db->getQuery(true)
->select([
$db->quoteName('id'),
$db->quoteName('configuration'),
])
->from($db->qn($platform->tableNameProfiles));
$encryptedProfiles = $db->setQuery($sql)->loadAssocList('id', 'configuration');
}
catch (\Throwable $e)
{
return false;
}
// For each key file, try to decrypt the backup profiles and assign it the number of profiles it decrypted
$keys = array_map(function ($key) use ($encryptedProfiles, $secureSettings) {
$test = array_map(function($data) use ($secureSettings, $key) {
$data = $secureSettings->decryptSettings($data, $key);
try
{
$data = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($data) || empty($data))
{
return 0;
}
}
catch (\JsonException $e)
{
return 0;
}
return 1;
}, $encryptedProfiles);
return array_sum($test);
}, $keys);
// Rank key files by number of decrypted profiles, ascending (best key file is last)
asort($keys);
// Remove key files which failed to decrypt anything
$keys = array_filter($keys, fn($x) => $x > 0);
// No key files succeeded. Oof.
if (empty($keys))
{
return null;
}
// Get and return the highest ranking key file.
$files = array_keys($keys);
return array_pop($files);
}
/**
* Extract the secret key from a key file without including it (since we cannot redeclare constants)
*
* @param string $filePath
*
* @return string|null The key, or NULL if the file is missing, unreadable, or of an invalid format.
* @since 8.1.0
*/
private function getSecretKeyFromFile(string $filePath): ?string
{
// Make sure there is a file, and we can read its contents.
if (!@file_exists($filePath) || !@is_file($filePath) || !@is_readable($filePath))
{
return null;
}
$fileContents = @file_get_contents($filePath);
if ($fileContents === false)
{
return null;
}
// Try to locate the key value using a RegEx
$pattern = '/define\s*\(\s*(\'AKEEBA_SERVERKEY\'|"AKEEBA_SERVERKEY")\s*,\s*(\'.*\'|".*")\s*\)/';
if (!preg_match($pattern, $fileContents, $matches))
{
return null;
}
if (!isset($matches[2]) || !is_string($matches[2]) || empty($matches[2]))
{
return null;
}
// The matched string is either `'SOMETHING'` or `"SOMETHING"`. We need to find the quote type and remove it.
$quote = substr($matches[2], 0, 1);
$encodedKey = trim($matches[2], $quote);
return base64_decode($encodedKey);
}
}