summaryrefslogtreecommitdiff
path: root/includes/upload/UploadBase.php
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2013-01-18 16:46:04 +0100
committerPierre Schmitz <pierre@archlinux.de>2013-01-18 16:46:04 +0100
commit63601400e476c6cf43d985f3e7b9864681695ed4 (patch)
treef7846203a952e38aaf66989d0a4702779f549962 /includes/upload/UploadBase.php
parent8ff01378c9e0207f9169b81966a51def645b6a51 (diff)
Update to MediaWiki 1.20.2
this update includes: * adjusted Arch Linux skin * updated FluxBBAuthPlugin * patch for https://bugzilla.wikimedia.org/show_bug.cgi?id=44024
Diffstat (limited to 'includes/upload/UploadBase.php')
-rw-r--r--includes/upload/UploadBase.php148
1 files changed, 118 insertions, 30 deletions
diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php
index 6cc2287a..d40b53d3 100644
--- a/includes/upload/UploadBase.php
+++ b/includes/upload/UploadBase.php
@@ -1,6 +1,28 @@
<?php
/**
- * @defgroup Upload
+ * Base class for the backend of file upload.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Upload
+ */
+
+/**
+ * @defgroup Upload Upload related
*/
/**
@@ -41,6 +63,10 @@ abstract class UploadBase {
const WINDOWS_NONASCII_FILENAME = 13;
const FILENAME_TOO_LONG = 14;
+ /**
+ * @param $error int
+ * @return string
+ */
public function getVerificationErrorCode( $error ) {
$code_to_status = array(self::EMPTY_FILE => 'empty-file',
self::FILE_TOO_LARGE => 'file-too-large',
@@ -64,6 +90,7 @@ abstract class UploadBase {
/**
* Returns true if uploads are enabled.
* Can be override by subclasses.
+ * @return bool
*/
public static function isEnabled() {
global $wgEnableUploads;
@@ -82,6 +109,7 @@ abstract class UploadBase {
* Can be overriden by subclasses.
*
* @param $user User
+ * @return bool
*/
public static function isAllowed( $user ) {
foreach ( array( 'upload', 'edit' ) as $permission ) {
@@ -100,6 +128,7 @@ abstract class UploadBase {
*
* @param $request WebRequest
* @param $type
+ * @return null
*/
public static function createFromRequest( &$request, $type = null ) {
$type = $type ? $type : $request->getVal( 'wpSourceType', 'File' );
@@ -140,6 +169,8 @@ abstract class UploadBase {
/**
* Check whether a request if valid for this handler
+ * @param $request
+ * @return bool
*/
public static function isValidRequest( $request ) {
return false;
@@ -161,7 +192,7 @@ abstract class UploadBase {
* @param $tempPath string the temporary path
* @param $fileSize int the file size
* @param $removeTempFile bool (false) remove the temporary file?
- * @return null
+ * @throws MWException
*/
public function initializePathInfo( $name, $tempPath, $fileSize, $removeTempFile = false ) {
$this->mDesiredDestName = $name;
@@ -180,6 +211,7 @@ abstract class UploadBase {
/**
* Fetch the file. Usually a no-op
+ * @return Status
*/
public function fetchFile() {
return Status::newGood();
@@ -203,17 +235,20 @@ abstract class UploadBase {
/**
* @param $srcPath String: the source path
- * @return the real path if it was a virtual URL
+ * @return string the real path if it was a virtual URL
*/
function getRealPath( $srcPath ) {
+ wfProfileIn( __METHOD__ );
$repo = RepoGroup::singleton()->getLocalRepo();
if ( $repo->isVirtualUrl( $srcPath ) ) {
// @TODO: just make uploads work with storage paths
// UploadFromStash loads files via virtuals URLs
$tmpFile = $repo->getLocalCopy( $srcPath );
$tmpFile->bind( $this ); // keep alive with $thumb
+ wfProfileOut( __METHOD__ );
return $tmpFile->getPath();
}
+ wfProfileOut( __METHOD__ );
return $srcPath;
}
@@ -222,10 +257,13 @@ abstract class UploadBase {
* @return mixed self::OK or else an array with error information
*/
public function verifyUpload() {
+ wfProfileIn( __METHOD__ );
+
/**
* If there was no filename or a zero size given, give up quick.
*/
if( $this->isEmptyFile() ) {
+ wfProfileOut( __METHOD__ );
return array( 'status' => self::EMPTY_FILE );
}
@@ -234,6 +272,7 @@ abstract class UploadBase {
*/
$maxSize = self::getMaxUploadSize( $this->getSourceType() );
if( $this->mFileSize > $maxSize ) {
+ wfProfileOut( __METHOD__ );
return array(
'status' => self::FILE_TOO_LARGE,
'max' => $maxSize,
@@ -247,6 +286,7 @@ abstract class UploadBase {
*/
$verification = $this->verifyFile();
if( $verification !== true ) {
+ wfProfileOut( __METHOD__ );
return array(
'status' => self::VERIFICATION_ERROR,
'details' => $verification
@@ -258,15 +298,19 @@ abstract class UploadBase {
*/
$result = $this->validateName();
if( $result !== true ) {
+ wfProfileOut( __METHOD__ );
return $result;
}
$error = '';
if( !wfRunHooks( 'UploadVerification',
- array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
+ array( $this->mDestName, $this->mTempPath, &$error ) ) )
+ {
+ wfProfileOut( __METHOD__ );
return array( 'status' => self::HOOK_ABORTED, 'error' => $error );
}
+ wfProfileOut( __METHOD__ );
return array( 'status' => self::OK );
}
@@ -304,15 +348,18 @@ abstract class UploadBase {
*/
protected function verifyMimeType( $mime ) {
global $wgVerifyMimeType;
+ wfProfileIn( __METHOD__ );
if ( $wgVerifyMimeType ) {
wfDebug ( "\n\nmime: <$mime> extension: <{$this->mFinalExtension}>\n\n");
global $wgMimeTypeBlacklist;
if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
+ wfProfileOut( __METHOD__ );
return array( 'filetype-badmime', $mime );
}
# 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 );
}
@@ -326,11 +373,13 @@ abstract class UploadBase {
$ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
foreach ( $ieTypes as $ieType ) {
if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
+ wfProfileOut( __METHOD__ );
return array( 'filetype-bad-ie-mime', $ieType );
}
}
}
+ wfProfileOut( __METHOD__ );
return true;
}
@@ -341,6 +390,8 @@ abstract class UploadBase {
*/
protected function verifyFile() {
global $wgAllowJavaUploads, $wgDisableUploadScriptChecks;
+ wfProfileIn( __METHOD__ );
+
# get the title, even though we are doing nothing with it, because
# we need to populate mFinalExtension
$this->getTitle();
@@ -351,16 +402,19 @@ abstract class UploadBase {
$mime = $this->mFileProps[ 'file-mime' ];
$status = $this->verifyMimeType( $mime );
if ( $status !== true ) {
+ wfProfileOut( __METHOD__ );
return $status;
}
# check for htmlish code and javascript
if ( !$wgDisableUploadScriptChecks ) {
if( self::detectScript( $this->mTempPath, $mime, $this->mFinalExtension ) ) {
+ wfProfileOut( __METHOD__ );
return array( 'uploadscripted' );
}
if( $this->mFinalExtension == 'svg' || $mime == 'image/svg+xml' ) {
if( $this->detectScriptInSvg( $this->mTempPath ) ) {
+ wfProfileOut( __METHOD__ );
return array( 'uploadscripted' );
}
}
@@ -376,10 +430,12 @@ abstract class UploadBase {
$errors = $zipStatus->getErrorsArray();
$error = reset( $errors );
if ( $error[0] !== 'zip-wrong-format' ) {
+ wfProfileOut( __METHOD__ );
return $error;
}
}
if ( $this->mJavaDetected ) {
+ wfProfileOut( __METHOD__ );
return array( 'uploadjava' );
}
}
@@ -387,6 +443,7 @@ abstract class UploadBase {
# Scan the uploaded file for viruses
$virus = $this->detectVirus( $this->mTempPath );
if ( $virus ) {
+ wfProfileOut( __METHOD__ );
return array( 'uploadvirus', $virus );
}
@@ -395,16 +452,19 @@ abstract class UploadBase {
$handlerStatus = $handler->verifyUpload( $this->mTempPath );
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;
}
@@ -490,6 +550,7 @@ abstract class UploadBase {
*/
public function checkWarnings() {
global $wgLang;
+ wfProfileIn( __METHOD__ );
$warnings = array();
@@ -550,6 +611,7 @@ abstract class UploadBase {
$warnings['duplicate-archive'] = $archivedImage->getName();
}
+ wfProfileOut( __METHOD__ );
return $warnings;
}
@@ -557,11 +619,16 @@ abstract class UploadBase {
* Really perform the upload. Stores the file in the local repo, watches
* if necessary and runs the UploadComplete hook.
*
+ * @param $comment
+ * @param $pageText
+ * @param $watch
* @param $user User
*
* @return Status indicating the whether the upload succeeded.
*/
public function performUpload( $comment, $pageText, $watch, $user ) {
+ wfProfileIn( __METHOD__ );
+
$status = $this->getLocalFile()->upload(
$this->mTempPath,
$comment,
@@ -576,10 +643,10 @@ abstract class UploadBase {
if ( $watch ) {
$user->addWatch( $this->getLocalFile()->getTitle() );
}
-
wfRunHooks( 'UploadComplete', array( &$this ) );
}
+ wfProfileOut( __METHOD__ );
return $status;
}
@@ -699,7 +766,7 @@ abstract class UploadBase {
/**
* Return the local file and initializes if necessary.
*
- * @return LocalFile
+ * @return LocalFile|null
*/
public function getLocalFile() {
if( is_null( $this->mLocalFile ) ) {
@@ -710,26 +777,6 @@ abstract class UploadBase {
}
/**
- * NOTE: Probably should be deprecated in favor of UploadStash, but this is sometimes
- * called outside that context.
- *
- * Stash a file in a temporary directory for later processing
- * after the user has confirmed it.
- *
- * If the user doesn't explicitly cancel or accept, these files
- * can accumulate in the temp directory.
- *
- * @param $saveName String: the destination filename
- * @param $tempSrc String: the source temporary file to save
- * @return String: full path the stashed file, or false on failure
- */
- protected function saveTempUploadedFile( $saveName, $tempSrc ) {
- $repo = RepoGroup::singleton()->getLocalRepo();
- $status = $repo->storeTemp( $saveName, $tempSrc );
- return $status;
- }
-
- /**
* If the user does not supply all necessary information in the first upload form submission (either by accident or
* by design) then we may want to stash the file temporarily, get more information, and publish the file later.
*
@@ -742,9 +789,13 @@ abstract class UploadBase {
*/
public function stashFile() {
// was stashSessionFile
+ wfProfileIn( __METHOD__ );
+
$stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash();
$file = $stash->stashFile( $this->mTempPath, $this->getSourceType() );
$this->mLocalFile = $file;
+
+ wfProfileOut( __METHOD__ );
return $file;
}
@@ -787,6 +838,7 @@ abstract class UploadBase {
* earlier pseudo-'extensions' to determine type and execute
* scripts, so the blacklist needs to check them all.
*
+ * @param $filename string
* @return array
*/
public static function splitExtensions( $filename ) {
@@ -870,6 +922,7 @@ abstract class UploadBase {
*/
public static function detectScript( $file, $mime, $extension ) {
global $wgAllowTitlesInSVG;
+ wfProfileIn( __METHOD__ );
# ugly hack: for text files, always look at the entire file.
# For binary field, just check the first K.
@@ -885,6 +938,7 @@ abstract class UploadBase {
$chunk = strtolower( $chunk );
if( !$chunk ) {
+ wfProfileOut( __METHOD__ );
return false;
}
@@ -908,6 +962,7 @@ abstract class UploadBase {
# check for HTML doctype
if ( preg_match( "/<!DOCTYPE *X?HTML/i", $chunk ) ) {
+ wfProfileOut( __METHOD__ );
return true;
}
@@ -944,6 +999,7 @@ abstract class UploadBase {
foreach( $tags as $tag ) {
if( false !== strpos( $chunk, $tag ) ) {
wfDebug( __METHOD__ . ": found something that may make it be mistaken for html: $tag\n" );
+ wfProfileOut( __METHOD__ );
return true;
}
}
@@ -958,25 +1014,33 @@ abstract class UploadBase {
# look for script-types
if( preg_match( '!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim', $chunk ) ) {
wfDebug( __METHOD__ . ": found script types\n" );
+ wfProfileOut( __METHOD__ );
return true;
}
# look for html-style script-urls
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;
}
# look for css-style script-urls
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;
}
+ /**
+ * @param $filename string
+ * @return bool
+ */
protected function detectScriptInSvg( $filename ) {
$check = new XmlTypeCheck( $filename, array( $this, 'checkSvgScriptCallback' ) );
return $check->filterMatch;
@@ -984,6 +1048,9 @@ abstract class UploadBase {
/**
* @todo Replace this with a whitelist filter!
+ * @param $element string
+ * @param $attribs array
+ * @return bool
*/
public function checkSvgScriptCallback( $element, $attribs ) {
$strippedElement = $this->stripXmlNamespace( $element );
@@ -1054,7 +1121,7 @@ abstract class UploadBase {
}
- # use handler attribute with remote / data / script
+ # 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;
@@ -1082,6 +1149,10 @@ abstract class UploadBase {
return false; //No scripts detected
}
+ /**
+ * @param $name string
+ * @return string
+ */
private function stripXmlNamespace( $name ) {
// 'http://www.w3.org/2000/svg:script' -> 'script'
$parts = explode( ':', strtolower( $name ) );
@@ -1100,9 +1171,11 @@ abstract class UploadBase {
*/
public static function detectVirus( $file ) {
global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut;
+ wfProfileIn( __METHOD__ );
if ( !$wgAntivirus ) {
wfDebug( __METHOD__ . ": virus scanner disabled\n" );
+ wfProfileOut( __METHOD__ );
return null;
}
@@ -1110,7 +1183,8 @@ abstract class UploadBase {
wfDebug( __METHOD__ . ": unknown virus scanner: $wgAntivirus\n" );
$wgOut->wrapWikiMsg( "<div class=\"error\">\n$1\n</div>",
array( 'virus-badscanner', $wgAntivirus ) );
- return wfMsg( 'virus-unknownscanner' ) . " $wgAntivirus";
+ wfProfileOut( __METHOD__ );
+ return wfMessage( 'virus-unknownscanner' )->text() . " $wgAntivirus";
}
# look up scanner configuration
@@ -1152,17 +1226,21 @@ abstract class UploadBase {
wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" );
if ( $wgAntivirusRequired ) {
- return wfMsg( 'virus-scanfailed', array( $exitCode ) );
+ wfProfileOut( __METHOD__ );
+ return wfMessage( 'virus-scanfailed', array( $exitCode ) )->text();
} else {
+ wfProfileOut( __METHOD__ );
return null;
}
} elseif ( $mappedCode === AV_SCAN_ABORTED ) {
# scan failed because filetype is unknown (probably imune)
wfDebug( __METHOD__ . ": unsupported file type $file (code $exitCode).\n" );
+ wfProfileOut( __METHOD__ );
return null;
} elseif ( $mappedCode === AV_NO_VIRUS ) {
# no virus found
wfDebug( __METHOD__ . ": file passed virus scan.\n" );
+ wfProfileOut( __METHOD__ );
return false;
} else {
$output = trim( $output );
@@ -1179,6 +1257,7 @@ abstract class UploadBase {
}
wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output \n" );
+ wfProfileOut( __METHOD__ );
return $output;
}
}
@@ -1325,6 +1404,8 @@ abstract class UploadBase {
/**
* Helper function that checks whether the filename looks like a thumbnail
+ * @param $filename string
+ * @return bool
*/
public static function isThumbName( $filename ) {
$n = strrpos( $filename, '.' );
@@ -1387,13 +1468,20 @@ abstract class UploadBase {
return $info;
}
-
+ /**
+ * @param $error array
+ * @return Status
+ */
public function convertVerifyErrorToStatus( $error ) {
$code = $error['status'];
unset( $code['status'] );
return Status::newFatal( $this->getVerificationErrorCode( $code ), $error );
}
+ /**
+ * @param $forType null|string
+ * @return int
+ */
public static function getMaxUploadSize( $forType = null ) {
global $wgMaxUploadSize;