Fix #11464 phpMyAdmin suggests upgrading to newer version not usable on that system

Signed-off-by: Madhura Jayaratne <madhura.cj@gmail.com>
This commit is contained in:
Madhura Jayaratne 2015-09-22 21:35:05 +10:00
parent f18ac85048
commit 2da36e93b2
9 changed files with 560 additions and 231 deletions

View File

@ -7,6 +7,7 @@ phpMyAdmin - ChangeLog
- issue #11480 Notice Undefined index: drop_database
- issue #11486 Server variable edition in ANSI_QUOTES sql_mode: losing current value
- issue #11491 Propose table structure broken
- issue #11464 phpMyAdmin suggests upgrading to newer version not usable on that system
4.5.0.0 (not yet released)
+ rfe Pagination for GIS visualization

View File

@ -3971,7 +3971,7 @@ AJAX.registerOnload('functions.js', function () {
* Load version information asynchronously.
*/
if ($('li.jsversioncheck').length > 0) {
$.getJSON('version_check.php', {}, PMA_current_version);
$.getJSON('version_check.php', {'server' : PMA_commonParams.get('server')}, PMA_current_version);
}
if ($('#is_git_revision').length > 0) {

View File

@ -4372,161 +4372,6 @@ class PMA_Util
curl_setopt($curl_handle, CURLOPT_USERAGENT, 'phpMyAdmin/' . PMA_VERSION);
return $curl_handle;
}
/**
* Returns information with latest version from phpmyadmin.net
*
* @return object JSON decoded object with the data
*/
public static function getLatestVersion()
{
if (!$GLOBALS['cfg']['VersionCheck']) {
return new stdClass();
}
// wait 3s at most for server response, it's enough to get information
// from a working server
$connection_timeout = 3;
$response = '{}';
// Get response text from phpmyadmin.net or from the session
// Update cache every 6 hours
if (isset($_SESSION['cache']['version_check'])
&& time() < $_SESSION['cache']['version_check']['timestamp'] + 3600 * 6
) {
$save = false;
$response = $_SESSION['cache']['version_check']['response'];
} else {
$save = true;
$file = 'https://www.phpmyadmin.net/home_page/version.json';
if (ini_get('allow_url_fopen')) {
$context = array(
'http' => array(
'request_fulluri' => true,
'timeout' => $connection_timeout,
)
);
$context = PMA_Util::handleContext($context);
if (! defined('TESTSUITE')) {
session_write_close();
}
$response = file_get_contents(
$file,
false,
stream_context_create($context)
);
} else if (function_exists('curl_init')) {
$curl_handle = curl_init($file);
if ($curl_handle === false) {
return null;
}
$curl_handle = PMA_Util::configureCurl($curl_handle);
curl_setopt(
$curl_handle,
CURLOPT_HEADER,
false
);
curl_setopt(
$curl_handle,
CURLOPT_RETURNTRANSFER,
true
);
curl_setopt(
$curl_handle,
CURLOPT_TIMEOUT,
$connection_timeout
);
if (! defined('TESTSUITE')) {
session_write_close();
}
$response = curl_exec($curl_handle);
}
}
$data = json_decode($response);
if (is_object($data)
&& ! empty($data->version)
&& ! empty($data->date)
&& $save
) {
if (! isset($_SESSION) && ! defined('TESTSUITE')) {
ini_set('session.use_only_cookies', 'false');
ini_set('session.use_cookies', 'false');
ini_set('session.use_trans_sid', 'false');
ini_set('session.cache_limiter', 'nocache');
session_start();
}
$_SESSION['cache']['version_check'] = array(
'response' => $response,
'timestamp' => time()
);
}
return $data;
}
/**
* Calculates numerical equivalent of phpMyAdmin version string
*
* @param string $version version
*
* @return mixed false on failure, integer on success
*/
public static function versionToInt($version)
{
$parts = explode('-', $version);
if (count($parts) > 1) {
$suffix = $parts[1];
} else {
$suffix = '';
}
$parts = explode('.', $parts[0]);
$result = 0;
if (count($parts) >= 1 && is_numeric($parts[0])) {
$result += 1000000 * $parts[0];
}
if (count($parts) >= 2 && is_numeric($parts[1])) {
$result += 10000 * $parts[1];
}
if (count($parts) >= 3 && is_numeric($parts[2])) {
$result += 100 * $parts[2];
}
if (count($parts) >= 4 && is_numeric($parts[3])) {
$result += 1 * $parts[3];
}
if (!empty($suffix)) {
$matches = array();
if (preg_match('/^(\D+)(\d+)$/', $suffix, $matches)) {
$suffix = $matches[1];
$result += intval($matches[2]);
}
switch ($suffix) {
case 'pl':
$result += 60;
break;
case 'rc':
$result += 30;
break;
case 'beta':
$result += 20;
break;
case 'alpha':
$result += 10;
break;
case 'dev':
$result += 0;
break;
}
} else {
$result += 50; // for final
}
return $result;
}
/**
* Add fractional seconds to time, datetime and timestamp strings.

View File

@ -0,0 +1,270 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Responsile for retrieving version information and notifiying about latest version
*
* @package PhpMyAdmin
*/
if (! defined('PHPMYADMIN')) {
exit;
}
/**
* Responsile for retrieving version information and notifiying about latest version
*
* @package PhpMyAdmin
*
*/
class VersionInformation
{
/**
* Returns information with latest version from phpmyadmin.net
*
* @return object JSON decoded object with the data
*/
public function getLatestVersion()
{
if (!$GLOBALS['cfg']['VersionCheck']) {
return new stdClass();
}
// wait 3s at most for server response, it's enough to get information
// from a working server
$connection_timeout = 3;
$response = '{}';
// Get response text from phpmyadmin.net or from the session
// Update cache every 6 hours
if (isset($_SESSION['cache']['version_check'])
&& time() < $_SESSION['cache']['version_check']['timestamp'] + 3600 * 6
) {
$save = false;
$response = $_SESSION['cache']['version_check']['response'];
} else {
$save = true;
$file = 'https://www.phpmyadmin.net/home_page/version.json';
if (ini_get('allow_url_fopen')) {
$context = array(
'http' => array(
'request_fulluri' => true,
'timeout' => $connection_timeout,
)
);
$context = PMA_Util::handleContext($context);
if (! defined('TESTSUITE')) {
session_write_close();
}
$response = file_get_contents(
$file,
false,
stream_context_create($context)
);
} else if (function_exists('curl_init')) {
$curl_handle = curl_init($file);
if ($curl_handle === false) {
return null;
}
$curl_handle = PMA_Util::configureCurl($curl_handle);
curl_setopt(
$curl_handle,
CURLOPT_HEADER,
false
);
curl_setopt(
$curl_handle,
CURLOPT_RETURNTRANSFER,
true
);
curl_setopt(
$curl_handle,
CURLOPT_TIMEOUT,
$connection_timeout
);
if (! defined('TESTSUITE')) {
session_write_close();
}
$response = curl_exec($curl_handle);
}
}
$data = json_decode($response);
if (is_object($data)
&& ! empty($data->version)
&& ! empty($data->date)
&& $save
) {
if (! isset($_SESSION) && ! defined('TESTSUITE')) {
ini_set('session.use_only_cookies', 'false');
ini_set('session.use_cookies', 'false');
ini_set('session.use_trans_sid', 'false');
ini_set('session.cache_limiter', 'nocache');
session_start();
}
$_SESSION['cache']['version_check'] = array(
'response' => $response,
'timestamp' => time()
);
}
return $data;
}
/**
* Calculates numerical equivalent of phpMyAdmin version string
*
* @param string $version version
*
* @return mixed false on failure, integer on success
*/
public function versionToInt($version)
{
$parts = explode('-', $version);
if (count($parts) > 1) {
$suffix = $parts[1];
} else {
$suffix = '';
}
$parts = explode('.', $parts[0]);
$result = 0;
if (count($parts) >= 1 && is_numeric($parts[0])) {
$result += 1000000 * $parts[0];
}
if (count($parts) >= 2 && is_numeric($parts[1])) {
$result += 10000 * $parts[1];
}
if (count($parts) >= 3 && is_numeric($parts[2])) {
$result += 100 * $parts[2];
}
if (count($parts) >= 4 && is_numeric($parts[3])) {
$result += 1 * $parts[3];
}
if (!empty($suffix)) {
$matches = array();
if (preg_match('/^(\D+)(\d+)$/', $suffix, $matches)) {
$suffix = $matches[1];
$result += intval($matches[2]);
}
switch ($suffix) {
case 'pl':
$result += 60;
break;
case 'rc':
$result += 30;
break;
case 'beta':
$result += 20;
break;
case 'alpha':
$result += 10;
break;
case 'dev':
$result += 0;
break;
}
} else {
$result += 50; // for final
}
return $result;
}
/**
* Returns the version and date of the latest phpMyAdmin version compatible
* with avilable PHP and MySQL versions
*
* @param array $releases array of information related to each version
*
* @return array containing the version and date of latest compatibel version
*/
public function getLatestCompatibleVersion($releases)
{
foreach ($releases as $release) {
$phpVersions = $release->php_versions;
$phpConditions = explode(",", $phpVersions);
foreach ($phpConditions as $phpCondition) {
if (! $this->evaluateVersionCondition("PHP", $phpCondition)) {
continue 2;
}
}
// We evalute MySQL version constraint if there are only
// one server configured.
if (count($GLOBALS['cfg']['Servers']) == 1) {
$mysqlVersions = $release->mysql_versions;
$mysqlConditions = explode(",", $mysqlVersions);
foreach ($mysqlConditions as $mysqlCondition) {
if (! $this->evaluateVersionCondition('MySQL', $mysqlCondition)) {
continue 2;
}
}
}
return array(
'version' => $release->version,
'date' => $release->date,
);
}
// no compatible version
return null;
}
/**
* Checks whether PHP or MySQL version meets supplied version condition
*
* @param string $type PHP or MySQL
* @param string $condition version condition
*
* @return boolean whether the condition is met
*/
public function evaluateVersionCondition($type, $condition)
{
$operator = null;
$operators = array("<=", ">=", "!=", "<>", "<", ">", "="); // preserve order
foreach ($operators as $oneOperator) {
if (strpos($condition, $oneOperator) === 0) {
$operator = $oneOperator;
$version = substr($condition, strlen($oneOperator));
break;
}
}
$myVersion = null;
if ($type == 'PHP') {
$myVersion = $this->getPHPVersion();
} elseif ($type == 'MySQL') {
$myVersion = $this->getMySQLVersion();
}
if ($myVersion != null && $operator != null) {
return version_compare($myVersion, $version, $operator);
}
return false;
}
/**
* Returns the PHP version
*
* @return string PHP version
*/
protected function getPHPVersion()
{
return PHP_VERSION;
}
/**
* Returns the MySQL version
*
* @return string MySQL version
*/
protected function getMySQLVersion()
{
return PMA_Util::cacheGet('PMA_MYSQL_STR_VERSION');
}
}
?>

View File

@ -16,6 +16,7 @@ if (!defined('PHPMYADMIN')) {
require_once './libraries/display_select_lang.lib.php';
require_once './libraries/config/FormDisplay.class.php';
require_once './libraries/config/ServerConfigChecks.class.php';
require_once './libraries/VersionInformation.php';
require_once './setup/lib/index.lib.php';
// prepare unfiltered language list

View File

@ -108,7 +108,8 @@ function PMA_versionCheck()
$message_id = uniqid('version_check');
// Fetch data
$version_data = PMA_Util::getLatestVersion();
$versionInformation = new VersionInformation();
$version_data = $versionInformation->getLatestVersion();
if (empty($version_data)) {
PMA_messagesSet(
@ -123,10 +124,16 @@ function PMA_versionCheck()
return;
}
$version = $version_data->version;
$date = $version_data->date;
$releases = $version_data->releases;
$latestCompatible = $versionInformation->getLatestCompatibleVersion($releases);
if ($latestCompatible != null) {
$version = $latestCompatible['version'];
$date = $latestCompatible['date'];
} else {
return;
}
$version_upstream = PMA_Util::versionToInt($version);
$version_upstream = $versionInformation->versionToInt($version);
if ($version_upstream === false) {
PMA_messagesSet(
'error',
@ -137,7 +144,7 @@ function PMA_versionCheck()
return;
}
$version_local = PMA_Util::versionToInt(
$version_local = $versionInformation->versionToInt(
$GLOBALS['PMA_Config']->get('PMA_VERSION')
);
if ($version_local === false) {

View File

@ -71,72 +71,6 @@ class PMA_Util_Test extends PHPUnit_Framework_TestCase
);
}
/**
* Test version checking
*
* @return void
*
* @group large
*/
public function testGetLatestVersion()
{
$GLOBALS['cfg']['ProxyUrl'] = PROXY_URL;
$GLOBALS['cfg']['ProxyUser'] = PROXY_USER;
$GLOBALS['cfg']['ProxyPass'] = PROXY_PASS;
$GLOBALS['cfg']['VersionCheck'] = true;
$version = PMA_Util::getLatestVersion();
$this->assertNotEmpty($version->version);
$this->assertNotEmpty($version->date);
}
/**
* Test version to int conversion.
*
* @param string $version Version string
* @param int $numeric Integer matching version
*
* @return void
*
* @dataProvider dataVersions
*/
public function testVersionToInt($version, $numeric)
{
$this->assertEquals(
$numeric,
PMA_Util::versionToInt($version)
);
}
/**
* Data provider for version parsing
*
* @return array with test data
*/
public function dataVersions()
{
return array(
array('1.0.0', 1000050),
array('2.0.0.2-dev', 2000002),
array('3.4.2.1', 3040251),
array('3.4.2-dev3', 3040203),
array('3.4.2-dev', 3040200),
array('3.4.2-pl', 3040260),
array('3.4.2-pl3', 3040263),
array('4.4.2-rc22', 4040252),
array('4.4.2-rc', 4040230),
array('4.4.22-beta22', 4042242),
array('4.4.22-beta', 4042220),
array('4.4.21-alpha22', 4042132),
array('4.4.20-alpha', 4042010),
array('4.40.20-alpha-dev', 4402010),
array('4.4a', 4000050),
array('4.4.4-test', 4040400),
array('4.1.0', 4010050),
array('4.0.1.3', 4000153),
array('4.1-dev', 4010000),
);
}
/**
* Test for isForeignKeyCheck
*

View File

@ -0,0 +1,260 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Tests for methods in VersionInformation class
*
* @package PhpMyAdmin-test
*/
require_once 'libraries/Util.class.php';
/*
* Include to test.
*/
require_once 'libraries/VersionInformation.php';
/**
* Tests for methods in VersionInformation class
*
* @package PhpMyAdmin-test
*/
class VersionInformationTest extends PHPUnit_Framework_TestCase
{
private $_releases;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*
* @return void
*/
protected function setUp()
{
$this->_releases = array();
$release = new stdClass();
$release->date = "2015-09-08";
$release->php_versions = ">=5.3,<7.1";
$release->version = "4.4.14.1";
$release->mysql_versions = ">=5.5";
$this->_releases[] = $release;
$release = new stdClass();
$release->date = "2015-09-09";
$release->php_versions = ">=5.3,<7.0";
$release->version = "4.4.13.3";
$release->mysql_versions = ">=5.5";
$this->_releases[] = $release;
$release = new stdClass();
$release->date = "2015-05-13";
$release->php_versions = ">=5.2,<5.3";
$release->version = "4.0.10.10";
$release->mysql_versions = ">=5.0";
$this->_releases[] = $release;
}
/**
* Test version checking
*
* @return void
*
* @group large
*/
public function testGetLatestVersion()
{
$GLOBALS['cfg']['ProxyUrl'] = PROXY_URL;
$GLOBALS['cfg']['ProxyUser'] = PROXY_USER;
$GLOBALS['cfg']['ProxyPass'] = PROXY_PASS;
$GLOBALS['cfg']['VersionCheck'] = true;
$versionInformation = new VersionInformation();
$version = $versionInformation->getLatestVersion();
$this->assertNotEmpty($version->version);
$this->assertNotEmpty($version->date);
}
/**
* Test version to int conversion.
*
* @param string $version Version string
* @param int $numeric Integer matching version
*
* @return void
*
* @dataProvider dataVersions
*/
public function testVersionToInt($version, $numeric)
{
$versionInformation = new VersionInformation();
$this->assertEquals(
$numeric,
$versionInformation->versionToInt($version)
);
}
/**
* Data provider for version parsing
*
* @return array with test data
*/
public function dataVersions()
{
return array(
array('1.0.0', 1000050),
array('2.0.0.2-dev', 2000002),
array('3.4.2.1', 3040251),
array('3.4.2-dev3', 3040203),
array('3.4.2-dev', 3040200),
array('3.4.2-pl', 3040260),
array('3.4.2-pl3', 3040263),
array('4.4.2-rc22', 4040252),
array('4.4.2-rc', 4040230),
array('4.4.22-beta22', 4042242),
array('4.4.22-beta', 4042220),
array('4.4.21-alpha22', 4042132),
array('4.4.20-alpha', 4042010),
array('4.40.20-alpha-dev', 4402010),
array('4.4a', 4000050),
array('4.4.4-test', 4040400),
array('4.1.0', 4010050),
array('4.0.1.3', 4000153),
array('4.1-dev', 4010000),
);
}
/**
* Tests getLatestCompatibleVersion() when there is only one server confgiured
*
* @return void
*/
public function testGetLatestCompatibleVersionWithSingleServer()
{
$GLOBALS['cfg']['Servers'] = array(
array()
);
$mockVersionInfo = $this->getMockBuilder('VersionInformation')
->setMethods(array('evaluateVersionCondition'))
->getMock();
$mockVersionInfo->expects($this->at(0))
->method('evaluateVersionCondition')
->with('PHP', '>=5.3')
->will($this->returnValue(true));
$mockVersionInfo->expects($this->at(1))
->method('evaluateVersionCondition')
->with('PHP', '<7.1')
->will($this->returnValue(true));
$mockVersionInfo->expects($this->at(2))
->method('evaluateVersionCondition')
->with('MySQL', '>=5.5')
->will($this->returnValue(true));
$compatible = $mockVersionInfo
->getLatestCompatibleVersion($this->_releases);
$this->assertEquals('4.4.14.1', $compatible['version']);
}
/**
* Tests getLatestCompatibleVersion() when there are multiple servers configured
*
* @return void
*/
public function testGetLaestCompatibleVersionWithMultipleServers()
{
$GLOBALS['cfg']['Servers'] = array(
array(),
array()
);
$mockVersionInfo = $this->getMockBuilder('VersionInformation')
->setMethods(array('evaluateVersionCondition'))
->getMock();
$mockVersionInfo->expects($this->at(0))
->method('evaluateVersionCondition')
->with('PHP', '>=5.3')
->will($this->returnValue(true));
$mockVersionInfo->expects($this->at(1))
->method('evaluateVersionCondition')
->with('PHP', '<7.1')
->will($this->returnValue(true));
$compatible = $mockVersionInfo
->getLatestCompatibleVersion($this->_releases);
$this->assertEquals('4.4.14.1', $compatible['version']);
}
/**
* Tests getLatestCompatibleVersion() with an old PHP version
*
* @return void
*/
public function testGetLaestCompatibleVersionWithOldPHPVersion()
{
$GLOBALS['cfg']['Servers'] = array(
array(),
array()
);
$mockVersionInfo = $this->getMockBuilder('VersionInformation')
->setMethods(array('evaluateVersionCondition'))
->getMock();
$mockVersionInfo->expects($this->at(0))
->method('evaluateVersionCondition')
->with('PHP', '>=5.3')
->will($this->returnValue(false));
$mockVersionInfo->expects($this->at(1))
->method('evaluateVersionCondition')
->with('PHP', '>=5.3')
->will($this->returnValue(false));
$mockVersionInfo->expects($this->at(2))
->method('evaluateVersionCondition')
->with('PHP', '>=5.2')
->will($this->returnValue(true));
$mockVersionInfo->expects($this->at(3))
->method('evaluateVersionCondition')
->with('PHP', '<5.3')
->will($this->returnValue(true));
$compatible = $mockVersionInfo
->getLatestCompatibleVersion($this->_releases);
$this->assertEquals('4.0.10.10', $compatible['version']);
}
/**
* Tests evaluateVersionCondition() method
*
* @return void
*/
public function testEvaluateVersionCondition()
{
$mockVersionInfo = $this->getMockBuilder('VersionInformation')
->setMethods(array('getPHPVersion'))
->getMock();
$mockVersionInfo->expects($this->any())
->method('getPHPVersion')
->will($this->returnValue('5.2.4'));
$this->assertTrue($mockVersionInfo->evaluateVersionCondition('PHP', '<=5.3'));
$this->assertTrue($mockVersionInfo->evaluateVersionCondition('PHP', '<5.3'));
$this->assertTrue($mockVersionInfo->evaluateVersionCondition('PHP', '>=5.2'));
$this->assertTrue($mockVersionInfo->evaluateVersionCondition('PHP', '>5.2'));
$this->assertTrue($mockVersionInfo->evaluateVersionCondition('PHP', '!=5.3'));
$this->assertFalse($mockVersionInfo->evaluateVersionCondition('PHP', '<=5.2'));
$this->assertFalse($mockVersionInfo->evaluateVersionCondition('PHP', '<5.2'));
$this->assertFalse($mockVersionInfo->evaluateVersionCondition('PHP', '>=7.0'));
$this->assertFalse($mockVersionInfo->evaluateVersionCondition('PHP', '>7.0'));
$this->assertTrue($mockVersionInfo->evaluateVersionCondition('PHP', '!=5.2'));
}
}

View File

@ -10,19 +10,30 @@
define('PMA_MINIMUM_COMMON', true);
require_once 'libraries/common.inc.php';
require_once 'libraries/Util.class.php';
require_once 'libraries/VersionInformation.php';
// Always send the correct headers
header('Content-type: application/json; charset=UTF-8');
$version = PMA_Util::getLatestVersion();
$versionInformation = new VersionInformation();
$versionDetails = $versionInformation->getLatestVersion();
if (empty($version)) {
if (empty($versionDetails)) {
echo json_encode(array());
} else {
$latestCompatible = $versionInformation->getLatestCompatibleVersion(
$versionDetails->releases
);
$version = '';
$date = '';
if ($latestCompatible != null) {
$version = $latestCompatible['version'];
$date = $latestCompatible['date'];
}
echo json_encode(
array(
'version' => (! empty($version->version) ? $version->version : ''),
'date' => (! empty($version->date) ? $version->date : ''),
'version' => (! empty($version) ? $version : ''),
'date' => (! empty($date) ? $date : ''),
)
);
}