return $permission;
}
}
+
return true;
}
$handler = new $className;
$handler->initializeFromRequest( $request );
+
return $handler;
}
return false;
}
- public function __construct() {}
+ public function __construct() {
+ }
/**
* Returns the upload type. Should be overridden by child classes
$path = $srcPath;
}
wfProfileOut( __METHOD__ );
+
return $path;
}
*/
if ( $this->isEmptyFile() ) {
wfProfileOut( __METHOD__ );
+
return array( 'status' => self::EMPTY_FILE );
}
$maxSize = self::getMaxUploadSize( $this->getSourceType() );
if ( $this->mFileSize > $maxSize ) {
wfProfileOut( __METHOD__ );
+
return array(
'status' => self::FILE_TOO_LARGE,
'max' => $maxSize,
$verification = $this->verifyFile();
if ( $verification !== true ) {
wfProfileOut( __METHOD__ );
+
return array(
'status' => self::VERIFICATION_ERROR,
'details' => $verification
$result = $this->validateName();
if ( $result !== true ) {
wfProfileOut( __METHOD__ );
+
return $result;
}
array( $this->mDestName, $this->mTempPath, &$error ) )
) {
wfProfileOut( __METHOD__ );
+
return array( 'status' => self::HOOK_ABORTED, 'error' => $error );
}
wfProfileOut( __METHOD__ );
+
return array( 'status' => self::OK );
}
$result['blacklistedExt'] = $this->mBlackListedExtensions;
}
}
+
return $result;
}
$this->mDestName = $this->getLocalFile()->getName();
global $wgMimeTypeBlacklist;
if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
wfProfileOut( __METHOD__ );
+
return array( 'filetype-badmime', $mime );
}
foreach ( $ieTypes as $ieType ) {
if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
wfProfileOut( __METHOD__ );
+
return array( 'filetype-bad-ie-mime', $ieType );
}
}
}
wfProfileOut( __METHOD__ );
+
return true;
}
$status = $this->verifyPartialFile();
if ( $status !== true ) {
wfProfileOut( __METHOD__ );
+
return $status;
}
# XXX: Missing extension will be caught by validateName() via getTitle()
if ( $this->mFinalExtension != '' && !$this->verifyExtension( $mime, $this->mFinalExtension ) ) {
wfProfileOut( __METHOD__ );
+
return array( 'filetype-mime-mismatch', $this->mFinalExtension, $mime );
}
}
if ( !$handlerStatus->isOK() ) {
$errors = $handlerStatus->getErrorsArray();
wfProfileOut( __METHOD__ );
+
return reset( $errors );
}
}
wfRunHooks( 'UploadVerifyFile', array( $this, $mime, &$status ) );
if ( $status !== true ) {
wfProfileOut( __METHOD__ );
+
return $status;
}
wfDebug( __METHOD__ . ": all clear; passing.\n" );
wfProfileOut( __METHOD__ );
+
return true;
}
$status = $this->verifyMimeType( $mime );
if ( $status !== true ) {
wfProfileOut( __METHOD__ );
+
return $status;
}
if ( !$wgDisableUploadScriptChecks ) {
if ( self::detectScript( $this->mTempPath, $mime, $this->mFinalExtension ) ) {
wfProfileOut( __METHOD__ );
+
return array( 'uploadscripted' );
}
if ( $this->mFinalExtension == 'svg' || $mime == 'image/svg+xml' ) {
$svgStatus = $this->detectScriptInSvg( $this->mTempPath );
if ( $svgStatus !== false ) {
wfProfileOut( __METHOD__ );
+
return $svgStatus;
}
}
$error = reset( $errors );
if ( $error[0] !== 'zip-wrong-format' ) {
wfProfileOut( __METHOD__ );
+
return $error;
}
}
if ( $this->mJavaDetected ) {
wfProfileOut( __METHOD__ );
+
return array( 'uploadjava' );
}
}
$virus = $this->detectVirus( $this->mTempPath );
if ( $virus ) {
wfProfileOut( __METHOD__ );
+
return array( 'uploadvirus', $virus );
}
wfProfileOut( __METHOD__ );
+
return true;
}
if ( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
$permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) );
$permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsCreate, $permErrors ) );
+
return $permErrors;
}
}
wfProfileOut( __METHOD__ );
+
return $warnings;
}
}
wfProfileOut( __METHOD__ );
+
return $status;
}
if ( strlen( $this->mFilteredName ) > 240 ) {
$this->mTitleError = self::FILENAME_TOO_LONG;
$this->mTitle = null;
+
return $this->mTitle;
}
if ( is_null( $nt ) ) {
$this->mTitleError = self::ILLEGAL_FILENAME;
$this->mTitle = null;
+
return $this->mTitle;
}
$this->mFilteredName = $nt->getDBkey();
if ( $this->mFinalExtension == '' ) {
$this->mTitleError = self::FILETYPE_MISSING;
$this->mTitle = null;
+
return $this->mTitle;
} elseif ( $blackListedExtensions ||
- ( $wgCheckFileExtensions && $wgStrictFileExtensions &&
- !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) ) ) {
+ ( $wgCheckFileExtensions && $wgStrictFileExtensions &&
+ !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) )
+ ) {
$this->mBlackListedExtensions = $blackListedExtensions;
$this->mTitleError = self::FILETYPE_BADTYPE;
$this->mTitle = null;
+
return $this->mTitle;
}
if ( wfIsWindows() && !preg_match( '/^[\x0-\x7f]*$/', $nt->getText() ) ) {
$this->mTitleError = self::WINDOWS_NONASCII_FILENAME;
$this->mTitle = null;
+
return $this->mTitle;
}
if ( strlen( $partname ) < 1 ) {
$this->mTitleError = self::MIN_LENGTH_PARTNAME;
$this->mTitle = null;
+
return $this->mTitle;
}
$this->mTitle = $nt;
+
return $this->mTitle;
}
$nt = $this->getTitle();
$this->mLocalFile = is_null( $nt ) ? null : wfLocalFile( $nt );
}
+
return $this->mLocalFile;
}
$this->mLocalFile = $file;
wfProfileOut( __METHOD__ );
+
return $file;
}
public static function splitExtensions( $filename ) {
$bits = explode( '.', $filename );
$basename = array_shift( $bits );
+
return array( $basename, $bits );
}
if ( !$magic->isRecognizableExtension( $extension ) ) {
wfDebug( __METHOD__ . ": passing file with unknown detected mime type; " .
"unrecognized extension '$extension', can't verify\n" );
+
return true;
} else {
wfDebug( __METHOD__ . ": rejecting file with unknown detected mime type; " .
"recognized extension '$extension', so probably invalid file\n" );
+
return false;
}
}
if ( $match === null ) {
if ( $magic->getTypesForExtension( $extension ) !== null ) {
wfDebug( __METHOD__ . ": No extension known for $mime, but we know a mime for $extension\n" );
+
return false;
} else {
wfDebug( __METHOD__ . ": no file extension known for mime type $mime, passing file\n" );
+
return true;
}
} elseif ( $match === true ) {
#TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
return true;
-
} else {
wfDebug( __METHOD__ . ": mime type $mime mismatches file extension $extension, rejecting file\n" );
+
return false;
}
}
if ( !$chunk ) {
wfProfileOut( __METHOD__ );
+
return false;
}
# check for HTML doctype
if ( preg_match( "/<!DOCTYPE *X?HTML/i", $chunk ) ) {
wfProfileOut( __METHOD__ );
+
return true;
}
if ( $extension == 'svg' || strpos( $mime, 'image/svg' ) === 0 ) {
if ( self::checkXMLEncodingMissmatch( $file ) ) {
wfProfileOut( __METHOD__ );
+
return true;
}
}
'<a href',
'<body',
'<head',
- '<html', #also in safari
+ '<html', #also in safari
'<img',
'<pre',
'<script', #also in safari
if ( false !== strpos( $chunk, $tag ) ) {
wfDebug( __METHOD__ . ": found something that may make it be mistaken for html: $tag\n" );
wfProfileOut( __METHOD__ );
+
return true;
}
}
if ( preg_match( '!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim', $chunk ) ) {
wfDebug( __METHOD__ . ": found script types\n" );
wfProfileOut( __METHOD__ );
+
return true;
}
if ( preg_match( '!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
wfDebug( __METHOD__ . ": found html-style script urls\n" );
wfProfileOut( __METHOD__ );
+
return true;
}
if ( preg_match( '!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
wfDebug( __METHOD__ . ": found css-style script urls\n" );
wfProfileOut( __METHOD__ );
+
return true;
}
wfDebug( __METHOD__ . ": no scripts found\n" );
wfProfileOut( __METHOD__ );
+
return false;
}
&& !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
) {
wfDebug( __METHOD__ . ": Found unsafe XML encoding '{$encMatch[1]}'\n" );
+
return true;
}
} elseif ( preg_match( "!<\?xml\b!si", $contents ) ) {
// Start of XML declaration without an end in the first $wgSVGMetadataCutoff
// bytes. There shouldn't be a legitimate reason for this to happen.
wfDebug( __METHOD__ . ": Unmatched XML declaration start\n" );
+
return true;
} elseif ( substr( $contents, 0, 4 ) == "\x4C\x6F\xA7\x94" ) {
// EBCDIC encoded XML
wfDebug( __METHOD__ . ": EBCDIC Encoded XML\n" );
+
return true;
}
wfSuppressWarnings();
$str = iconv( $encoding, 'UTF-8', $contents );
wfRestoreWarnings();
- if ( $str != '' && preg_match( "!<\?xml\b(.*?)\?>!si", $str, $matches ) ) {
+ if ( $str != '' && preg_match( "!<\?xml\b(.*?)\?>!si", $str, $matches ) ) {
if ( preg_match( $encodingRegex, $matches[1], $encMatch )
&& !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
) {
wfDebug( __METHOD__ . ": Found unsafe XML encoding '{$encMatch[1]}'\n" );
+
return true;
}
} elseif ( $str != '' && preg_match( "!<\?xml\b!si", $str ) ) {
// Start of XML declaration without an end in the first $wgSVGMetadataCutoff
// bytes. There shouldn't be a legitimate reason for this to happen.
wfDebug( __METHOD__ . ": Unmatched XML declaration start\n" );
+
return true;
}
}
if ( $this->mSVGNSError ) {
return array( 'uploadscriptednamespace', $this->mSVGNSError );
}
+
return array( 'uploadscripted' );
}
+
return false;
}
if ( preg_match( '/xml-stylesheet/i', $target ) ) {
return true;
}
+
return false;
}
wfDebug( __METHOD__ . ": Non-svg namespace '$namespace' in uploaded file.\n" );
// @TODO return a status object to a closure in XmlTypeCheck, for MW1.21+
$this->mSVGNSError = $namespace;
+
return true;
}
*/
if ( $strippedElement == 'script' ) {
wfDebug( __METHOD__ . ": Found script element '$element' in uploaded file.\n" );
+
return true;
}
# e.g., <svg xmlns="http://www.w3.org/2000/svg"> <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
if ( $strippedElement == 'handler' ) {
wfDebug( __METHOD__ . ": Found scriptable element '$element' in uploaded file.\n" );
+
return true;
}
# SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
if ( $strippedElement == 'stylesheet' ) {
wfDebug( __METHOD__ . ": Found scriptable element '$element' in uploaded file.\n" );
+
return true;
}
# Block iframes, in case they pass the namespace check
if ( $strippedElement == 'iframe' ) {
wfDebug( __METHOD__ . ": iframe in uploaded file.\n" );
+
return true;
}
if ( substr( $stripped, 0, 2 ) == 'on' ) {
wfDebug( __METHOD__ . ": Found event-handler attribute '$attrib'='$value' in uploaded file.\n" );
+
return true;
}
# href with javascript target
if ( $stripped == 'href' && strpos( strtolower( $value ), 'javascript:' ) !== false ) {
wfDebug( __METHOD__ . ": Found script in href attribute '$attrib'='$value' in uploaded file.\n" );
+
return true;
}
# href with embedded svg as target
if ( $stripped == 'href' && preg_match( '!data:[^,]*image/svg[^,]*,!sim', $value ) ) {
wfDebug( __METHOD__ . ": Found href to embedded svg \"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+
return true;
}
# href with embedded (text/xml) svg as target
if ( $stripped == 'href' && preg_match( '!data:[^,]*text/xml[^,]*,!sim', $value ) ) {
wfDebug( __METHOD__ . ": Found href to embedded svg \"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+
return true;
}
# use set/animate to add event-handler attribute to parent
if ( ( $strippedElement == 'set' || $strippedElement == 'animate' ) && $stripped == 'attributename' && substr( $value, 0, 2 ) == 'on' ) {
wfDebug( __METHOD__ . ": Found svg setting event-handler attribute with \"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
+
return true;
}
# use set to add href attribute to parent element
if ( $strippedElement == 'set' && $stripped == 'attributename' && strpos( $value, 'href' ) !== false ) {
wfDebug( __METHOD__ . ": Found svg setting href attribute '$value' in uploaded file.\n" );
+
return true;
}
# use set to add a remote / data / script target to an element
if ( $strippedElement == 'set' && $stripped == 'to' && preg_match( '!(http|https|data|script):!sim', $value ) ) {
wfDebug( __METHOD__ . ": Found svg setting attribute to '$value' in uploaded file.\n" );
+
return true;
}
# use handler attribute with remote / data / script
if ( $stripped == 'handler' && preg_match( '!(http|https|data|script):!sim', $value ) ) {
wfDebug( __METHOD__ . ": Found svg setting handler with remote/data/script '$attrib'='$value' in uploaded file.\n" );
+
return true;
}
foreach ( $matches[1] as $match ) {
if ( !preg_match( '!(?:font|clip-path|fill|filter|marker|marker-end|marker-mid|marker-start|mask|stroke)\s*:\s*url\s*\(\s*(#|\'#|"#)!sim', $match ) ) {
wfDebug( __METHOD__ . ": Found svg setting a style with remote url '$attrib'='$value' in uploaded file.\n" );
+
return true;
}
}
# image filters can pull in url, which could be svg that executes scripts
if ( $strippedElement == 'image' && $stripped == 'filter' && preg_match( '!url\s*\(!sim', $value ) ) {
wfDebug( __METHOD__ . ": Found image filter with url: \"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
+
return true;
}
-
}
return false; //No scripts detected
$parts = explode( ':', strtolower( $element ) );
$name = array_pop( $parts );
$ns = implode( ':', $parts );
+
return array( $ns, $name );
}
private function stripXmlNamespace( $name ) {
// 'http://www.w3.org/2000/svg:script' -> 'script'
$parts = explode( ':', strtolower( $name ) );
+
return array_pop( $parts );
}
if ( !$wgAntivirus ) {
wfDebug( __METHOD__ . ": virus scanner disabled\n" );
wfProfileOut( __METHOD__ );
+
return null;
}
$wgOut->wrapWikiMsg( "<div class=\"error\">\n$1\n</div>",
array( 'virus-badscanner', $wgAntivirus ) );
wfProfileOut( __METHOD__ );
+
return wfMessage( 'virus-unknownscanner' )->text() . " $wgAntivirus";
}
}
wfProfileOut( __METHOD__ );
+
return $output;
}
// Check for files with the same name but a different extension
$similarFiles = RepoGroup::singleton()->getLocalRepo()->findFilesByPrefix(
- "{$partname}.", 1 );
+ "{$partname}.", 1 );
if ( count( $similarFiles ) ) {
return array(
'warning' => 'exists-normalized',
public static function isThumbName( $filename ) {
$n = strrpos( $filename, '.' );
$partname = $n ? substr( $filename, 0, $n ) : $filename;
+
return (
- substr( $partname, 3, 3 ) == 'px-' ||
- substr( $partname, 2, 3 ) == 'px-'
- ) &&
- preg_match( "/[0-9]{2}/", substr( $partname, 0, 2 ) );
+ substr( $partname, 3, 3 ) == 'px-' ||
+ substr( $partname, 2, 3 ) == 'px-'
+ ) &&
+ preg_match( "/[0-9]{2}/", substr( $partname, 0, 2 ) );
}
/**
$blacklist[] = trim( $line );
}
}
+
return $blacklist;
}
$imParam = ApiQueryImageInfo::getPropertyNames();
$info = ApiQueryImageInfo::getInfo( $file, array_flip( $imParam ), $result );
}
+
return $info;
}
public function convertVerifyErrorToStatus( $error ) {
$code = $error['status'];
unset( $code['status'] );
+
return Status::newFatal( $this->getVerificationErrorCode( $code ), $error );
}
* - enable applications to temporarily stash files without publishing them to the wiki.
* - Several parts of MediaWiki do this in similar ways: UploadBase, UploadWizard, and FirefoggChunkedExtension
* And there are several that reimplement stashing from scratch, in idiosyncratic ways. The idea is to unify them all here.
- * Mostly all of them are the same except for storing some custom fields, which we subsume into the data array.
+ * Mostly all of them are the same except for storing some custom fields, which we subsume into the data array.
* - enable applications to find said files later, as long as the db table or temp files haven't been purged.
* - enable the uploading user (and *ONLY* the uploading user) to access said files, and thumbnails of said files, via a URL.
* We accomplish this using a database table, with ownership checking as you might expect. See SpecialUploadStash, which
* @ingroup Upload
*/
class UploadStash {
-
// Format of the key for files -- has to be suitable as a filename itself (e.g. ab12cd34ef.jpg)
const KEY_FORMAT_REGEX = '/^[\w-\.]+\.\w*$/';
* @return UploadStashFile
*/
public function getFile( $key, $noAuth = false ) {
- if ( ! preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
+ if ( !preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
throw new UploadStashBadPathException( "key '$key' is not in a proper format" );
}
}
}
- if ( ! $this->files[$key]->exists() ) {
+ if ( !$this->files[$key]->exists() ) {
wfDebug( __METHOD__ . " tried to get file at $key, but it doesn't exist\n" );
throw new UploadStashBadPathException( "path doesn't exist" );
}
*/
public function getMetadata( $key ) {
$this->getFile( $key );
+
return $this->fileMetadata[$key];
}
*/
public function getFileProps( $key ) {
$this->getFile( $key );
+
return $this->fileProps[$key];
}
$this->fileProps[$key] = $fileProps;
- if ( ! preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
+ if ( !preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
throw new UploadStashBadPathException( "key '$key' is not in a proper format" );
}
// if not already in a temporary area, put it there
$storeStatus = $this->repo->storeTemp( basename( $pathWithGoodExtension ), $path );
- if ( ! $storeStatus->isOK() ) {
+ if ( !$storeStatus->isOK() ) {
// It is a convention in MediaWiki to only return one error per API exception, even if multiple errors
// are available. We use reset() to pick the "first" thing that was wrong, preferring errors to warnings.
// This is a bit lame, as we may have more info in the $storeStatus and we're throwing it away, but to fix it means
// $storeStatus->value just contains the virtual URL (if anything) which is probably useless to the caller
$error = $storeStatus->getErrorsArray();
$error = reset( $error );
- if ( ! count( $error ) ) {
+ if ( !count( $error ) ) {
$error = $storeStatus->getWarningsArray();
$error = reset( $error );
- if ( ! count( $error ) ) {
+ if ( !count( $error ) ) {
$error = array( 'unknown', 'no error recorded' );
}
}
// put it in a web accesible directory.
return '';
}
+
return $extension;
}
throw new UploadStashZeroLengthFileException( "File is zero length" );
}
$this->files[$key] = $file;
+
return true;
}
}
if ( $repo->isVirtualUrl( $path ) ) {
$path = $repo->resolveVirtualUrl( $path );
} else {
-
// check if path appears to be sane, no parent traversals, and is in this repo's temp zone.
$repoTempPath = $repo->getZonePath( 'temp' );
- if ( ( ! $repo->validateFilename( $path ) ) ||
- ( strpos( $path, $repoTempPath ) !== 0 ) ) {
+ if ( ( !$repo->validateFilename( $path ) ) ||
+ ( strpos( $path, $repoTempPath ) !== 0 )
+ ) {
wfDebug( "UploadStash: tried to construct an UploadStashFile from a file that should already exist at '$path', but path is not valid\n" );
throw new UploadStashBadPathException( 'path is not valid' );
}
// check if path exists! and is a plain file.
- if ( ! $repo->fileExists( $path ) ) {
+ if ( !$repo->fileExists( $path ) ) {
wfDebug( "UploadStash: tried to construct an UploadStashFile from a file that should already exist at '$path', but path is not found\n" );
throw new UploadStashFileNotFoundException( 'cannot find path, or not a plain file' );
}
if ( $thumbName !== false ) {
$path .= "/$thumbName";
}
+
return $path;
}
*/
public function getThumbUrl( $thumbName = false ) {
wfDebug( __METHOD__ . " getting for $thumbName \n" );
+
return $this->getSpecialUrl( 'thumb/' . $this->getUrlName() . '/' . $thumbName );
}
* @return string Base url name, like '120px-123456.jpg'
*/
public function getUrlName() {
- if ( ! $this->urlName ) {
+ if ( !$this->urlName ) {
$this->urlName = $this->fileKey;
}
+
return $this->urlName;
}
if ( !isset( $this->url ) ) {
$this->url = $this->getSpecialUrl( 'file/' . $this->getUrlName() );
}
+
return $this->url;
}
public function exists() {
return $this->repo->fileExists( $this->path );
}
+}
+
+class UploadStashException extends MWException {
+}
+
+class UploadStashNotAvailableException extends UploadStashException {
+}
+
+class UploadStashFileNotFoundException extends UploadStashException {
+}
+
+class UploadStashBadPathException extends UploadStashException {
+}
+
+class UploadStashFileException extends UploadStashException {
+}
+
+class UploadStashZeroLengthFileException extends UploadStashException {
+}
+
+class UploadStashNotLoggedInException extends UploadStashException {
+}
+
+class UploadStashWrongOwnerException extends UploadStashException {
+}
+class UploadStashNoSuchKeyException extends UploadStashException {
}
-class UploadStashException extends MWException {};
-class UploadStashNotAvailableException extends UploadStashException {};
-class UploadStashFileNotFoundException extends UploadStashException {};
-class UploadStashBadPathException extends UploadStashException {};
-class UploadStashFileException extends UploadStashException {};
-class UploadStashZeroLengthFileException extends UploadStashException {};
-class UploadStashNotLoggedInException extends UploadStashException {};
-class UploadStashWrongOwnerException extends UploadStashException {};
-class UploadStashNoSuchKeyException extends UploadStashException {};