From 5f0981788d7adbd25b71addbc415f700eb1c71b5 Mon Sep 17 00:00:00 2001 From: Andrew Nacin Date: Mon, 10 Feb 2014 23:00:15 +0000 Subject: [PATCH] Detect and handle symlinking of plugins in plugin_basename(). props rmccue, MikeSchinkel, jdgrimes. see #16953. Built from https://develop.svn.wordpress.org/trunk@27158 git-svn-id: http://core.svn.wordpress.org/trunk@27024 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/includes/plugin.php | 5 ++++- wp-admin/plugins.php | 1 + wp-admin/update.php | 3 ++- wp-includes/functions.php | 17 ++++++++++++++++ wp-includes/plugin.php | 38 +++++++++++++++++++++++++++++------- wp-settings.php | 8 +++++++- 6 files changed, 62 insertions(+), 10 deletions(-) diff --git a/wp-admin/includes/plugin.php b/wp-admin/includes/plugin.php index 8146c69184..5a2eff015a 100644 --- a/wp-admin/includes/plugin.php +++ b/wp-admin/includes/plugin.php @@ -537,7 +537,8 @@ function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silen if ( !empty($redirect) ) wp_redirect(add_query_arg('_error_nonce', wp_create_nonce('plugin-activation-error_' . $plugin), $redirect)); // we'll override this later if the plugin can be included without fatal error ob_start(); - include_once(WP_PLUGIN_DIR . '/' . $plugin); + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); + include_once( WP_PLUGIN_DIR . '/' . $plugin ); if ( ! $silent ) { /** @@ -921,6 +922,7 @@ function uninstall_plugin($plugin) { unset($uninstallable_plugins); define('WP_UNINSTALL_PLUGIN', $file); + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . dirname( $file ) ); include WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php'; return true; @@ -932,6 +934,7 @@ function uninstall_plugin($plugin) { update_option('uninstall_plugins', $uninstallable_plugins); unset($uninstallable_plugins); + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file ); include WP_PLUGIN_DIR . '/' . $file; add_action( 'uninstall_' . $file, $callable ); diff --git a/wp-admin/plugins.php b/wp-admin/plugins.php index 776a2b54a7..61efcf5c80 100644 --- a/wp-admin/plugins.php +++ b/wp-admin/plugins.php @@ -143,6 +143,7 @@ if ( $action ) { @ini_set('display_errors', true); //Ensure that Fatal errors are displayed. // Go back to "sandbox" scope so we get the same errors as before function plugin_sandbox_scrape( $plugin ) { + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); include( WP_PLUGIN_DIR . '/' . $plugin ); } plugin_sandbox_scrape( $plugin ); diff --git a/wp-admin/update.php b/wp-admin/update.php index 4290e58d78..e70d9c2221 100644 --- a/wp-admin/update.php +++ b/wp-admin/update.php @@ -84,7 +84,8 @@ if ( isset($_GET['action']) ) { error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR ); @ini_set('display_errors', true); //Ensure that Fatal errors are displayed. - include(WP_PLUGIN_DIR . '/' . $plugin); + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); + include( WP_PLUGIN_DIR . '/' . $plugin ); } iframe_footer(); } elseif ( 'install-plugin' == $action ) { diff --git a/wp-includes/functions.php b/wp-includes/functions.php index 0a80bdf414..32fe225564 100644 --- a/wp-includes/functions.php +++ b/wp-includes/functions.php @@ -1433,6 +1433,23 @@ function path_join( $base, $path ) { return rtrim($base, '/') . '/' . ltrim($path, '/'); } +/** + * Normalize a filesystem path. + * + * Replaces backslashes with forward slashes for Windows systems, + * and ensures no duplicate slashes exist. + * + * @since 3.9.0 + * + * @param string $path Path to normalize. + * @return string Normalized path. + */ +function wp_normalize_path( $path ) { + $path = str_replace( '\\', '/', $path ); + $path = preg_replace( '|/+|','/', $path ); + return $path; +} + /** * Determines a writable directory for temporary files. * Function's preference is the return value of sys_get_temp_dir(), diff --git a/wp-includes/plugin.php b/wp-includes/plugin.php index 08a0abd720..7a66ec04b5 100644 --- a/wp-includes/plugin.php +++ b/wp-includes/plugin.php @@ -592,18 +592,42 @@ function remove_all_actions($tag, $priority = false) { * @return string The name of a plugin. * @uses WP_PLUGIN_DIR */ -function plugin_basename($file) { - $file = str_replace('\\','/',$file); // sanitize for Win32 installs - $file = preg_replace('|/+|','/', $file); // remove any duplicate slash - $plugin_dir = str_replace('\\','/',WP_PLUGIN_DIR); // sanitize for Win32 installs - $plugin_dir = preg_replace('|/+|','/', $plugin_dir); // remove any duplicate slash - $mu_plugin_dir = str_replace('\\','/',WPMU_PLUGIN_DIR); // sanitize for Win32 installs - $mu_plugin_dir = preg_replace('|/+|','/', $mu_plugin_dir); // remove any duplicate slash +function plugin_basename( $file ) { + global $wp_plugin_paths; + + foreach ( $wp_plugin_paths as $dir => $realdir ) { + if ( strpos( $file, $realdir ) === 0 ) { + $file = $dir . substr( $file, strlen( $realdir ) ); + } + } + + $file = wp_normalize_path( $file ); + $plugin_dir = wp_normalize_path( WP_PLUGIN_DIR ); + $mu_plugin_dir = wp_normalize_path( WPMU_PLUGIN_DIR ); + $file = preg_replace('#^' . preg_quote($plugin_dir, '#') . '/|^' . preg_quote($mu_plugin_dir, '#') . '/#','',$file); // get relative path from plugins dir $file = trim($file, '/'); return $file; } +/** + * Register a plugin's real path. + * + * This is used in {@see plugin_basename()} to resolve symlinked paths. + * + * @param string $file Known path to the file. + */ +function wp_register_plugin_realpath( $file ) { + global $wp_plugin_paths; + + $plugin_path = wp_normalize_path( dirname( $file ) ); + $plugin_realpath = wp_normalize_path( dirname( realpath( $file ) ) ); + + if ( $plugin_path !== $plugin_realpath ) { + $wp_plugin_paths[ $plugin_path ] = $plugin_realpath; + } +} + /** * Gets the filesystem directory path (with trailing slash) for the plugin __FILE__ passed in * @package WordPress diff --git a/wp-settings.php b/wp-settings.php index 236615bf05..aee35b3e9c 100644 --- a/wp-settings.php +++ b/wp-settings.php @@ -164,8 +164,11 @@ if ( is_multisite() ) { // Define must-use plugin directory constants, which may be overridden in the sunrise.php drop-in. wp_plugin_directory_constants(); +$GLOBALS['wp_plugin_paths'] = array(); + // Load must-use plugins. foreach ( wp_get_mu_plugins() as $mu_plugin ) { + wp_register_plugin_realpath( $mu_plugin ); include_once( $mu_plugin ); } unset( $mu_plugin ); @@ -173,6 +176,7 @@ unset( $mu_plugin ); // Load network activated plugins. if ( is_multisite() ) { foreach( wp_get_active_network_plugins() as $network_plugin ) { + wp_register_plugin_realpath( $network_plugin ); include_once( $network_plugin ); } unset( $network_plugin ); @@ -206,8 +210,10 @@ create_initial_post_types(); register_theme_directory( get_theme_root() ); // Load active plugins. -foreach ( wp_get_active_and_valid_plugins() as $plugin ) +foreach ( wp_get_active_and_valid_plugins() as $plugin ) { + wp_register_plugin_realpath( $plugin ); include_once( $plugin ); +} unset( $plugin ); // Load pluggable functions.