Skip to content

Commit

Permalink
Various plugin-related fixes and enhancements
Browse files Browse the repository at this point in the history
- Check that a plugin is loaded when accessing its pages to prevent
  errors
- Improved error messages
- Simplified handling of MantisCore pseudo-plugin
- Remove direct access to $g_plugin_cache global variable
- Improved plugin file validation regex
- No re-registration of previously registered plugins
- Fixed data type issues in plugin API
- Various code cleanup and whitespace fixes

Fixes #17359, #17366, #17368
  • Loading branch information
dregad committed Jun 2, 2014
2 parents 05f1037 + 14a0959 commit 506088e
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 77 deletions.
4 changes: 4 additions & 0 deletions core/constant_inc.php
Expand Up @@ -383,6 +383,10 @@
define( 'ERROR_PLUGIN_UPGRADE_FAILED', 2503 );
define( 'ERROR_PLUGIN_INSTALL_FAILED', 2504 );
define( 'ERROR_PLUGIN_UPGRADE_NEEDED', 2505 );
define( 'ERROR_PLUGIN_NOT_LOADED', 2506 );
define( 'ERROR_PLUGIN_INVALID_PAGE', 2507 );
define( 'ERROR_PLUGIN_INVALID_FILE', 2508 );
define( 'ERROR_PLUGIN_FILE_NOT_FOUND', 2509 );
define( 'ERROR_PLUGIN_GENERIC', 2599 );

# ERROR_COLUMNS_*
Expand Down
87 changes: 63 additions & 24 deletions core/plugin_api.php
Expand Up @@ -82,6 +82,43 @@ function plugin_pop_current() {
return( isset( $g_plugin_current[0] ) ? array_shift( $g_plugin_current ) : null );
}

/**
* Returns the list of force-installed plugins
* @see $g_plugins_force_installed
* @return array List of plugins (basename => priority)
*/
function plugin_get_force_installed() {
$t_forced_plugins = config_get_global( 'plugins_force_installed' );

# MantisCore pseudo-plugin is force-installed by definition, with priority 3
$t_forced_plugins['MantisCore'] = 3;

return $t_forced_plugins;
}

/**
* Returns an object representing the specified plugin
* Triggers an error if the plugin is not registered
* @param string|null $p_basename Plugin base name (defaults to current plugin)
* @return object Plugin Object
*/
function plugin_get( $p_basename = null ) {
global $g_plugin_cache;

if( is_null( $p_basename ) ) {
$t_current = plugin_get_current();
} else {
$t_current = $p_basename;
}

if ( !plugin_is_registered( $t_current ) ) {
error_parameters( $t_current );
trigger_error( ERROR_PLUGIN_NOT_REGISTERED, ERROR );
}

return $g_plugin_cache[$p_basename];
}

