[ Mini Kiebo ]
Server: Windows NT DESKTOP-5B8S0D4 6.2 build 9200 (Windows 8 Professional Edition) i586
Path:
D:
/
Backup
/
05122024
/
htdocs
/
jurnal-kesmas
/
v1
/
lib
/
pkp
/
classes
/
plugins
/
[
Home
]
File: PluginHelper.php
<?php /** * @file classes/plugins/PluginHelper.php * * Copyright (c) 2014-2021 Simon Fraser University * Copyright (c) 2003-2021 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class PluginHelper * * @ingroup classes_plugins * * @brief Helper class implementing plugin administration functions. */ namespace PKP\plugins; use APP\install\Install; use APP\install\Upgrade; use DirectoryIterator; use Exception; use Illuminate\Support\Arr; use PharData; use PKP\config\Config; use PKP\core\Core; use PKP\db\DAORegistry; use PKP\file\FileManager; use PKP\site\SiteDAO; use PKP\site\Version; use PKP\site\VersionCheck; use PKP\site\VersionDAO; use Throwable; class PluginHelper { public const PLUGIN_ACTION_UPLOAD = 'upload'; public const PLUGIN_ACTION_UPGRADE = 'upgrade'; public const PLUGIN_VERSION_FILE = 'version.xml'; public const PLUGIN_INSTALL_FILE = 'install.xml'; public const PLUGIN_UPGRADE_FILE = 'upgrade.xml'; /** * Extract the plugin, executes the callback, then cleanup the files * * @template T of mixed * * @param string $filePath Full path to plugin archive * @param string $originalFileName Original filename of plugin archive * @param callable(string $pluginDirectory):T $onExtracted The function will receive as parameter the directory of the plugin * * @throws Exception If any unexpected error happens (failure to unpack, absence of version.xml, etc) * * @return T Returns the result of the $onExtracted call */ private function extractPlugin(string $filePath, string $originalFileName, callable $onExtracted): mixed { $fileManager = new FileManager(); $extension = $this->sanitizeFilename($fileManager->parseFileExtension($originalFileName)); $baseName = $this->sanitizeFilename(basename($originalFileName, ".{$extension}")) ?: 'plugin'; // If the extension doesn't match the original one, copy (we don't know the original file) the file to another location to avoid issues with the PharData class $filePathWithExtension = null; if ($fileManager->parseFileExtension($filePath) !== $extension) { $filePathWithExtension = ($fileManager->getTemporaryFile($baseName, ".{$extension}"))->getPathname(); $fileManager->copyFile($filePath, $filePathWithExtension) || throw new Exception('Failed to copy plugin file'); } $extractPath = null; try { // Create a random directory to avoid symlink attacks. $extractPath = rtrim(sys_get_temp_dir(), '\\/') . "/{$baseName}" . substr(md5(random_int(0, PHP_INT_MAX)), 0, 10) . '/'; $fileManager->mkdir($extractPath) || throw new Exception("Could not create directory {$extractPath}"); // Extract files (new PharData($filePathWithExtension ?? $filePath))->extractTo($extractPath, null, true); // Ensure there's a file named "version.xml" at the main directory or at the direct sub-directories foreach (new DirectoryIterator($extractPath) as $current) { if ($current->isDir() && $current->getBasename() !== '..' && is_file(($path = "{$current->getPathname()}/") . static::PLUGIN_VERSION_FILE)) { return $onExtracted($path); } } throw new Exception(__('manager.plugins.invalidPluginArchive')); } finally { // Cleanup the extracted folder on failure and rethrow if ($extractPath) { $fileManager->rmtree($extractPath); } // Cleanup the temporary archive file in case it was created if ($filePathWithExtension) { unlink($filePathWithExtension); } } } /** * Installs an extracted plugin * * @param string $path path to plugin archive * @param string $originalFileName Original filename of plugin archive * * @return Version Version of installed plugin on success */ public function installPlugin(string $path, string $originalFileName): Version { return $this->extractPlugin($path, $originalFileName, function (string $pluginFolder): Version { $fileManager = new FileManager(); $versionFile = $pluginFolder . static::PLUGIN_VERSION_FILE; $pluginVersion = VersionCheck::getValidPluginVersionInfo($versionFile); /** @var VersionDAO */ $versionDao = DAORegistry::getDAO('VersionDAO'); $installedPlugin = $versionDao->getCurrentVersion($pluginVersion->getProductType(), $pluginVersion->getProduct()); $baseDir = Core::getBaseDir() . '/'; $destinyPath = $baseDir . strtr($pluginVersion->getProductType(), '.', '/') . "/{$pluginVersion->getProduct()}"; if ($installedPlugin && is_dir($destinyPath)) { throw new Exception( $installedPlugin->compare($pluginVersion) < 0 ? __('manager.plugins.pleaseUpgrade') : __('manager.plugins.installedVersionNewest') ); } // Copy the plug-in from the temporary folder to the target folder. $fileManager->copyDir($pluginFolder, $destinyPath) || throw new Exception('Failed to copy plugin to destination folder'); try { // Upgrade the database with the new plug-in. $installFile = Arr::first( ["{$destinyPath}/" . static::PLUGIN_INSTALL_FILE, $baseDir . PKP_LIB_PATH . '/xml/defaultPluginInstall.xml'], fn (string $path) => is_file($path) ) ?? throw new Exception('Missing installation file'); $siteDao = DAORegistry::getDAO('SiteDAO'); /** @var SiteDAO $siteDao */ $site = $siteDao->getSite(); $params = $this->_getConnectionParams(); $params['locale'] = $site->getPrimaryLocale(); $params['additionalLocales'] = $site->getSupportedLocales(); $installer = new Install($params, $installFile, true); $installer->setCurrentVersion($pluginVersion); $installer->execute() || throw new Exception(__('manager.plugins.installFailed', ['errorString' => $installer->getErrorString()])); $versionDao->insertVersion($pluginVersion, true); return $pluginVersion; } catch (Throwable $e) { // Delete the plugin files on failure $fileManager->rmtree($destinyPath); throw $e; } }); } /** * Load database connection parameters into an array (needed for upgrade). */ protected function _getConnectionParams(): array { return [ 'connectionCharset' => Config::getVar('i18n', 'connection_charset'), 'databaseDriver' => Config::getVar('database', 'driver'), 'databaseHost' => Config::getVar('database', 'host'), 'databasePort' => Config::getVar('database', 'port'), 'unixSocket' => Config::getVar('database', 'unix_socket'), 'databaseUsername' => Config::getVar('database', 'username'), 'databasePassword' => Config::getVar('database', 'password'), 'databaseName' => Config::getVar('database', 'name') ]; } /** * Upgrade a plugin to a newer version from the user's filesystem * * @param string $path path to plugin archive * @param string $originalFileName Original filename of plugin archive * */ public function upgradePlugin(string $category, string $plugin, string $path, string $originalFileName): Version { return $this->extractPlugin($path, $originalFileName, function (string $pluginFolder) use ($category, $plugin): Version { $fileManager = new FileManager(); $versionFile = $pluginFolder . static::PLUGIN_VERSION_FILE; $pluginVersion = VersionCheck::getValidPluginVersionInfo($versionFile); // Check whether the uploaded plug-in fits the original plug-in. if ("plugins.{$category}" !== $pluginVersion->getProductType()) { throw new Exception(__('manager.plugins.wrongCategory')); } if ($plugin !== $pluginVersion->getProduct()) { throw new Exception(__('manager.plugins.wrongName')); } $versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */ $installedPlugin = $versionDao->getCurrentVersion($pluginVersion->getProductType(), $pluginVersion->getProduct()); if (!$installedPlugin) { throw new Exception(__('manager.plugins.pleaseInstall')); } if ($installedPlugin->compare($pluginVersion) >= 0) { throw new Exception(__('manager.plugins.installedVersionNewer')); } $destinyPath = Core::getBaseDir() . "/plugins/{$category}/{$plugin}"; // Delete existing files. $fileManager->rmtree($destinyPath); // Check whether deleting has worked. if (is_dir($destinyPath)) { throw new Exception(__('manager.plugins.deleteError', ['pluginName' => $pluginVersion->getProduct()])); } // Copy the plug-in from the temporary folder to the target folder. $fileManager->copyDir($pluginFolder, $destinyPath) || throw new Exception('Could not copy plugin to destination!'); try { $upgradeFile = "{$destinyPath}/" . static::PLUGIN_UPGRADE_FILE; if ($fileManager->fileExists($upgradeFile)) { /** @var SiteDAO */ $siteDao = DAORegistry::getDAO('SiteDAO'); $site = $siteDao->getSite(); $params = $this->_getConnectionParams(); $params['locale'] = $site->getPrimaryLocale(); $params['additionalLocales'] = $site->getSupportedLocales(); $installer = new Upgrade($params, $upgradeFile, true); // Run the upgrade/migration $installer->execute() || throw new Exception(__('manager.plugins.upgradeFailed', ['errorString' => $installer->getErrorString()])); } // Add the new version to the database $pluginVersion->setCurrent(1); $versionDao->insertVersion($pluginVersion, true); return $pluginVersion; } catch (Throwable $e) { // Delete the plugin files on failure $fileManager->rmtree($destinyPath); throw $e; } }); } /** * Drops risky characters from a filename */ private function sanitizeFilename(string $filename): string { return preg_replace('/[^\w.-]/', '', $filename); } } if (!PKP_STRICT_MODE) { class_alias('\PKP\plugins\PluginHelper', '\PluginHelper'); foreach ([ 'PLUGIN_ACTION_UPLOAD', 'PLUGIN_ACTION_UPGRADE', 'PLUGIN_VERSION_FILE', 'PLUGIN_INSTALL_FILE', 'PLUGIN_UPGRADE_FILE', ] as $constantName) { define($constantName, constant('\PluginHelper::' . $constantName)); } }