protected $mDesiredDestName, $mDestName, $mRemoveTempFile, $mSourceType;
protected $mTitle = false, $mTitleError = 0;
protected $mFilteredName, $mFinalExtension;
+ protected $mLocalFile;
const SUCCESS = 0;
const OK = 0;
- const BEFORE_PROCESSING = 1;
- const LARGE_FILE_SERVER = 2;
const EMPTY_FILE = 3;
const MIN_LENGTH_PARTNAME = 4;
const ILLEGAL_FILENAME = 5;
- const PROTECTED_PAGE = 6;
const OVERWRITE_EXISTING_FILE = 7;
const FILETYPE_MISSING = 8;
const FILETYPE_BADTYPE = 9;
const VERIFICATION_ERROR = 10;
const UPLOAD_VERIFICATION_ERROR = 11;
- const UPLOAD_WARNING = 12;
- const INTERNAL_ERROR = 13;
- const MIN_LENGHT_PARTNAME = 14;
+ const HOOK_ABORTED = 11;
const SESSION_VERSION = 2;
* Create a form of UploadBase depending on wpSourceType and initializes it
*/
public static function createFromRequest( &$request, $type = null ) {
- $type = $type ? $type : $request->getVal( 'wpSourceType' );
+ $type = $type ? $type : $request->getVal( 'wpSourceType', 'File' );
if( !$type )
return null;
// Get the upload class
$type = ucfirst( $type );
- $className = 'UploadFrom' . $type;
- wfDebug( __METHOD__ . ": class name: $className\n" );
- if( !in_array( $type, self::$uploadHandlers ) )
- return null;
+
+ // Give hooks the chance to handle this request
+ $className = null;
+ wfRunHooks( 'UploadCreateFromRequest', array( $type, &$className ) );
+ if ( is_null( $className ) ) {
+ $className = 'UploadFrom' . $type;
+ wfDebug( __METHOD__ . ": class name: $className\n" );
+ if( !in_array( $type, self::$uploadHandlers ) )
+ return null;
+ }
// Check whether this upload class is enabled
if( !call_user_func( array( $className, 'isEnabled' ) ) )
public function isEmptyFile(){
return empty( $this->mFileSize );
}
+
+ /**
+ * getRealPath
+ * @param string $srcPath the source path
+ * @returns the real path if it was a virtual url
+ */
+ function getRealPath( $srcPath ){
+ $repo = RepoGroup::singleton()->getLocalRepo();
+ if ( $repo->isVirtualUrl( $srcPath ) ) {
+ return $repo->resolveVirtualUrl( $srcPath );
+ }
+ return $srcPath;
+ }
/**
* Verify whether the upload is sane.
*/
if( $this->isEmptyFile() )
return array( 'status' => self::EMPTY_FILE );
+
+ /**
+ * Look at the contents of the file; if we can recognize the
+ * type but it's corrupt or data of the wrong type, we should
+ * probably not accept it.
+ */
+ $verification = $this->verifyFile();
+ if( $verification !== true ) {
+ if( !is_array( $verification ) )
+ $verification = array( $verification );
+ return array( 'status' => self::VERIFICATION_ERROR,
+ 'details' => $verification );
+ }
+
$nt = $this->getTitle();
if( is_null( $nt ) ) {
$result = array( 'status' => $this->mTitleError );
if( $overwrite !== true )
return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite );
- /**
- * Look at the contents of the file; if we can recognize the
- * type but it's corrupt or data of the wrong type, we should
- * probably not accept it.
- */
- $verification = $this->verifyFile( $this->mTempPath );
-
- if( $verification !== true ) {
- if( !is_array( $verification ) )
- $verification = array( $verification );
- $verification['status'] = self::VERIFICATION_ERROR;
- return $verification;
- }
-
$error = '';
if( !wfRunHooks( 'UploadVerification',
array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
- return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error );
+ // This status needs another name...
+ return array( 'status' => self::HOOK_ABORTED, 'error' => $error );
}
- return self::OK;
+ return array( 'status' => self::OK );
}
/**
* @param string $tmpfile the full path of the temporary file to verify
* @return mixed true of the file is verified, a string or array otherwise.
*/
- protected function verifyFile( $tmpfile ) {
+ protected function verifyFile() {
$this->mFileProps = File::getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
$this->checkMacBinary();
#magically determine mime type
$magic = MimeMagic::singleton();
- $mime = $magic->guessMimeType( $tmpfile, false );
+ $mime = $magic->guessMimeType( $this->mTempPath, false );
#check mime type, if desired
global $wgVerifyMimeType;
return array( 'filetype-badmime', $mime );
# Check IE type
- $fp = fopen( $tmpfile, 'rb' );
+ $fp = fopen( $this->mTempPath, 'rb' );
$chunk = fread( $fp, 256 );
fclose( $fp );
$extMime = $magic->guessTypesForExtension( $this->mFinalExtension );
- $ieTypes = $magic->getIEMimeTypes( $tmpfile, $chunk, $extMime );
+ $ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
foreach ( $ieTypes as $ieType ) {
if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
return array( 'filetype-bad-ie-mime', $ieType );
}
#check for htmlish code and javascript
- if( self::detectScript( $tmpfile, $mime, $this->mFinalExtension ) ) {
+ if( self::detectScript( $this->mTempPath, $mime, $this->mFinalExtension ) ) {
return 'uploadscripted';
}
if( $this->mFinalExtension == 'svg' || $mime == 'image/svg+xml' ) {
- if( self::detectScriptInSvg( $tmpfile ) ) {
+ if( self::detectScriptInSvg( $this->mTempPath ) ) {
return 'uploadscripted';
}
}
/**
* Scan the uploaded file for viruses
*/
- $virus = $this->detectVirus( $tmpfile );
+ $virus = $this->detectVirus( $this->mTempPath );
if ( $virus ) {
return array( 'uploadvirus', $virus );
}
* @return array Array of warnings
*/
public function checkWarnings() {
- $warning = array();
+ $warnings = array();
$localFile = $this->getLocalFile();
$filename = $localFile->getName();
* but ignore things like ucfirst() and spaces/underscore things
*/
$comparableName = str_replace( ' ', '_', $this->mDesiredDestName );
- global $wgCapitalLinks, $wgContLang;
- if ( $wgCapitalLinks ) {
- $comparableName = $wgContLang->ucfirst( $comparableName );
- }
+ $comparableName = Title::capitalize( $comparableName, NS_FILE );
+
if( $this->mDesiredDestName != $filename && $comparableName != $filename )
- $warning['badfilename'] = $filename;
+ $warnings['badfilename'] = $filename;
// Check whether the file extension is on the unwanted list
global $wgCheckFileExtensions, $wgFileExtensions;
if ( $wgCheckFileExtensions ) {
if ( !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) )
- $warning['filetype-unwanted-type'] = $this->mFinalExtension;
+ $warnings['filetype-unwanted-type'] = $this->mFinalExtension;
}
global $wgUploadSizeWarning;
if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) )
- $warning['large-file'] = $wgUploadSizeWarning;
+ $warnings['large-file'] = $wgUploadSizeWarning;
if ( $this->mFileSize == 0 )
- $warning['emptyfile'] = true;
+ $warnings['emptyfile'] = true;
$exists = self::getExistsWarning( $localFile );
if( $exists !== false )
- $warning['exists'] = $exists;
-
- // Check whether this may be a thumbnail
- if( $exists !== false && $exists[0] != 'thumb'
- && self::isThumbName( $filename ) ){
- // Make the title
- $nt = $this->getTitle();
- $warning['file-thumbnail-no'] = substr( $filename, 0,
- strpos( $nt->getText() , '-' ) +1 );
- }
+ $warnings['exists'] = $exists;
// Check dupes against existing files
$hash = File::sha1Base36( $this->mTempPath );
unset( $dupes[$key] );
}
if( $dupes )
- $warning['duplicate'] = $dupes;
+ $warnings['duplicate'] = $dupes;
// Check dupes against archives
$archivedImage = new ArchivedFile( null, 0, "{$hash}.{$this->mFinalExtension}" );
if ( $archivedImage->getID() > 0 )
- $warning['duplicate-archive'] = $archivedImage->getName();
-
- $filenamePrefixBlacklist = self::getFilenamePrefixBlacklist();
- foreach( $filenamePrefixBlacklist as $prefix ) {
- if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
- $warning['filename-bad-prefix'] = $prefix;
- break;
- }
- }
-
- # If the file existed before and was deleted, warn the user of this
- # Don't bother doing so if the file exists now, however
- if( $localFile->wasDeleted() && !$localFile->exists() )
- $warning['filewasdeleted'] = $localFile->getTitle();
+ $warnings['duplicate-archive'] = $archivedImage->getName();
- return $warning;
+ return $warnings;
}
/**
* can accumulate in the temp directory.
*
* @param string $saveName - the destination filename
- * @param string $tempName - the source temporary file to save
+ * @param string $tempSrc - the source temporary file to save
* @return string - full path the stashed file, or false on failure
* @access private
*/
- protected function saveTempUploadedFile( $saveName, $tempName ) {
+ protected function saveTempUploadedFile( $saveName, $tempSrc ) {
$repo = RepoGroup::singleton()->getLocalRepo();
- $status = $repo->storeTemp( $saveName, $tempName );
- return $status;
- }
-
- /**
- * Append a file to a stashed file.
- *
- * @param string $srcPath Path to file to append from
- * @param string $toAppendPath Path to file to append to
- * @return Status Status
- */
- public function appendToUploadFile( $srcPath, $toAppendPath ){
- $repo = RepoGroup::singleton()->getLocalRepo();
- $status = $repo->append( $srcPath, $toAppendPath );
+ $status = $repo->storeTemp( $saveName, $tempSrc );
return $status;
}
return $key;
}
- /**
- * Remove a temporarily kept file stashed by saveTempUploadedFile().
- * @return success
- */
- public function unsaveUploadedFile() {
- $repo = RepoGroup::singleton()->getLocalRepo();
- $success = $repo->freeTemp( $this->mTempPath );
- return $success;
- }
/**
* If we've modified the upload file we need to manually remove it
$match = $magic->isMatchingExtension( $extension, $mime );
- if ( $match === NULL ) {
+ if ( $match === null ) {
wfDebug( __METHOD__ . ": no file extension known for mime type $mime, passing file\n" );
return true;
} elseif( $match === true ) {
elseif( substr( $chunk, 0, 2 ) == "\xff\xfe" )
$enc = "UTF-16LE";
else
- $enc = NULL;
+ $enc = null;
if( $enc )
$chunk = iconv( $enc, "ASCII//IGNORE", $chunk );
* when served with a generic content-type.
*/
$tags = array(
- '<a',
+ '<a href',
'<body',
'<head',
'<html', #also in safari
if ( !$wgAntivirus ) {
wfDebug( __METHOD__ . ": virus scanner disabled\n" );
- return NULL;
+ return null;
}
if ( !$wgAntivirusSetup[$wgAntivirus] ) {
#NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
# that does not seem to be worth the pain.
# Ask me (Duesentrieb) about it if it's ever needed.
- $output = array();
- if ( wfIsWindows() ) {
- exec( "$command", $output, $exitCode );
- } else {
- exec( "$command 2>&1", $output, $exitCode );
- }
+ $output = wfShellExec( "$command 2>&1", $exitCode );
# map exit code to AV_xxx constants.
$mappedCode = $exitCode;
if ( $wgAntivirusRequired ) {
return wfMsg( 'virus-scanfailed', array( $exitCode ) );
} else {
- return NULL;
+ return null;
}
} else if ( $mappedCode === AV_SCAN_ABORTED ) {
# scan failed because filetype is unknown (probably imune)
wfDebug( __METHOD__ . ": unsupported file type $file (code $exitCode).\n" );
- return NULL;
+ return null;
} else if ( $mappedCode === AV_NO_VIRUS ) {
# no virus found
wfDebug( __METHOD__ . ": file passed virus scan.\n" );
return false;
} else {
- $output = join( "\n", $output );
$output = trim( $output );
if ( !$output ) {
global $wgUser;
// First check whether the local file can be overwritten
$file = $this->getLocalFile();
- if( $file->exists() )
+ if( $file->exists() ) {
if( !self::userCanReUpload( $wgUser, $file ) )
return 'fileexists-forbidden';
+ else
+ return true;
+ }
- // Check shared conflicts
- $file = wfFindFile( $file->getName() );
- if ( $file && ( !$wgUser->isAllowed( 'reupload' ) ||
- !$wgUser->isAllowed( 'reupload-shared' ) ) )
+ /* Check shared conflicts: if the local file does not exist, but
+ * wfFindFile finds a file, it exists in a shared repository.
+ */
+ $file = wfFindFile( $this->getTitle() );
+ if ( $file && !$wgUser->isAllowed( 'reupload-shared' ) )
return 'fileexists-shared-forbidden';
return true;
*/
public static function getExistsWarning( $file ) {
if( $file->exists() )
- return array( 'exists', $file );
+ return array( 'warning' => 'exists', 'file' => $file );
if( $file->getTitle()->getArticleID() )
- return array( 'page-exists', $file );
-
+ return array( 'warning' => 'page-exists', 'file' => $file );
+
+ if ( $file->wasDeleted() && !$file->exists() )
+ return array( 'warning' => 'was-deleted', 'file' => $file );
+
if( strpos( $file->getName(), '.' ) == false ) {
$partname = $file->getName();
- $rawExtension = '';
+ $extension = '';
} else {
$n = strrpos( $file->getName(), '.' );
- $rawExtension = substr( $file->getName(), $n + 1 );
+ $extension = substr( $file->getName(), $n + 1 );
$partname = substr( $file->getName(), 0, $n );
}
+ $normalizedExtension = File::normalizeExtension( $extension );
- if ( $rawExtension != $file->getExtension() ) {
+ if ( $normalizedExtension != $extension ) {
// We're not using the normalized form of the extension.
// Normal form is lowercase, using most common of alternate
// extensions (eg 'jpg' rather than 'JPEG').
//
// Check for another file using the normalized form...
- $nt_lc = Title::makeTitle( NS_FILE, $partname . '.' . $file->getExtension() );
+ $nt_lc = Title::makeTitle( NS_FILE, "{$partname}.{$normalizedExtension}" );
$file_lc = wfLocalFile( $nt_lc );
if( $file_lc->exists() )
- return array( 'exists-normalized', $file_lc );
+ return array( 'warning' => 'exists-normalized', 'file' => $file, 'normalizedFile' => $file_lc );
}
if ( self::isThumbName( $file->getName() ) ) {
# Check for filenames like 50px- or 180px-, these are mostly thumbnails
- $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $rawExtension );
+ $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) +1 ) . '.' . $extension, NS_FILE );
$file_thb = wfLocalFile( $nt_thb );
if( $file_thb->exists() )
- return array( 'thumb', $file_thb );
+ return array( 'warning' => 'thumb', 'file' => $file, 'thumbFile' => $file_thb );
+ else
+ // File does not exist, but we just don't like the name
+ return array( 'warning' => 'thumb-name', 'file' => $file, 'thumbFile' => $file_thb );
}
+
+
+ foreach( self::getFilenamePrefixBlacklist() as $prefix ) {
+ if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix )
+ return array( 'warning' => 'bad-prefix', 'file' => $file, 'prefix' => $prefix );
+ }
+
+
return false;
}