* Add preprocessor tests that Bergi supplied for Bug #28642
[lhc/web/wiklou.git] / includes / WebRequest.php
index bf1155f..41077d3 100644 (file)
@@ -55,7 +55,22 @@ class WebRequest {
                $this->data = $_POST + $_GET;
        }
 
+       /**
+        * Extract the PATH_INFO variable even when it isn't a reasonable
+        * value. On some large webhosts, PATH_INFO includes the script
+        * path as well as everything after it.
+        *
+        * @param $want string: If this is not 'all', then the function
+        * will return an empty array if it determines that the URL is
+        * inside a rewrite path.
+        *
+        * @return Array: 'title' key is the title of the article.
+        */
        static public function getPathInfo( $want = 'all' ) {
+               // PATH_INFO is mangled due to http://bugs.php.net/bug.php?id=31892
+               // And also by Apache 2.x, double slashes are converted to single slashes.
+               // So we will use REQUEST_URI if possible.
+               $matches = array();
                if ( !empty( $_SERVER['REQUEST_URI'] ) ) {
                        // Slurp out the path portion to examine...
                        $url = $_SERVER['REQUEST_URI'];
@@ -70,7 +85,7 @@ class WebRequest {
                                if( $path == $wgScript && $want !== 'all' ) {
                                        // Script inside a rewrite path?
                                        // Abort to keep from breaking...
-                                       return;
+                                       return $matches;
                                }
                                // Raw PATH_INFO style
                                $matches = self::extractTitle( $path, "$wgScript/$1" );
@@ -124,9 +139,11 @@ class WebRequest {
                        return;
                }
 
-               $matches = self::getPathInfo( 'title' );
-               foreach( $matches as $key => $val) {
-                       $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
+               if ( $wgUsePathInfo ) {
+                       $matches = self::getPathInfo( 'title' );
+                       foreach( $matches as $key => $val) {
+                               $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
+                       }
                }
        }
 
@@ -425,6 +442,16 @@ class WebRequest {
                return $retVal;
        }
 
+       /**
+        * Get the values passed in the query string.
+        * No transformation is performed on the values.
+        *
+        * @return Array
+        */
+        public function getQueryValues() {
+               return $_GET;
+        }
+
        /**
         * Returns true if the present request was reached by a POST operation,
         * false otherwise (GET, HEAD, or command-line).
@@ -475,10 +502,12 @@ class WebRequest {
         * @return String
         */
        public function getRequestURL() {
-               if( isset( $_SERVER['REQUEST_URI']) && strlen($_SERVER['REQUEST_URI']) ) {
+               if( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
                        $base = $_SERVER['REQUEST_URI'];
-               } elseif( isset( $_SERVER['SCRIPT_NAME'] ) ) {
+               } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) {
                        // Probably IIS; doesn't set REQUEST_URI
+                       $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
+               } elseif( isset( $_SERVER['SCRIPT_NAME'] ) ) {
                        $base = $_SERVER['SCRIPT_NAME'];
                        if( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
                                $base .= '?' . $_SERVER['QUERY_STRING'];
@@ -486,8 +515,8 @@ class WebRequest {
                } else {
                        // This shouldn't happen!
                        throw new MWException( "Web server doesn't provide either " .
-                               "REQUEST_URI or SCRIPT_NAME. Report details of your " .
-                               "web server configuration to http://bugzilla.wikimedia.org/" );
+                               "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
+                               "of your web server configuration to http://bugzilla.wikimedia.org/" );
                }
                // User-agents should not send a fragment with the URI, but
                // if they do, and the web server passes it on to us, we
@@ -497,7 +526,7 @@ class WebRequest {
                if( $hash !== false ) {
                        $base = substr( $base, 0, $hash );
                }
-               if( $base{0} == '/' ) {
+               if( $base[0] == '/' ) {
                        return $base;
                } else {
                        // We may get paths with a host prepended; strip it.
@@ -522,25 +551,7 @@ class WebRequest {
         * @return String
         */
        public function appendQuery( $query ) {
-               global $wgTitle;
-               $basequery = '';
-               foreach( $_GET as $var => $val ) {
-                       if ( $var == 'title' ) {
-                               continue;
-                       }
-                       if ( is_array( $val ) ) {
-                               /* This will happen given a request like
-                                * http://en.wikipedia.org/w/index.php?title[]=Special:Userlogin&returnto[]=Main_Page
-                                */
-                               continue;
-                       }
-                       $basequery .= '&' . urlencode( $var ) . '=' . urlencode( $val );
-               }
-               $basequery .= '&' . $query;
-
-               # Trim the extra &
-               $basequery = substr( $basequery, 1 );
-               return $wgTitle->getLocalURL( $basequery );
+               return $this->appendQueryArray( wfCgiToArray( $query ) );
        }
 
        /**
@@ -567,7 +578,7 @@ class WebRequest {
         */
        public function appendQueryArray( $array, $onlyquery = false ) {
                global $wgTitle;
-               $newquery = $_GET;
+               $newquery = $this->getQueryValues();
                unset( $newquery['title'] );
                $newquery = array_merge( $newquery, $array );
                $query = wfArrayToCGI( $newquery );
@@ -683,33 +694,52 @@ class WebRequest {
                return $this->response;
        }
 
+       /**
+        * Initialise the header list
+        */
+       private function initHeaders() {
+               if ( count( $this->headers ) ) {
+                       return;
+               }
+
+               if ( function_exists( 'apache_request_headers' ) ) {
+                       foreach ( apache_request_headers() as $tempName => $tempValue ) {
+                               $this->headers[ strtoupper( $tempName ) ] = $tempValue;
+                       }
+               } else {
+                       $headers = $_SERVER;
+                       foreach ( $_SERVER as $name => $value ) {
+                               if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
+                                       $name = str_replace( '_', '-',  substr( $name, 5 ) );
+                                       $this->headers[$name] = $value;
+                               } elseif ( $name === 'CONTENT_LENGTH' ) {
+                                       $this->headers['CONTENT-LENGTH'] = $value;
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Get an array containing all request headers
+        *
+        * @return Array mapping header name to its value
+        */
+       public function getAllHeaders() {
+               $this->initHeaders();
+               return $this->headers;
+       }
+
        /**
         * Get a request header, or false if it isn't set
         * @param $name String: case-insensitive header name
         */
        public function getHeader( $name ) {
+               $this->initHeaders();
                $name = strtoupper( $name );
-               if ( function_exists( 'apache_request_headers' ) ) {
-                       if ( !$this->headers ) {
-                               foreach ( apache_request_headers() as $tempName => $tempValue ) {
-                                       $this->headers[ strtoupper( $tempName ) ] = $tempValue;
-                               }
-                       }
-                       if ( isset( $this->headers[$name] ) ) {
-                               return $this->headers[$name];
-                       } else {
-                               return false;
-                       }
+               if ( isset( $this->headers[$name] ) ) {
+                       return $this->headers[$name];
                } else {
-                       $name = 'HTTP_' . str_replace( '-', '_', $name );
-                       if ( $name === 'HTTP_CONTENT_LENGTH' && !isset( $_SERVER[$name] ) ) {
-                               $name = 'CONTENT_LENGTH';
-                       }
-                       if ( isset( $_SERVER[$name] ) ) {
-                               return $_SERVER[$name];
-                       } else {
-                               return false;
-                       }
+                       return false;
                }
        }
 
@@ -747,10 +777,27 @@ class WebRequest {
         * but only by prefixing it with the script name and maybe some other stuff,
         * the extension is not mangled. So this should be a reasonably portable
         * way to perform this security check.
+        *
+        * Also checks for anything that looks like a file extension at the end of
+        * QUERY_STRING, since IE 6 and earlier will use this to get the file type
+        * if there was no dot before the question mark (bug 28235).
         */
        public function isPathInfoBad() {
                global $wgScriptExtension;
 
+               if ( isset( $_SERVER['QUERY_STRING'] ) 
+                       && preg_match( '/\.[a-z0-9]{1,4}(#|\?|$)/i', $_SERVER['QUERY_STRING'] ) )
+               {
+                       // Bug 28235
+                       // Block only Internet Explorer, and requests with missing UA 
+                       // headers that could be IE users behind a privacy proxy.
+                       if ( !isset( $_SERVER['HTTP_USER_AGENT'] ) 
+                               || preg_match( '/; *MSIE/', $_SERVER['HTTP_USER_AGENT'] ) )
+                       {
+                               return true;
+                       }
+               }
+
                if ( !isset( $_SERVER['PATH_INFO'] ) ) {
                        return false;
                }
@@ -958,6 +1005,14 @@ class FauxRequest extends WebRequest {
                return $this->data;
        }
 
+       public function getQueryValues() {
+               if ( $this->wasPosted ) {
+                       return array();
+               } else {
+                       return $this->data;
+               }
+       }
+
        public function wasPosted() {
                return $this->wasPosted;
        }
@@ -970,28 +1025,6 @@ class FauxRequest extends WebRequest {
                $this->notImplemented( __METHOD__ );
        }
 
-       public function appendQuery( $query ) {
-               global $wgTitle;
-               $basequery = '';
-               foreach( $this->data as $var => $val ) {
-                       if ( $var == 'title' ) {
-                               continue;
-                       }
-                       if ( is_array( $val ) ) {
-                               /* This will happen given a request like
-                                * http://en.wikipedia.org/w/index.php?title[]=Special:Userlogin&returnto[]=Main_Page
-                                */
-                               continue;
-                       }
-                       $basequery .= '&' . urlencode( $var ) . '=' . urlencode( $val );
-               }
-               $basequery .= '&' . $query;
-
-               # Trim the extra &
-               $basequery = substr( $basequery, 1 );
-               return $wgTitle->getLocalURL( $basequery );
-       }
-
        public function getHeader( $name ) {
                return isset( $this->headers[$name] ) ? $this->headers[$name] : false;
        }
@@ -1009,6 +1042,10 @@ class FauxRequest extends WebRequest {
                $this->session[$key] = $data;
        }
 
+       public function getSessionArray() {
+               return $this->session;
+       }
+
        public function isPathInfoBad() {
                return false;
        }