Followup r95753 per CR: prevent extensions from making isMovable() return true for...
[lhc/web/wiklou.git] / includes / GlobalFunctions.php
index 8aa13e7..017b842 100644 (file)
@@ -294,6 +294,11 @@ function wfRandom() {
 */
 function wfUrlencode( $s ) {
        static $needle;
+       if ( is_null( $s ) ) {
+               $needle = null;
+               return;
+       }
+
        if ( is_null( $needle ) ) {
                $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' );
                if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) || ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false ) ) {
@@ -427,20 +432,49 @@ function wfAppendQuery( $url, $query ) {
  * Expand a potentially local URL to a fully-qualified URL.  Assumes $wgServer
  * is correct.
  *
+ * The meaning of the PROTO_* constants is as follows:
+ * PROTO_HTTP: Output a URL starting with http://
+ * PROTO_HTTPS: Output a URL starting with https://
+ * PROTO_RELATIVE: Output a URL starting with // (protocol-relative URL)
+ * PROTO_CURRENT: Output a URL starting with either http:// or https:// , depending on which protocol was used for the current incoming request
+ * PROTO_CANONICAL: For URLs without a domain, like /w/index.php , use $wgCanonicalServer. For protocol-relative URLs, use the protocol of $wgCanonicalServer
+ *
  * @todo this won't work with current-path-relative URLs
  * like "subdir/foo.html", etc.
  *
  * @param $url String: either fully-qualified or a local path + query
+ * @param $defaultProto Mixed: one of the PROTO_* constants. Determines the protocol to use if $url or $wgServer is protocol-relative
  * @return string Fully-qualified URL
  */
-function wfExpandUrl( $url ) {
-       global $wgServer;
+function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
+       global $wgServer, $wgCanonicalServer;
+       $serverUrl = $defaultProto === PROTO_CANONICAL ? $wgCanonicalServer : $wgServer;
+
+       if ( $defaultProto === PROTO_CURRENT ) {
+               $defaultProto = WebRequest::detectProtocol() . '://';
+       }
+
+       // Analyze $serverUrl to obtain its protocol
+       $bits = wfParseUrl( $serverUrl );
+       $serverHasProto = $bits && $bits['scheme'] != '';
+
+       if ( $defaultProto === PROTO_CANONICAL ) {
+               if ( $serverHasProto ) {
+                       $defaultProto = $bits['scheme'] . '://';
+               } else {
+                       // $wgCanonicalServer doesn't have a protocol. This really isn't supposed to happen
+                       // Fall back to HTTP in this ridiculous case
+                       $defaultProto = PROTO_HTTP;
+               }
+       }
+
+       $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
+
        if( substr( $url, 0, 2 ) == '//' ) {
-               $bits = wfParseUrl( $wgServer );
-               $scheme = $bits && $bits['scheme'] !== '' ? $bits['scheme'] : 'http';
-               return $scheme . ':' . $url;
+               return $defaultProtoWithoutSlashes . $url;
        } elseif( substr( $url, 0, 1 ) == '/' ) {
-               return $wgServer . $url;
+               // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, otherwise leave it alone
+               return ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
        } else {
                return $url;
        }
@@ -449,14 +483,18 @@ function wfExpandUrl( $url ) {
 /**
  * Returns a regular expression of url protocols
  *
+ * @param $includeProtocolRelative bool If false, remove '//' from the returned protocol list.
+ *        DO NOT USE this directy, use wfUrlProtocolsWithoutProtRel() instead
  * @return String
  */
-function wfUrlProtocols() {
+function wfUrlProtocols( $includeProtocolRelative = true ) {
        global $wgUrlProtocols;
 
-       static $retval = null;
-       if ( !is_null( $retval ) ) {
-               return $retval;
+       // Cache return values separately based on $includeProtocolRelative
+       static $withProtRel = null, $withoutProtRel = null;
+       $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
+       if ( !is_null( $cachedValue ) ) {
+               return $cachedValue;
        }
 
        // Support old-style $wgUrlProtocols strings, for backwards compatibility
@@ -464,16 +502,39 @@ function wfUrlProtocols() {
        if ( is_array( $wgUrlProtocols ) ) {
                $protocols = array();
                foreach ( $wgUrlProtocols as $protocol ) {
-                       $protocols[] = preg_quote( $protocol, '/' );
+                       // Filter out '//' if !$includeProtocolRelative
+                       if ( $includeProtocolRelative || $protocol !== '//' ) {
+                               $protocols[] = preg_quote( $protocol, '/' );
+                       }
                }
 
                $retval = implode( '|', $protocols );
        } else {
+               // Ignore $includeProtocolRelative in this case
+               // This case exists for pre-1.6 compatibility, and we can safely assume
+               // that '//' won't appear in a pre-1.6 config because protocol-relative
+               // URLs weren't supported until 1.18
                $retval = $wgUrlProtocols;
        }
+
+       // Cache return value
+       if ( $includeProtocolRelative ) {
+               $withProtRel = $retval;
+       } else {
+               $withoutProtRel = $retval;
+       }
        return $retval;
 }
 
+/**
+ * Like wfUrlProtocols(), but excludes '//' from the protocol list. Use this if
+ * you need a regex that matches all URL protocols but does not match protocol-
+ * relative URLs
+ */
+function wfUrlProtocolsWithoutProtRel() {
+       return wfUrlProtocols( false );
+}
+
 /**
  * parse_url() work-alike, but non-broken.  Differences:
  *
@@ -486,7 +547,7 @@ function wfUrlProtocols() {
  */
 function wfParseUrl( $url ) {
        global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
-       
+
        // Protocol-relative URLs are handled really badly by parse_url(). It's so bad that the easiest
        // way to handle them is to just prepend 'http:' and strip the protocol out later
        $wasRelative = substr( $url, 0, 2 ) == '//';
@@ -496,7 +557,9 @@ function wfParseUrl( $url ) {
        wfSuppressWarnings();
        $bits = parse_url( $url );
        wfRestoreWarnings();
-       if ( !$bits ) {
+       // parse_url() returns an array without scheme for some invalid URLs, e.g.
+       // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
+       if ( !$bits || !isset( $bits['scheme'] ) ) {
                return false;
        }
 
@@ -524,7 +587,7 @@ function wfParseUrl( $url ) {
                        $bits['path'] = '/' . $bits['path'];
                }
        }
-       
+
        // If the URL was protocol-relative, fix scheme and delimiter
        if ( $wasRelative ) {
                $bits['scheme'] = '';
@@ -582,6 +645,26 @@ function wfMakeUrlIndex( $url ) {
        return $index;
 }
 
+/**
+ * Check whether a given URL has a domain that occurs in a given set of domains
+ * @param $url string URL
+ * @param $domains array Array of domains (strings)
+ * @return bool True if the host part of $url ends in one of the strings in $domains
+ */
+function wfMatchesDomainList( $url, $domains ) {
+       $bits = wfParseUrl( $url );
+       if ( is_array( $bits ) && isset( $bits['host'] ) ) {
+               foreach ( (array)$domains as $domain ) {
+                       // FIXME: This gives false positives. http://nds-nl.wikipedia.org will match nl.wikipedia.org
+                       // We should use something that interprets dots instead
+                       if ( substr( $bits['host'], -strlen( $domain ) ) === $domain ) {
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
 /**
  * Sends a line to the debug log if enabled or, optionally, to a comment in output.
  * In normal operation this is a NOP.
@@ -926,7 +1009,7 @@ function wfGetLangObj( $langcode = false ) {
  * Old function when $wgBetterDirectionality existed
  * Removed in core, kept in extensions for backwards compat.
  *
- * @deprecated since 1.19
+ * @deprecated since 1.18
  * @return Language
  */
 function wfUILang() {
@@ -1156,7 +1239,8 @@ function wfMsgWikiHtml( $key ) {
        $args = func_get_args();
        array_shift( $args );
        return wfMsgReplaceArgs(
-               MessageCache::singleton()->parse( wfMsgGetKey( $key ), null, /* can't be set to false */ true )->getText(),
+               MessageCache::singleton()->parse( wfMsgGetKey( $key ), null,
+               /* can't be set to false */ true, /* interface */ true )->getText(),
                $args );
 }
 
@@ -1323,6 +1407,8 @@ function wfReportTime() {
  * debug_backtrace is disabled, otherwise the output from
  * debug_backtrace() (trimmed).
  *
+ * @param $limit int This parameter can be used to limit the number of stack frames returned
+ *
  * @return array of backtrace information
  */
 function wfDebugBacktrace( $limit = 0 ) {
@@ -1348,7 +1434,7 @@ function wfDebugBacktrace( $limit = 0 ) {
        }
 
        if ( $limit && version_compare( PHP_VERSION, '5.4.0', '>=' ) ) {
-               return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, 1 ), 1 );
+               return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit ), 1 );
        } else {
                return array_slice( debug_backtrace(), 1 );
        }
@@ -2948,7 +3034,7 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = t
  *
  * @param $name String
  * @param $p Array: parameters
- * @deprecated since 1.18, warnings in 1.19, removal in 1.20
+ * @deprecated since 1.18, warnings in 1.18, removal in 1.20
  */
 function wfCreateObject( $name, $p ) {
        wfDeprecated( __FUNCTION__ );
@@ -3173,6 +3259,14 @@ function wfLocalFile( $title ) {
        return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
 }
 
+/**
+ * Stream a file to the browser. Back-compat alias for StreamFile::stream()
+ * @deprecated since 1.19
+ */
+function wfStreamFile( $fname, $headers = array() ) {
+       StreamFile::stream( $fname, $headers );
+}
+
 /**
  * Should low-performance queries be disabled?
  *
@@ -3331,7 +3425,7 @@ function wfWaitForSlaves( $maxLag = false, $wiki = false ) {
 
 /**
  * Used to be used for outputting text in the installer/updater
- * @deprecated since 1.18, warnings in 1.19, remove in 1.20
+ * @deprecated since 1.18, warnings in 1.18, remove in 1.20
  */
 function wfOut( $s ) {
        wfDeprecated( __METHOD__ );