00001 <?php
00006 # Copyright (C) 2003 Brion Vibber <brion@pobox.com>
00007 # http://www.mediawiki.org/
00008 #
00009 # This program is free software; you can redistribute it and/or modify
00010 # it under the terms of the GNU General Public License as published by
00011 # the Free Software Foundation; either version 2 of the License, or
00012 # (at your option) any later version.
00013 #
00014 # This program is distributed in the hope that it will be useful,
00015 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017 # GNU General Public License for more details.
00018 #
00019 # You should have received a copy of the GNU General Public License along
00020 # with this program; if not, write to the Free Software Foundation, Inc.,
00021 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00022 # http://www.gnu.org/copyleft/gpl.html
00023
00024
00029 if ( !function_exists( '__autoload' ) ) {
00030 require_once( dirname(__FILE__) . '/normal/UtfNormal.php' );
00031 }
00032
00045 class WebRequest {
00046 protected $data, $headers = array();
00047 private $_response;
00048
00049 public function __construct() {
00053 $this->checkMagicQuotes();
00054
00055
00056
00057 $this->data = $_POST + $_GET;
00058 }
00059
00067 public function interpolateTitle() {
00068 global $wgUsePathInfo;
00069
00070 if ( $wgUsePathInfo ) {
00071
00072
00073
00074 $matches = array();
00075
00076 if ( !empty( $_SERVER['REQUEST_URI'] ) ) {
00077
00078 $url = $_SERVER['REQUEST_URI'];
00079 if ( !preg_match( '!^https?://!', $url ) ) {
00080 $url = 'http://unused' . $url;
00081 }
00082 $a = parse_url( $url );
00083 if( $a ) {
00084 $path = isset( $a['path'] ) ? $a['path'] : '';
00085
00086 global $wgScript;
00087 if( $path == $wgScript ) {
00088
00089
00090 return;
00091 }
00092
00093 $matches = $this->extractTitle( $path, "$wgScript/$1" );
00094
00095 global $wgArticlePath;
00096 if( !$matches && $wgArticlePath ) {
00097 $matches = $this->extractTitle( $path, $wgArticlePath );
00098 }
00099
00100 global $wgActionPaths;
00101 if( !$matches && $wgActionPaths ) {
00102 $matches = $this->extractTitle( $path, $wgActionPaths, 'action' );
00103 }
00104
00105 global $wgVariantArticlePath, $wgContLang;
00106 if( !$matches && $wgVariantArticlePath ) {
00107 $variantPaths = array();
00108 foreach( $wgContLang->getVariants() as $variant ) {
00109 $variantPaths[$variant] =
00110 str_replace( '$2', $variant, $wgVariantArticlePath );
00111 }
00112 $matches = $this->extractTitle( $path, $variantPaths, 'variant' );
00113 }
00114 }
00115 } elseif ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) {
00116
00117
00118
00119 $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
00120
00121 } elseif ( isset( $_SERVER['PATH_INFO'] ) && ($_SERVER['PATH_INFO'] != '') ) {
00122
00123 $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
00124 }
00125 foreach( $matches as $key => $val) {
00126 $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
00127 }
00128 }
00129 }
00130
00141 private function extractTitle( $path, $bases, $key=false ) {
00142 foreach( (array)$bases as $keyValue => $base ) {
00143
00144 $base = str_replace( '$1', '', $base );
00145 $baseLen = strlen( $base );
00146 if( substr( $path, 0, $baseLen ) == $base ) {
00147 $raw = substr( $path, $baseLen );
00148 if( $raw !== '' ) {
00149 $matches = array( 'title' => rawurldecode( $raw ) );
00150 if( $key ) {
00151 $matches[$key] = $keyValue;
00152 }
00153 return $matches;
00154 }
00155 }
00156 }
00157 return array();
00158 }
00159
00166 private function &fix_magic_quotes( &$arr ) {
00167 foreach( $arr as $key => $val ) {
00168 if( is_array( $val ) ) {
00169 $this->fix_magic_quotes( $arr[$key] );
00170 } else {
00171 $arr[$key] = stripslashes( $val );
00172 }
00173 }
00174 return $arr;
00175 }
00176
00183 private function checkMagicQuotes() {
00184 $mustFixQuotes = function_exists( 'get_magic_quotes_gpc' )
00185 && get_magic_quotes_gpc();
00186 if( $mustFixQuotes ) {
00187 $this->fix_magic_quotes( $_COOKIE );
00188 $this->fix_magic_quotes( $_ENV );
00189 $this->fix_magic_quotes( $_GET );
00190 $this->fix_magic_quotes( $_POST );
00191 $this->fix_magic_quotes( $_REQUEST );
00192 $this->fix_magic_quotes( $_SERVER );
00193 }
00194 }
00195
00202 function normalizeUnicode( $data ) {
00203 if( is_array( $data ) ) {
00204 foreach( $data as $key => $val ) {
00205 $data[$key] = $this->normalizeUnicode( $val );
00206 }
00207 } else {
00208 global $wgContLang;
00209 $data = $wgContLang->normalize( $data );
00210 }
00211 return $data;
00212 }
00213
00222 private function getGPCVal( $arr, $name, $default ) {
00223 # PHP is so nice to not touch input data, except sometimes:
00224 # http://us2.php.net/variables.external#language.variables.external.dot-in-names
00225 # Work around PHP *feature* to avoid *bugs* elsewhere.
00226 $name = strtr( $name, '.', '_' );
00227 if( isset( $arr[$name] ) ) {
00228 global $wgContLang;
00229 $data = $arr[$name];
00230 if( isset( $_GET[$name] ) && !is_array( $data ) ) {
00231 # Check for alternate/legacy character encoding.
00232 if( isset( $wgContLang ) ) {
00233 $data = $wgContLang->checkTitleEncoding( $data );
00234 }
00235 }
00236 $data = $this->normalizeUnicode( $data );
00237 return $data;
00238 } else {
00239 taint( $default );
00240 return $default;
00241 }
00242 }
00243
00254 public function getVal( $name, $default = null ) {
00255 $val = $this->getGPCVal( $this->data, $name, $default );
00256 if( is_array( $val ) ) {
00257 $val = $default;
00258 }
00259 if( is_null( $val ) ) {
00260 return $val;
00261 } else {
00262 return (string)$val;
00263 }
00264 }
00265
00272 public function setVal( $key, $value ) {
00273 $ret = isset( $this->data[$key] ) ? $this->data[$key] : null;
00274 $this->data[$key] = $value;
00275 return $ret;
00276 }
00277
00287 public function getArray( $name, $default = null ) {
00288 $val = $this->getGPCVal( $this->data, $name, $default );
00289 if( is_null( $val ) ) {
00290 return null;
00291 } else {
00292 return (array)$val;
00293 }
00294 }
00295
00306 public function getIntArray( $name, $default = null ) {
00307 $val = $this->getArray( $name, $default );
00308 if( is_array( $val ) ) {
00309 $val = array_map( 'intval', $val );
00310 }
00311 return $val;
00312 }
00313
00322 public function getInt( $name, $default = 0 ) {
00323 return intval( $this->getVal( $name, $default ) );
00324 }
00325
00333 public function getIntOrNull( $name ) {
00334 $val = $this->getVal( $name );
00335 return is_numeric( $val )
00336 ? intval( $val )
00337 : null;
00338 }
00339
00348 public function getBool( $name, $default = false ) {
00349 return $this->getVal( $name, $default ) ? true : false;
00350 }
00351
00359 public function getCheck( $name ) {
00360 # Checkboxes and buttons are only present when clicked
00361 # Presence connotes truth, abscense false
00362 $val = $this->getVal( $name, null );
00363 return isset( $val );
00364 }
00365
00378 public function getText( $name, $default = '' ) {
00379 global $wgContLang;
00380 $val = $this->getVal( $name, $default );
00381 return str_replace( "\r\n", "\n",
00382 $wgContLang->recodeInput( $val ) );
00383 }
00384
00390 public function getValues() {
00391 $names = func_get_args();
00392 if ( count( $names ) == 0 ) {
00393 $names = array_keys( $this->data );
00394 }
00395
00396 $retVal = array();
00397 foreach ( $names as $name ) {
00398 $value = $this->getVal( $name );
00399 if ( !is_null( $value ) ) {
00400 $retVal[$name] = $value;
00401 }
00402 }
00403 return $retVal;
00404 }
00405
00415 public function wasPosted() {
00416 return $_SERVER['REQUEST_METHOD'] == 'POST';
00417 }
00418
00430 public function checkSessionCookie() {
00431 return isset( $_COOKIE[session_name()] );
00432 }
00433
00438 public function getRequestURL() {
00439 if( isset( $_SERVER['REQUEST_URI']) && strlen($_SERVER['REQUEST_URI']) ) {
00440 $base = $_SERVER['REQUEST_URI'];
00441 } elseif( isset( $_SERVER['SCRIPT_NAME'] ) ) {
00442
00443 $base = $_SERVER['SCRIPT_NAME'];
00444 if( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
00445 $base .= '?' . $_SERVER['QUERY_STRING'];
00446 }
00447 } else {
00448
00449 throw new MWException( "Web server doesn't provide either " .
00450 "REQUEST_URI or SCRIPT_NAME. Report details of your " .
00451 "web server configuration to http://bugzilla.wikimedia.org/" );
00452 }
00453
00454
00455
00456
00457 $hash = strpos( $base, '#' );
00458 if( $hash !== false ) {
00459 $base = substr( $base, 0, $hash );
00460 }
00461 if( $base{0} == '/' ) {
00462 return $base;
00463 } else {
00464
00465 return preg_replace( '!^[^:]+://[^/]+/!', '/', $base );
00466 }
00467 }
00468
00473 public function getFullRequestURL() {
00474 global $wgServer;
00475 return $wgServer . $this->getRequestURL();
00476 }
00477
00483 public function appendQuery( $query ) {
00484 global $wgTitle;
00485 $basequery = '';
00486 foreach( $_GET as $var => $val ) {
00487 if ( $var == 'title' )
00488 continue;
00489 if ( is_array( $val ) )
00490
00491
00492
00493 continue;
00494 $basequery .= '&' . urlencode( $var ) . '=' . urlencode( $val );
00495 }
00496 $basequery .= '&' . $query;
00497
00498 # Trim the extra &
00499 $basequery = substr( $basequery, 1 );
00500 return $wgTitle->getLocalURL( $basequery );
00501 }
00502
00508 public function escapeAppendQuery( $query ) {
00509 return htmlspecialchars( $this->appendQuery( $query ) );
00510 }
00511
00512 public function appendQueryValue( $key, $value, $onlyquery = false ) {
00513 return $this->appendQueryArray( array( $key => $value ), $onlyquery );
00514 }
00515
00523 public function appendQueryArray( $array, $onlyquery = false ) {
00524 global $wgTitle;
00525 $newquery = $_GET;
00526 unset( $newquery['title'] );
00527 $newquery = array_merge( $newquery, $array );
00528 $query = wfArrayToCGI( $newquery );
00529 return $onlyquery ? $query : $wgTitle->getLocalURL( $query );
00530 }
00531
00541 public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
00542 global $wgUser;
00543
00544 $limit = $this->getInt( 'limit', 0 );
00545 if( $limit < 0 ) $limit = 0;
00546 if( ( $limit == 0 ) && ( $optionname != '' ) ) {
00547 $limit = (int)$wgUser->getOption( $optionname );
00548 }
00549 if( $limit <= 0 ) $limit = $deflimit;
00550 if( $limit > 5000 ) $limit = 5000; # We have *some* limits...
00551
00552 $offset = $this->getInt( 'offset', 0 );
00553 if( $offset < 0 ) $offset = 0;
00554
00555 return array( $limit, $offset );
00556 }
00557
00563 public function getFileTempname( $key ) {
00564 if( !isset( $_FILES[$key] ) ) {
00565 return null;
00566 }
00567 return $_FILES[$key]['tmp_name'];
00568 }
00569
00575 public function getFileSize( $key ) {
00576 if( !isset( $_FILES[$key] ) ) {
00577 return 0;
00578 }
00579 return $_FILES[$key]['size'];
00580 }
00581
00587 public function getUploadError( $key ) {
00588 if( !isset( $_FILES[$key] ) || !isset( $_FILES[$key]['error'] ) ) {
00589 return 0;
00590 }
00591 return $_FILES[$key]['error'];
00592 }
00593
00605 public function getFileName( $key ) {
00606 global $wgContLang;
00607 if( !isset( $_FILES[$key] ) ) {
00608 return null;
00609 }
00610 $name = $_FILES[$key]['name'];
00611
00612 # Safari sends filenames in HTML-encoded Unicode form D...
00613 # Horrid and evil! Let's try to make some kind of sense of it.
00614 $name = Sanitizer::decodeCharReferences( $name );
00615 $name = $wgContLang->normalize( $name );
00616 wfDebug( "WebRequest::getFileName() '" . $_FILES[$key]['name'] . "' normalized to '$name'\n" );
00617 return $name;
00618 }
00619
00624 public function response() {
00625
00626 if ( !is_object( $this->_response ) ) {
00627 $class = ( $this instanceof FauxRequest ) ? 'FauxResponse' : 'WebResponse';
00628 $this->_response = new $class();
00629 }
00630 return $this->_response;
00631 }
00632
00637 public function getHeader( $name ) {
00638 $name = strtoupper( $name );
00639 if ( function_exists( 'apache_request_headers' ) ) {
00640 if ( !$this->headers ) {
00641 foreach ( apache_request_headers() as $tempName => $tempValue ) {
00642 $this->headers[ strtoupper( $tempName ) ] = $tempValue;
00643 }
00644 }
00645 if ( isset( $this->headers[$name] ) ) {
00646 return $this->headers[$name];
00647 } else {
00648 return false;
00649 }
00650 } else {
00651 $name = 'HTTP_' . str_replace( '-', '_', $name );
00652 if ( isset( $_SERVER[$name] ) ) {
00653 return $_SERVER[$name];
00654 } else {
00655 return false;
00656 }
00657 }
00658 }
00659
00660
00661
00662
00663
00664
00665 public function getSessionData( $key ) {
00666 if( !isset( $_SESSION[$key] ) )
00667 return null;
00668 return $_SESSION[$key];
00669 }
00670
00676 public function setSessionData( $key, $data ) {
00677 $_SESSION[$key] = $data;
00678 }
00679
00696 public function isPathInfoBad() {
00697 global $wgScriptExtension;
00698
00699 if ( isset( $_SERVER['QUERY_STRING'] )
00700 && preg_match( '/\.[a-z0-9]{1,4}(#|\?|$)/i', $_SERVER['QUERY_STRING'] ) )
00701 {
00702
00703
00704
00705 if ( !isset( $_SERVER['HTTP_USER_AGENT'] )
00706 || preg_match( '/; *MSIE/', $_SERVER['HTTP_USER_AGENT'] ) )
00707 {
00708 return true;
00709 }
00710 }
00711
00712 if ( !isset( $_SERVER['PATH_INFO'] ) ) {
00713 return false;
00714 }
00715 $pi = $_SERVER['PATH_INFO'];
00716 $dotPos = strrpos( $pi, '.' );
00717 if ( $dotPos === false ) {
00718 return false;
00719 }
00720 $ext = substr( $pi, $dotPos );
00721 return !in_array( $ext, array( $wgScriptExtension, '.php', '.php5' ) );
00722 }
00723 }
00724
00730 class FauxRequest extends WebRequest {
00731 private $wasPosted = false;
00732 private $session = array();
00733 private $response;
00734
00740 public function __construct( $data, $wasPosted = false, $session = null ) {
00741 if( is_array( $data ) ) {
00742 $this->data = $data;
00743 } else {
00744 throw new MWException( "FauxRequest() got bogus data" );
00745 }
00746 $this->wasPosted = $wasPosted;
00747 if( $session )
00748 $this->session = $session;
00749 }
00750
00751 private function notImplemented( $method ) {
00752 throw new MWException( "{$method}() not implemented" );
00753 }
00754
00755 public function getText( $name, $default = '' ) {
00756 # Override; don't recode since we're using internal data
00757 return (string)$this->getVal( $name, $default );
00758 }
00759
00760 public function getValues() {
00761 return $this->data;
00762 }
00763
00764 public function wasPosted() {
00765 return $this->wasPosted;
00766 }
00767
00768 public function checkSessionCookie() {
00769 return false;
00770 }
00771
00772 public function getRequestURL() {
00773 $this->notImplemented( __METHOD__ );
00774 }
00775
00776 public function appendQuery( $query ) {
00777 $this->notImplemented( __METHOD__ );
00778 }
00779
00780 public function getHeader( $name ) {
00781 return isset( $this->headers[$name] ) ? $this->headers[$name] : false;
00782 }
00783
00784 public function setHeader( $name, $val ) {
00785 $this->headers[$name] = $val;
00786 }
00787
00788 public function getSessionData( $key ) {
00789 if( isset( $this->session[$key] ) )
00790 return $this->session[$key];
00791 }
00792
00793 public function setSessionData( $key, $data ) {
00794 $this->notImplemented( __METHOD__ );
00795 }
00796
00797 public function isPathInfoBad() {
00798 return false;
00799 }
00800 }