summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/DefaultSettings.php8
-rw-r--r--includes/GlobalFunctions.php2
-rw-r--r--includes/Hooks.php39
-rw-r--r--includes/HttpFunctions.php17
-rw-r--r--includes/MediaWiki.php174
-rw-r--r--includes/Setup.php15
-rw-r--r--includes/User.php7
-rw-r--r--includes/cache/HTMLFileCache.php1
-rw-r--r--includes/debug/logger/LoggerFactory.php2
-rw-r--r--includes/libs/MultiHttpClient.php13
-rw-r--r--includes/specialpage/RedirectSpecialPage.php12
-rw-r--r--includes/specials/SpecialMyLanguage.php11
-rw-r--r--includes/specials/SpecialMyRedirectPages.php50
-rw-r--r--includes/utils/IP.php22
14 files changed, 268 insertions, 105 deletions
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index c13aa5f4..9267d3ad 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -75,7 +75,7 @@ $wgConfigRegistry = array(
* MediaWiki version number
* @since 1.2
*/
-$wgVersion = '1.25.3';
+$wgVersion = '1.25.4';
/**
* Name of the site. It must be changed in LocalSettings.php
@@ -4522,6 +4522,12 @@ $wgWhitelistReadRegexp = false;
$wgEmailConfirmToEdit = false;
/**
+ * Should MediaWiki attempt to protect user's privacy when doing redirects?
+ * Keep this true if access counts to articles are made public.
+ */
+$wgHideIdentifiableRedirects = true;
+
+/**
* Permission keys given to users in each group.
*
* This is an array where the keys are all groups and each value is an
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index ab3f019f..e717c126 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -3860,7 +3860,7 @@ function wfMemoryLimit() {
* Converts shorthand byte notation to integer form
*
* @param string $string
- * @param int $default Returned if $string is empty
+ * @param int $default Return if $string is empty
* @return int
*/
function wfShorthandToInteger( $string = '', $default = -1 ) {
diff --git a/includes/Hooks.php b/includes/Hooks.php
index dffc7bcf..199b4754 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -198,35 +198,18 @@ class Hooks {
// Profile first in case the Profiler causes errors
$funcPS = $profiler->scopedProfileIn( $func );
- set_error_handler( 'Hooks::hookErrorHandler' );
// mark hook as deprecated, if deprecation version is specified
if ( $deprecatedVersion !== null ) {
wfDeprecated( "$event hook (used in $func)", $deprecatedVersion );
}
- try {
- $retval = call_user_func_array( $callback, $hook_args );
- } catch ( MWHookException $e ) {
- $badhookmsg = $e->getMessage();
- } catch ( Exception $e ) {
- restore_error_handler();
- throw $e;
- }
-
- restore_error_handler();
- $profiler->scopedProfileOut( $funcPS );
+ $retval = call_user_func_array( $callback, $hook_args );
// Process the return value.
if ( is_string( $retval ) ) {
// String returned means error.
throw new FatalError( $retval );
- } elseif ( $badhookmsg !== null ) {
- // Exception was thrown from Hooks::hookErrorHandler.
- throw new MWException(
- 'Detected bug in an extension! ' .
- "Hook $func has invalid call signature; " . $badhookmsg
- );
} elseif ( $retval === false ) {
// False was returned. Stop processing, but no error.
return false;
@@ -235,24 +218,4 @@ class Hooks {
return true;
}
-
- /**
- * Handle PHP errors issued inside a hook. Catch errors that have to do with
- * a function expecting a reference, and let all others pass through.
- *
- * This REALLY should be protected... but it's public for compatibility
- *
- * @since 1.18
- *
- * @param int $errno Error number (unused)
- * @param string $errstr Error message
- * @throws MWHookException If the error has to do with the function signature
- * @return bool Always returns false
- */
- public static function hookErrorHandler( $errno, $errstr ) {
- if ( strpos( $errstr, 'expected to be a reference, value given' ) !== false ) {
- throw new MWHookException( $errstr, $errno );
- }
- return false;
- }
}
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index fa54487a..9dc2f0a4 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -779,7 +779,22 @@ class CurlHttpRequest extends MWHttpRequest {
$this->curlOptions[CURLOPT_HEADER] = true;
} elseif ( $this->method == 'POST' ) {
$this->curlOptions[CURLOPT_POST] = true;
- $this->curlOptions[CURLOPT_POSTFIELDS] = $this->postData;
+ $postData = $this->postData;
+ // Don't interpret POST parameters starting with '@' as file uploads, because this
+ // makes it impossible to POST plain values starting with '@' (and causes security
+ // issues potentially exposing the contents of local files).
+ // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6,
+ // but we support lower versions, and the option doesn't exist in HHVM 5.6.99.
+ if ( defined( 'CURLOPT_SAFE_UPLOAD' ) ) {
+ $this->curlOptions[CURLOPT_SAFE_UPLOAD] = true;
+ } else if ( is_array( $postData ) ) {
+ // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS
+ // is an array, but not if it's a string. So convert $req['body'] to a string
+ // for safety.
+ $postData = wfArrayToCgi( $postData );
+ }
+ $this->curlOptions[CURLOPT_POSTFIELDS] = $postData;
+
// Suppress 'Expect: 100-continue' header, as some servers
// will reject it with a 417 and Curl won't auto retry
// with HTTP 1.0 fallback
diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php
index ec2f40f6..39551441 100644
--- a/includes/MediaWiki.php
+++ b/includes/MediaWiki.php
@@ -37,6 +37,11 @@ class MediaWiki {
private $config;
/**
+ * @var String Cache what action this request is
+ */
+ private $action;
+
+ /**
* @param IContextSource|null $context
*/
public function __construct( IContextSource $context = null ) {
@@ -133,13 +138,11 @@ class MediaWiki {
* @return string Action
*/
public function getAction() {
- static $action = null;
-
- if ( $action === null ) {
- $action = Action::getActionName( $this->context );
+ if ( $this->action === null ) {
+ $this->action = Action::getActionName( $this->context );
}
- return $action;
+ return $this->action;
}
/**
@@ -221,63 +224,128 @@ class MediaWiki {
$this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
throw new BadTitleError();
}
- // Redirect loops, no title in URL, $wgUsePathInfo URLs, and URLs with a variant
- } elseif ( $request->getVal( 'action', 'view' ) == 'view' && !$request->wasPosted()
- && ( $request->getVal( 'title' ) === null
- || $title->getPrefixedDBkey() != $request->getVal( 'title' ) )
- && !count( $request->getValueNames( array( 'action', 'title' ) ) )
- && Hooks::run( 'TestCanonicalRedirect', array( $request, $title, $output ) )
- ) {
+ // Handle any other redirects.
+ // Redirect loops, titleless URL, $wgUsePathInfo URLs, and URLs with a variant
+ } elseif ( !$this->tryNormaliseRedirect( $title ) ) {
+ // Prevent information leak via Special:MyPage et al (T109724)
if ( $title->isSpecialPage() ) {
- list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
- if ( $name ) {
- $title = SpecialPage::getTitleFor( $name, $subpage );
+ $specialPage = SpecialPageFactory::getPage( $title->getDBKey() );
+ if ( $specialPage instanceof RedirectSpecialPage
+ && $this->config->get( 'HideIdentifiableRedirects' )
+ && $specialPage->personallyIdentifiableTarget()
+ ) {
+ list( , $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBKey() );
+ $target = $specialPage->getRedirect( $subpage );
+ // target can also be true. We let that case fall through to normal processing.
+ if ( $target instanceof Title ) {
+ $query = $specialPage->getRedirectQuery() ?: array();
+ $request = new DerivativeRequest( $this->context->getRequest(), $query );
+ $request->setRequestURL( $this->context->getRequest()->getRequestURL() );
+ $this->context->setRequest( $request );
+ // Do not varnish cache these. May vary even for anons
+ $this->context->getOutput()->lowerCdnMaxage( 0 );
+ $this->context->setTitle( $target );
+ $wgTitle = $target;
+ // Reset action type cache. (Special pages have only view)
+ $this->action = null;
+ $title = $target;
+ $output->addJsConfigVars( array(
+ 'wgInternalRedirectTargetUrl' => $target->getFullURL( $query ),
+ ) );
+ $output->addModules( 'mediawiki.action.view.redirect' );
+ }
}
}
- $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
- // Redirect to canonical url, make it a 301 to allow caching
- if ( $targetUrl == $request->getFullRequestURL() ) {
- $message = "Redirect loop detected!\n\n" .
- "This means the wiki got confused about what page was " .
- "requested; this sometimes happens when moving a wiki " .
- "to a new server or changing the server configuration.\n\n";
-
- if ( $this->config->get( 'UsePathInfo' ) ) {
- $message .= "The wiki is trying to interpret the page " .
- "title from the URL path portion (PATH_INFO), which " .
- "sometimes fails depending on the web server. Try " .
- "setting \"\$wgUsePathInfo = false;\" in your " .
- "LocalSettings.php, or check that \$wgArticlePath " .
- "is correct.";
+
+ // Special pages ($title may have changed since if statement above)
+ if ( NS_SPECIAL == $title->getNamespace() ) {
+ // Actions that need to be made when we have a special pages
+ SpecialPageFactory::executePath( $title, $this->context );
+ } else {
+ // ...otherwise treat it as an article view. The article
+ // may still be a wikipage redirect to another article or URL.
+ $article = $this->initializeArticle();
+ if ( is_object( $article ) ) {
+ $this->performAction( $article, $requestTitle );
+ } elseif ( is_string( $article ) ) {
+ $output->redirect( $article );
} else {
- $message .= "Your web server was detected as possibly not " .
- "supporting URL path components (PATH_INFO) correctly; " .
- "check your LocalSettings.php for a customized " .
- "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
- "to true.";
+ throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()"
+ . " returned neither an object nor a URL" );
}
- throw new HttpError( 500, $message );
- } else {
- $output->setSquidMaxage( 1200 );
- $output->redirect( $targetUrl, '301' );
}
- // Special pages
- } elseif ( NS_SPECIAL == $title->getNamespace() ) {
- // Actions that need to be made when we have a special pages
- SpecialPageFactory::executePath( $title, $this->context );
- } else {
- // ...otherwise treat it as an article view. The article
- // may be a redirect to another article or URL.
- $article = $this->initializeArticle();
- if ( is_object( $article ) ) {
- $this->performAction( $article, $requestTitle );
- } elseif ( is_string( $article ) ) {
- $output->redirect( $article );
+ }
+ }
+
+ /**
+ * Handle redirects for uncanonical title requests.
+ *
+ * Handles:
+ * - Redirect loops.
+ * - No title in URL.
+ * - $wgUsePathInfo URLs.
+ * - URLs with a variant.
+ * - Other non-standard URLs (as long as they have no extra query parameters).
+ *
+ * Behaviour:
+ * - Normalise title values:
+ * /wiki/Foo%20Bar -> /wiki/Foo_Bar
+ * - Normalise empty title:
+ * /wiki/ -> /wiki/Main
+ * /w/index.php?title= -> /wiki/Main
+ * - Don't redirect anything with query parameters other than 'title' or 'action=view'.
+ *
+ * @param Title $title
+ * @return bool True if a redirect was set.
+ * @throws HttpError
+ */
+ private function tryNormaliseRedirect( Title $title ) {
+ $request = $this->context->getRequest();
+ $output = $this->context->getOutput();
+
+ if ( $request->getVal( 'action', 'view' ) != 'view'
+ || $request->wasPosted()
+ || ( $request->getVal( 'title' ) !== null
+ && $title->getPrefixedDBkey() == $request->getVal( 'title' ) )
+ || count( $request->getValueNames( array( 'action', 'title' ) ) )
+ || !Hooks::run( 'TestCanonicalRedirect', array( $request, $title, $output ) )
+ ) {
+ return false;
+ }
+
+ if ( $title->isSpecialPage() ) {
+ list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
+ if ( $name ) {
+ $title = SpecialPage::getTitleFor( $name, $subpage );
+ }
+ }
+ // Redirect to canonical url, make it a 301 to allow caching
+ $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
+ if ( $targetUrl == $request->getFullRequestURL() ) {
+ $message = "Redirect loop detected!\n\n" .
+ "This means the wiki got confused about what page was " .
+ "requested; this sometimes happens when moving a wiki " .
+ "to a new server or changing the server configuration.\n\n";
+
+ if ( $this->config->get( 'UsePathInfo' ) ) {
+ $message .= "The wiki is trying to interpret the page " .
+ "title from the URL path portion (PATH_INFO), which " .
+ "sometimes fails depending on the web server. Try " .
+ "setting \"\$wgUsePathInfo = false;\" in your " .
+ "LocalSettings.php, or check that \$wgArticlePath " .
+ "is correct.";
} else {
- throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()"
- . " returned neither an object nor a URL" );
+ $message .= "Your web server was detected as possibly not " .
+ "supporting URL path components (PATH_INFO) correctly; " .
+ "check your LocalSettings.php for a customized " .
+ "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
+ "to true.";
}
+ throw new HttpError( 500, $message );
}
+ $output->setSquidMaxage( 1200 );
+ $output->redirect( $targetUrl, '301' );
+ return true;
}
/**
diff --git a/includes/Setup.php b/includes/Setup.php
index 1b6d66c0..35feeced 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -490,6 +490,21 @@ MWExceptionHandler::installHandler();
require_once "$IP/includes/libs/normal/UtfNormalUtil.php";
+
+$ps_validation = Profiler::instance()->scopedProfileIn( $fname . '-validation' );
+
+// T48998: Bail out early if $wgArticlePath is non-absolute
+if ( !preg_match( '/^(https?:\/\/|\/)/', $wgArticlePath ) ) {
+ throw new FatalError(
+ 'If you use a relative URL for $wgArticlePath, it must start ' .
+ 'with a slash (<code>/</code>).<br><br>See ' .
+ '<a href="https://www.mediawiki.org/wiki/Manual:$wgArticlePath">' .
+ 'https://www.mediawiki.org/wiki/Manual:$wgArticlePath</a>.'
+ );
+}
+
+Profiler::instance()->scopedProfileOut( $ps_validation );
+
$ps_default2 = Profiler::instance()->scopedProfileIn( $fname . '-defaults2' );
if ( $wgScriptExtension !== '.php' || defined( 'MW_ENTRY_PHP5' ) ) {
diff --git a/includes/User.php b/includes/User.php
index 663a80b7..62d72bdf 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -1051,11 +1051,10 @@ class User implements IDBAccessObject {
// stopping at a minimum of 10 chars.
$length = max( 10, $wgMinimalPasswordLength );
// Multiply by 1.25 to get the number of hex characters we need
- $length = $length * 1.25;
// Generate random hex chars
- $hex = MWCryptRand::generateHex( $length );
+ $hex = MWCryptRand::generateHex( ceil( $length * 1.25 ) );
// Convert from base 16 to base 32 to get a proper password like string
- return wfBaseConvert( $hex, 16, 32 );
+ return substr( wfBaseConvert( $hex, 16, 32, $length ), -$length );
}
/**
@@ -4109,7 +4108,7 @@ class User implements IDBAccessObject {
$salt, $request ?: $this->getRequest(), $timestamp
);
- if ( $val != $sessionToken ) {
+ if ( !hash_equals( $sessionToken, $val ) ) {
wfDebug( "User::matchEditToken: broken session data\n" );
}
diff --git a/includes/cache/HTMLFileCache.php b/includes/cache/HTMLFileCache.php
index c07032bf..483eaa57 100644
--- a/includes/cache/HTMLFileCache.php
+++ b/includes/cache/HTMLFileCache.php
@@ -48,6 +48,7 @@ class HTMLFileCache extends FileCacheBase {
* @throws MWException
*/
public function __construct( $title, $action ) {
+ parent::__construct();
$allowedTypes = self::cacheablePageActions();
if ( !in_array( $action, $allowedTypes ) ) {
throw new MWException( 'Invalid file cache type given.' );
diff --git a/includes/debug/logger/LoggerFactory.php b/includes/debug/logger/LoggerFactory.php
index b3078b9a..11d5ceaf 100644
--- a/includes/debug/logger/LoggerFactory.php
+++ b/includes/debug/logger/LoggerFactory.php
@@ -94,7 +94,7 @@ class LoggerFactory {
* @return \Psr\Log\LoggerInterface
*/
public static function getInstance( $channel ) {
- if ( !interface_exists( '\Psr\Log\LoggerInterface' ) ) {
+ if ( !interface_exists( 'Psr\Log\LoggerInterface' ) ) {
$message = (
'MediaWiki requires the <a href="https://github.com/php-fig/log">PSR-3 logging ' .
"library</a> to be present. This library is not embedded directly in MediaWiki's " .
diff --git a/includes/libs/MultiHttpClient.php b/includes/libs/MultiHttpClient.php
index fb2daa69..000d73b5 100644
--- a/includes/libs/MultiHttpClient.php
+++ b/includes/libs/MultiHttpClient.php
@@ -318,6 +318,19 @@ class MultiHttpClient {
);
} elseif ( $req['method'] === 'POST' ) {
curl_setopt( $ch, CURLOPT_POST, 1 );
+ // Don't interpret POST parameters starting with '@' as file uploads, because this
+ // makes it impossible to POST plain values starting with '@' (and causes security
+ // issues potentially exposing the contents of local files).
+ // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6,
+ // but we support lower versions, and the option doesn't exist in HHVM 5.6.99.
+ if ( defined( 'CURLOPT_SAFE_UPLOAD' ) ) {
+ curl_setopt( $ch, CURLOPT_SAFE_UPLOAD, true );
+ } else if ( is_array( $req['body'] ) ) {
+ // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS
+ // is an array, but not if it's a string. So convert $req['body'] to a string
+ // for safety.
+ $req['body'] = wfArrayToCgi( $req['body'] );
+ }
curl_setopt( $ch, CURLOPT_POSTFIELDS, $req['body'] );
} else {
if ( is_resource( $req['body'] ) || $req['body'] !== '' ) {
diff --git a/includes/specialpage/RedirectSpecialPage.php b/includes/specialpage/RedirectSpecialPage.php
index 2e6e55a7..a9bcf8c4 100644
--- a/includes/specialpage/RedirectSpecialPage.php
+++ b/includes/specialpage/RedirectSpecialPage.php
@@ -89,6 +89,18 @@ abstract class RedirectSpecialPage extends UnlistedSpecialPage {
? $params
: false;
}
+
+ /**
+ * Indicate if the target of this redirect can be used to identify
+ * a particular user of this wiki (e.g., if the redirect is to the
+ * user page of a User). See T109724.
+ *
+ * @since 1.27
+ * @return bool
+ */
+ public function personallyIdentifiableTarget() {
+ return false;
+ }
}
/**
diff --git a/includes/specials/SpecialMyLanguage.php b/includes/specials/SpecialMyLanguage.php
index 6cea1581..9d4f6167 100644
--- a/includes/specials/SpecialMyLanguage.php
+++ b/includes/specials/SpecialMyLanguage.php
@@ -95,4 +95,15 @@ class SpecialMyLanguage extends RedirectSpecialArticle {
return $base;
}
}
+
+ /**
+ * Target can identify a specific user's language preference.
+ *
+ * @see T109724
+ * @since 1.27
+ * @return bool
+ */
+ public function personallyIdentifiableTarget() {
+ return true;
+ }
}
diff --git a/includes/specials/SpecialMyRedirectPages.php b/includes/specials/SpecialMyRedirectPages.php
index 9b8d52bb..65ac864e 100644
--- a/includes/specials/SpecialMyRedirectPages.php
+++ b/includes/specials/SpecialMyRedirectPages.php
@@ -41,6 +41,16 @@ class SpecialMypage extends RedirectSpecialArticle {
return Title::makeTitle( NS_USER, $this->getUser()->getName() );
}
}
+
+ /**
+ * Target identifies a specific User. See T109724.
+ *
+ * @since 1.27
+ * @return bool
+ */
+ public function personallyIdentifiableTarget() {
+ return true;
+ }
}
/**
@@ -60,6 +70,16 @@ class SpecialMytalk extends RedirectSpecialArticle {
return Title::makeTitle( NS_USER_TALK, $this->getUser()->getName() );
}
}
+
+ /**
+ * Target identifies a specific User. See T109724.
+ *
+ * @since 1.27
+ * @return bool
+ */
+ public function personallyIdentifiableTarget() {
+ return true;
+ }
}
/**
@@ -77,6 +97,16 @@ class SpecialMycontributions extends RedirectSpecialPage {
function getRedirect( $subpage ) {
return SpecialPage::getTitleFor( 'Contributions', $this->getUser()->getName() );
}
+
+ /**
+ * Target identifies a specific User. See T109724.
+ *
+ * @since 1.27
+ * @return bool
+ */
+ public function personallyIdentifiableTarget() {
+ return true;
+ }
}
/**
@@ -93,6 +123,16 @@ class SpecialMyuploads extends RedirectSpecialPage {
function getRedirect( $subpage ) {
return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() );
}
+
+ /**
+ * Target identifies a specific User. See T109724.
+ *
+ * @since 1.27
+ * @return bool
+ */
+ public function personallyIdentifiableTarget() {
+ return true;
+ }
}
/**
@@ -111,4 +151,14 @@ class SpecialAllMyUploads extends RedirectSpecialPage {
return SpecialPage::getTitleFor( 'Listfiles', $this->getUser()->getName() );
}
+
+ /**
+ * Target identifies a specific User. See T109724.
+ *
+ * @since 1.27
+ * @return bool
+ */
+ public function personallyIdentifiableTarget() {
+ return true;
+ }
}
diff --git a/includes/utils/IP.php b/includes/utils/IP.php
index 4441236d..b14a3843 100644
--- a/includes/utils/IP.php
+++ b/includes/utils/IP.php
@@ -130,8 +130,9 @@ class IP {
/**
* Convert an IP into a verbose, uppercase, normalized form.
- * IPv6 addresses in octet notation are expanded to 8 words.
- * IPv4 addresses are just trimmed.
+ * Both IPv4 and IPv6 addresses are trimmed. Additionally,
+ * IPv6 addresses in octet notation are expanded to 8 words;
+ * IPv4 addresses have leading zeros, in each octet, removed.
*
* @param string $ip IP address in quad or octet form (CIDR or not).
* @return string
@@ -141,8 +142,16 @@ class IP {
if ( $ip === '' ) {
return null;
}
- if ( self::isIPv4( $ip ) || !self::isIPv6( $ip ) ) {
- return $ip; // nothing else to do for IPv4 addresses or invalid ones
+ /* If not an IP, just return trimmed value, since sanitizeIP() is called
+ * in a number of contexts where usernames are supplied as input.
+ */
+ if ( !self::isIPAddress($ip) ) {
+ return $ip;
+ }
+ if ( self::isIPv4( $ip ) ) {
+ // Remove leading 0's from octet representation of IPv4 address
+ $ip = preg_replace( '/(?:^|(?<=\.))0+(?=[1-9]|0\.|0$)/', '', $ip );
+ return $ip;
}
// Remove any whitespaces, convert to upper case
$ip = strtoupper( $ip );
@@ -395,8 +404,9 @@ class IP {
if ( self::isIPv6( $ip ) ) {
$n = 'v6-' . self::IPv6ToRawHex( $ip );
} elseif ( self::isIPv4( $ip ) ) {
- // Bug 60035: an IP with leading 0's fails in ip2long sometimes (e.g. *.08)
- $ip = preg_replace( '/(?<=\.)0+(?=[1-9])/', '', $ip );
+ // T62035/T97897: An IP with leading 0's fails in ip2long sometimes (e.g. *.08),
+ // also double/triple 0 needs to be changed to just a single 0 for ip2long.
+ $ip = self::sanitizeIP( $ip );
$n = ip2long( $ip );
if ( $n < 0 ) {
$n += pow( 2, 32 );