/**
* Get the URL to the plugin wrapper page.
* @param string $p_page Page name
Expand Down Expand Up @@ -143,7 +180,7 @@ function plugin_file( $p_file, $p_redirect = false, $p_base_name = null ) {
*/
function plugin_file_include( $p_filename, $p_basename = null ) {

global $g_plugin_mime_types;
global $g_plugin_mime_types;

if( is_null( $p_basename ) ) {
$t_current = plugin_get_current();
Expand All @@ -153,7 +190,8 @@ function plugin_file_include( $p_filename, $p_basename = null ) {

$t_file_path = plugin_file_path( $p_filename, $t_current );
if( false === $t_file_path ) {
trigger_error( ERROR_GENERIC, ERROR );
error_parameters( $t_current, $p_filename );
trigger_error( ERROR_PLUGIN_FILE_NOT_FOUND, ERROR );
}

$t_content_type = '';
Expand All @@ -176,7 +214,7 @@ function plugin_file_include( $p_filename, $p_basename = null ) {
}

if ( $t_content_type )
header('Content-Type: ' . $t_content_type );
header('Content-Type: ' . $t_content_type );

readfile( $t_file_path );
}
Expand Down Expand Up @@ -557,11 +595,6 @@ function plugin_dependency( $p_base_name, $p_required, $p_initialized = false )
function plugin_protected( $p_base_name ) {
global $g_plugin_cache_protected;

# For pseudo-plugin MantisCore, return protected as 1.
if( $p_base_name == 'MantisCore' ) {
return 1;
}

return $g_plugin_cache_protected[$p_base_name];
}

Expand All @@ -573,11 +606,6 @@ function plugin_protected( $p_base_name ) {
function plugin_priority( $p_base_name ) {
global $g_plugin_cache_priority;

# For pseudo-plugin MantisCore, return priority as 3.
if( $p_base_name == 'MantisCore' ) {
return 3;
}

return $g_plugin_cache_priority[$p_base_name];
}

Expand All @@ -587,15 +615,13 @@ function plugin_priority( $p_base_name ) {
* @return bool True if plugin is installed
*/
function plugin_is_installed( $p_basename ) {
$t_plugin_table = db_get_table( 'plugin' );

$t_forced_plugins = config_get_global( 'plugins_force_installed' );
foreach( $t_forced_plugins as $t_basename => $t_priority ) {
foreach( plugin_get_force_installed() as $t_basename => $t_priority ) {
if ( $t_basename == $p_basename ) {
return true;
}
}

$t_plugin_table = db_get_table( 'plugin' );
$t_query = "SELECT COUNT(*) FROM $t_plugin_table WHERE basename=" . db_param();
$t_result = db_query_bound( $t_query, array( $p_basename ) );
return( 0 < db_result( $t_result ) );
Expand All @@ -610,6 +636,7 @@ function plugin_install( $p_plugin ) {
access_ensure_global_level( config_get_global( 'manage_plugin_threshold' ) );

if( plugin_is_installed( $p_plugin->basename ) ) {
error_parameters( $p_plugin->basename );
trigger_error( ERROR_PLUGIN_ALREADY_INSTALLED, WARNING );
return null;
}
Expand Down Expand Up @@ -815,6 +842,17 @@ function plugin_require_api( $p_file, $p_basename = null ) {
require_once( $t_path . $p_file );
}

/**
* Determine if a given plugin is registered.
* @param string $p_basename Plugin basename
* @return boolean True if plugin is registered
*/
function plugin_is_registered( $p_basename ) {
global $g_plugin_cache;

return isset( $g_plugin_cache[$p_basename] );
}

/**
* Register a plugin with MantisBT.
* The plugin class must already be loaded before calling.
Expand Down Expand Up @@ -867,13 +905,13 @@ function plugin_register( $p_basename, $p_return = false, $p_child = null ) {

/**
* Find and register all installed plugins.
* This includes the MantisCore pseudo-plugin.
*/
function plugin_register_installed() {
global $g_plugin_cache_priority, $g_plugin_cache_protected;

# register plugins specified in the site configuration
$t_forced_plugins = config_get_global( 'plugins_force_installed' );
foreach( $t_forced_plugins as $t_basename => $t_priority ) {
foreach( plugin_get_force_installed() as $t_basename => $t_priority ) {
plugin_register( $t_basename );
$g_plugin_cache_priority[$t_basename] = $t_priority;
$g_plugin_cache_protected[$t_basename] = true;
Expand All @@ -883,13 +921,15 @@ function plugin_register_installed() {
$t_plugin_table = db_get_table( 'plugin' );

$t_query = "SELECT basename, priority, protected FROM $t_plugin_table WHERE enabled=" . db_param() . ' ORDER BY priority DESC';
$t_result = db_query_bound( $t_query, array( 1 ) );
$t_result = db_query_bound( $t_query, array( true ) );

while( $t_row = db_fetch_array( $t_result ) ) {
$t_basename = $t_row['basename'];
plugin_register( $t_basename );
$g_plugin_cache_priority[$t_basename] = $t_row['priority'];
$g_plugin_cache_protected[$t_basename] = $t_row['protected'];
if( !plugin_is_registered( $t_basename ) ) {
plugin_register( $t_basename );
$g_plugin_cache_priority[$t_basename] = (int)$t_row['priority'];
$g_plugin_cache_protected[$t_basename] = (bool)$t_row['protected'];
}
}
}

Expand All @@ -909,7 +949,6 @@ function plugin_init_installed() {
$g_plugin_cache_priority = array();
$g_plugin_cache_protected = array();

plugin_register( 'MantisCore' );
plugin_register_installed();

$t_plugins = array_keys( $g_plugin_cache );
Expand Down
2 changes: 1 addition & 1 deletion core/print_api.php
Expand Up @@ -1152,7 +1152,7 @@ function print_plugin_priority_list( $p_priority ) {
}

for( $i = 5;$i >= 1;$i-- ) {
echo '<option value="', $i, '" ', check_selected( $p_priority, (string)$i ), ' >', $i, '</option>';
echo '<option value="', $i, '" ', check_selected( $p_priority, $i ), ' >', $i, '</option>';
}
}

Expand Down
10 changes: 7 additions & 3 deletions lang/strings_english.txt
Expand Up @@ -1667,12 +1667,16 @@ $MANTIS_ERROR[ERROR_TAG_NOT_ATTACHED] = 'That tag is not attached to that issue.
$MANTIS_ERROR[ERROR_TAG_ALREADY_ATTACHED] = 'That tag already attached to that issue.';
$MANTIS_ERROR[ERROR_TOKEN_NOT_FOUND] = 'Token could not be found.';
$MANTIS_ERROR[ERROR_EVENT_UNDECLARED] = 'Event "%1$s" has not yet been declared.';
$MANTIS_ERROR[ERROR_PLUGIN_NOT_REGISTERED] = 'Plugin is not registered with MantisBT.';
$MANTIS_ERROR[ERROR_PLUGIN_ALREADY_INSTALLED] = 'Plugin is already installed.';
$MANTIS_ERROR[ERROR_PLUGIN_PAGE_NOT_FOUND] = 'Plugin page not found.';
$MANTIS_ERROR[ERROR_PLUGIN_NOT_REGISTERED] = 'Plugin "%1$s" is not registered.';
$MANTIS_ERROR[ERROR_PLUGIN_NOT_LOADED] = 'Plugin "%1$s" is not loaded, make sure its dependencies are met.';
$MANTIS_ERROR[ERROR_PLUGIN_ALREADY_INSTALLED] = 'Plugin "%1$s" is already installed.';
$MANTIS_ERROR[ERROR_PLUGIN_PAGE_NOT_FOUND] = 'Page "%2$s" does not exist in Plugin "%1$s".';
$MANTIS_ERROR[ERROR_PLUGIN_FILE_NOT_FOUND] = 'File "%2$s" does not exist in Plugin "%1$s".';
$MANTIS_ERROR[ERROR_PLUGIN_INSTALL_FAILED] = 'Plugin installation failed: %1$s.';
$MANTIS_ERROR[ERROR_PLUGIN_UPGRADE_FAILED] = 'Upgrading the plugin schema failed in block #%1$s.';
$MANTIS_ERROR[ERROR_PLUGIN_UPGRADE_NEEDED] = 'The "%1$s" plugin needs to be upgraded before you can access this page.';
$MANTIS_ERROR[ERROR_PLUGIN_INVALID_PAGE] = 'The format of the specified plugin page "%1$s" is invalid. It must match "Plugin[/path/to]/page".';
$MANTIS_ERROR[ERROR_PLUGIN_INVALID_FILE] = 'The format of the specified plugin file "%1$s" is invalid. It must match "Plugin[/path/to]/file[.ext]".';
$MANTIS_ERROR[ERROR_PLUGIN_GENERIC] = 'There was an unknown error "%1$s" during execution of the "%2$s" plugin.';
$MANTIS_ERROR[ERROR_COLUMNS_DUPLICATE] = 'Field "%1$s" contains duplicate column "%2$s".';
$MANTIS_ERROR[ERROR_COLUMNS_INVALID] = 'Field "%1$s" contains invalid field "%2$s".';
Expand Down
58 changes: 33 additions & 25 deletions manage_plugin_page.php
Expand Up @@ -55,27 +55,18 @@

print_manage_menu( 'manage_plugin_page.php' );

/**
* Sort Plugins by name
* @param MantisPlugin $p_plugin1 Plugin 1
* @param MantisPlugin $p_plugin2 Plugin 2
* @return string
*/
function plugin_sort( $p_plugin1, $p_plugin2 ) {
return strcasecmp( $p_plugin1->name, $p_plugin2->name );
}

$t_plugins = plugin_find_all();
uasort( $t_plugins, 'plugin_sort' );

global $g_plugin_cache;
uasort( $t_plugins,
function ( $p1, $p2 ) {
return strcasecmp( $p1->name, $p2->name );
}
);

$t_plugins_installed = array();
$t_plugins_available = array();
$t_forced_plugins = config_get_global( 'plugins_force_installed' );

foreach( $t_plugins as $t_basename => $t_plugin ) {
if ( isset( $g_plugin_cache[$t_basename] ) ) {
if( plugin_is_registered( $t_basename ) ) {
$t_plugins_installed[$t_basename] = $t_plugin;
} else {
$t_plugins_available[$t_basename] = $t_plugin;
Expand Down Expand Up @@ -121,7 +112,6 @@ function plugin_sort( $p_plugin1, $p_plugin2 ) {
$t_url = $t_plugin->url;
$t_requires = $t_plugin->requires;
$t_depends = array();
$t_forced = isset( $t_forced_plugins[ $t_basename ] );
$t_priority = plugin_priority( $t_basename );
$t_protected = plugin_protected( $t_basename );

Expand All @@ -147,7 +137,6 @@ function plugin_sort( $p_plugin1, $p_plugin2 ) {
}

$t_upgrade = plugin_needs_upgrade( $t_plugin );
$t_uninstall = ( 'MantisCore' != $t_basename && !$t_protected );

if ( is_array( $t_requires ) ) {
foreach( $t_requires as $t_plugin => $t_version ) {
Expand Down Expand Up @@ -178,16 +167,30 @@ function plugin_sort( $p_plugin1, $p_plugin2 ) {
echo '<td class="small center">',$t_depends,'</td>';
if ( 'MantisCore' == $t_basename ) {
echo '<td>&#160;</td><td>&#160;</td>';
} else if ( $t_forced ) {
echo '<td class="center">','<select disabled="disabled">',print_plugin_priority_list( $t_priority ),'</select>','</td>';
echo '<td class="center">','<input type="checkbox" checked="checked" disabled="disabled"/>','</td>';
} else {
echo '<td class="center">','<select name="priority_',$t_basename,'">',print_plugin_priority_list( $t_priority ),'</select>','</td>';
echo '<td class="center">','<input type="checkbox" name="protected_',$t_basename,'" '.( $t_protected ? 'checked="checked" ' : '').'/>','</td>';
echo '<td class="center">',
'<select name="priority_' . $t_basename . '"',
check_disabled( $t_protected ), '>',
print_plugin_priority_list( $t_priority ),
'</select>','</td>';
echo '<td class="center">',
'<input type="checkbox" name="protected_' . $t_basename . '"',
check_disabled( $t_protected ), check_checked( $t_protected ), ' />',
'</select>','</td>';
}
echo '<td class="center">';
if ( $t_upgrade ) { print_bracket_link( 'manage_plugin_upgrade.php?name=' . $t_basename . form_security_param( 'manage_plugin_upgrade' ), lang_get( 'plugin_upgrade' ) ); }
if ( $t_uninstall ) { print_bracket_link( 'manage_plugin_uninstall.php?name=' . $t_basename . form_security_param( 'manage_plugin_uninstall' ), lang_get( 'plugin_uninstall' ) ); }
if( $t_upgrade ) {
print_bracket_link(
'manage_plugin_upgrade.php?name=' . $t_basename . form_security_param( 'manage_plugin_upgrade' ),
lang_get( 'plugin_upgrade' )
);
}
if( !$t_protected ) {
print_bracket_link(
'manage_plugin_uninstall.php?name=' . $t_basename . form_security_param( 'manage_plugin_uninstall' ),
lang_get( 'plugin_uninstall' )
);
}
echo '</td></tr>';
} ?>
</tbody>
Expand Down Expand Up @@ -286,7 +289,12 @@ function plugin_sort( $p_plugin1, $p_plugin2 ) {
echo '<td class="small">',$t_description,$t_author,$t_url,'</td>';
echo '<td class="center">',$t_depends,'</td>';
echo '<td class="center">';
if ( $t_ready ) { print_bracket_link( 'manage_plugin_install.php?name=' . $t_basename . form_security_param( 'manage_plugin_install' ), lang_get( 'plugin_install' ) ); }
if( $t_ready ) {
print_bracket_link(
'manage_plugin_install.php?name=' . $t_basename . form_security_param( 'manage_plugin_install' ),
lang_get( 'plugin_install' )
);
}
echo '</td></tr>';
}
?>
Expand Down
34 changes: 18 additions & 16 deletions plugin.php
Expand Up @@ -35,33 +35,35 @@

$t_plugin_path = config_get( 'plugin_path' );

$f_page= gpc_get_string( 'page' );
$t_matches = array();
$f_page = gpc_get_string( 'page' );

if ( !preg_match( '/^([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+[\/a-zA-Z0-9_-]*)/', $f_page, $t_matches ) ) {
trigger_error( ERROR_GENERIC, ERROR );
if( !preg_match( '/^([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+[\/a-zA-Z0-9_-]*)/', $f_page, $t_matches ) ) {
error_parameters( $f_page );
trigger_error( ERROR_PLUGIN_INVALID_PAGE, ERROR );
}

$t_basename = $t_matches[1];
$t_action = $t_matches[2];

global $g_plugin_cache;
if ( !isset( $g_plugin_cache[$t_basename] ) ) {
trigger_error( ERROR_PLUGIN_NOT_REGISTERED, ERROR );
}

$t_page = $t_plugin_path.$t_basename.DIRECTORY_SEPARATOR.
'pages'.DIRECTORY_SEPARATOR.$t_action.'.php';
$t_plugin = plugin_get( $t_basename );

if ( !is_file( $t_page ) ) {
trigger_error( ERROR_PLUGIN_PAGE_NOT_FOUND, ERROR );
if( plugin_needs_upgrade( $t_plugin ) ) {
error_parameters( $t_basename );
trigger_error( ERROR_PLUGIN_UPGRADE_NEEDED, ERROR );
}

if( plugin_needs_upgrade( $g_plugin_cache[$t_basename] ) ) {
# Plugin can be registered but fail to load e.g. due to unmet dependencies
if( !plugin_is_loaded( $t_basename ) ) {
error_parameters( $t_basename );
trigger_error( ERROR_PLUGIN_UPGRADE_NEEDED, ERROR );
trigger_error( ERROR_PLUGIN_NOT_LOADED, ERROR );
}

$t_page = $t_plugin_path . $t_basename . '/pages/' . $t_action . '.php';

if ( !is_file( $t_page ) ) {
error_parameters( $t_basename, $t_action );
trigger_error( ERROR_PLUGIN_PAGE_NOT_FOUND, ERROR );
}

plugin_push_current( $t_basename );
include( $t_page );

0 comments on commit 506088e

Please sign in to comment.