+
+ /**
+ * Returns true if the PATH_INFO ends with an extension other than a script
+ * extension. This could confuse IE for scripts that send arbitrary data which
+ * is not HTML but may be detected as such.
+ *
+ * Various past attempts to use the URL to make this check have generally
+ * run up against the fact that CGI does not provide a standard method to
+ * determine the URL. PATH_INFO may be mangled (e.g. if cgi.fix_pathinfo=0),
+ * 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.
+ */
+ public function isPathInfoBad() {
+ global $wgScriptExtension;
+
+ if ( !isset( $_SERVER['PATH_INFO'] ) ) {
+ return false;
+ }
+ $pi = $_SERVER['PATH_INFO'];
+ $dotPos = strrpos( $pi, '.' );
+ if ( $dotPos === false ) {
+ return false;
+ }
+ $ext = substr( $pi, $dotPos );
+ return !in_array( $ext, array( $wgScriptExtension, '.php', '.php5' ) );
+ }
+
+ /**
+ * Parse the Accept-Language header sent by the client into an array
+ * @return array( languageCode => q-value ) sorted by q-value in descending order
+ * May contain the "language" '*', which applies to languages other than those explicitly listed.
+ * This is aligned with rfc2616 section 14.4
+ */
+ public function getAcceptLang() {
+ // Modified version of code found at http://www.thefutureoftheweb.com/blog/use-accept-language-header
+ $acceptLang = $this->getHeader( 'Accept-Language' );
+ if ( !$acceptLang ) {
+ return array();
+ }
+
+ // Return the language codes in lower case
+ $acceptLang = strtolower( $acceptLang );
+
+ // Break up string into pieces (languages and q factors)
+ $lang_parse = null;
+ preg_match_all( '/([a-z]{1,8}(-[a-z]{1,8})?|\*)\s*(;\s*q\s*=\s*(1|0(\.[0-9]+)?)?)?/',
+ $acceptLang, $lang_parse );
+
+ if ( !count( $lang_parse[1] ) ) {
+ return array();
+ }
+
+ // Create a list like "en" => 0.8
+ $langs = array_combine( $lang_parse[1], $lang_parse[4] );
+ // Set default q factor to 1
+ foreach ( $langs as $lang => $val ) {
+ if ( $val === '' ) {
+ $langs[$lang] = 1;
+ } else if ( $val == 0 ) {
+ unset($langs[$lang]);
+ }
+ }
+
+ // Sort list
+ arsort( $langs, SORT_NUMERIC );
+ return $langs;
+ }
+}
+
+/**
+ * Object to access the $_FILES array
+ */
+class WebRequestUpload {
+ protected $request;
+ protected $doesExist;
+ protected $fileInfo;
+
+ /**
+ * Constructor. Should only be called by WebRequest
+ *
+ * @param $request WebRequest The associated request
+ * @param $key string Key in $_FILES array (name of form field)
+ */
+ public function __construct( $request, $key ) {
+ $this->request = $request;
+ $this->doesExist = isset( $_FILES[$key] );
+ if ( $this->doesExist ) {
+ $this->fileInfo = $_FILES[$key];
+ }
+ }
+
+ /**
+ * Return whether a file with this name was uploaded.
+ *
+ * @return bool
+ */
+ public function exists() {
+ return $this->doesExist;
+ }
+
+ /**
+ * Return the original filename of the uploaded file
+ *
+ * @return mixed Filename or null if non-existent
+ */
+ public function getName() {
+ if ( !$this->exists() ) {
+ return null;
+ }
+
+ global $wgContLang;
+ $name = $this->fileInfo['name'];
+
+ # Safari sends filenames in HTML-encoded Unicode form D...
+ # Horrid and evil! Let's try to make some kind of sense of it.
+ $name = Sanitizer::decodeCharReferences( $name );
+ $name = $wgContLang->normalize( $name );
+ wfDebug( __METHOD__ . ": {$this->fileInfo['name']} normalized to '$name'\n" );
+ return $name;
+ }
+
+ /**
+ * Return the file size of the uploaded file
+ *
+ * @return int File size or zero if non-existent
+ */
+ public function getSize() {
+ if ( !$this->exists() ) {
+ return 0;
+ }
+
+ return $this->fileInfo['size'];
+ }
+
+ /**
+ * Return the path to the temporary file
+ *
+ * @return mixed Path or null if non-existent
+ */
+ public function getTempName() {
+ if ( !$this->exists() ) {
+ return null;
+ }
+
+ return $this->fileInfo['tmp_name'];
+ }
+
+ /**
+ * Return the upload error. See link for explanation
+ * http://www.php.net/manual/en/features.file-upload.errors.php
+ *
+ * @return int One of the UPLOAD_ constants, 0 if non-existent
+ */
+ public function getError() {
+ if ( !$this->exists() ) {
+ return 0; # UPLOAD_ERR_OK
+ }
+
+ return $this->fileInfo['error'];
+ }
+
+ /**
+ * Returns whether this upload failed because of overflow of a maximum set
+ * in php.ini
+ *
+ * @return bool
+ */
+ public function isIniSizeOverflow() {
+ if ( $this->getError() == UPLOAD_ERR_INI_SIZE ) {
+ # PHP indicated that upload_max_filesize is exceeded
+ return true;
+ }
+
+ $contentLength = $this->request->getHeader( 'CONTENT_LENGTH' );
+ if ( $contentLength > wfShorthandToInteger( ini_get( 'post_max_size' ) ) ) {
+ # post_max_size is exceeded
+ return true;
+ }
+
+ return false;
+ }