var $mUploadAffirm, $mUploadFile, $mUploadDescription, $mIgnoreWarning;
var $mUploadSaveName, $mUploadTempName, $mUploadSize, $mUploadOldVersion;
var $mUploadCopyStatus, $mUploadSource, $mReUpload, $mAction, $mUpload;
- var $mOname, $mSessionKey;
- /**#@- */
+ var $mOname, $mSessionKey, $mStashed;
+ /**#@-*/
/**
* Constructor : initialise object
$this->mUploadTempName = $data['mUploadTempName'];
$this->mUploadSize = $data['mUploadSize'];
$this->mOname = $data['mOname'];
+ $this->mStashed = true;
} else {
/**
*Check for a newly uploaded file.
$this->mUploadSize = $request->getFileSize( 'wpUploadFile' );
$this->mOname = $request->getFileName( 'wpUploadFile' );
$this->mSessionKey = false;
+ $this->mStashed = false;
}
}
}
/** Various rights checks */
- if( ( $wgUser->getID() == 0 )
+ if( ( $wgUser->isAnon() )
OR $wgUser->isBlocked() ) {
$wgOut->errorpage( 'uploadnologin', 'uploadnologintext' );
return;
global $wgUploadDirectory;
global $wgUseCopyrightUpload, $wgCheckCopyrightUpload;
+ /**
+ * If there was no filename or a zero size given, give up quick.
+ */
+ if( ( trim( $this->mOname ) == '' ) || empty( $this->mUploadSize ) ) {
+ return $this->mainUploadForm('<li>'.wfMsg( 'emptyfile' ).'</li>');
+ }
+
/**
* When using detailed copyright, if user filled field, assume he
* confirmed the upload
/** User need to confirm his upload */
if( !$this->mUploadAffirm ) {
- $this->mainUploadForm( WfMsg( 'noaffirmation' ) );
+ $this->mainUploadForm( wfMsg( 'noaffirmation' ) );
return;
}
- if ( $this->mOname == '' && !isset($this->mUploadSaveName) ) {
- // no filename given!
- return $this->uploadError('<li>'.wfMsg( 'emptyfile' ).'</li>');
- }
-
# Chop off any directories in the given filename
$basename = basename( $this->mOname );
- if( preg_match( '/^(.*)\.([^.]*)$/', $basename, $matches ) ) {
- $partname = $matches[1];
- $ext = $matches[2];
+ /**
+ * We'll want to blacklist against *any* 'extension', and use
+ * only the final one for the whitelist.
+ */
+ list( $partname, $ext ) = $this->splitExtensions( $basename );
+ if( count( $ext ) ) {
+ $finalExt = $ext[count( $ext ) - 1];
} else {
- $partname = $basename;
- $ext = '';
+ $finalExt = '';
}
-
+ $fullExt = implode( '.', $ext );
+
if ( strlen( $partname ) < 3 ) {
$this->mainUploadForm( wfMsg( 'minlength' ) );
return;
* Filter out illegal characters, and try to make a legible name
* out of it. We'll strip some silently that Title would die on.
*/
- $filtered = preg_replace ( "/[^".Title::legalChars()."]/", '-', $basename );
+ $filtered = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $basename );
$nt = Title::newFromText( $filtered );
if( is_null( $nt ) ) {
return $this->uploadError( wfMsg( 'illegalfilename', htmlspecialchars( $filtered ) ) );
}
- $nt->setNamespace( NS_IMAGE );
+ $nt =& Title::makeTitle( NS_IMAGE, $nt->getDBkey() );
$this->mUploadSaveName = $nt->getDBkey();
/**
/* Don't allow users to override the blacklist */
global $wgStrictFileExtensions;
global $wgFileExtensions, $wgFileBlacklist;
- if( $this->checkFileExtension( $ext, $wgFileBlacklist ) ||
- ($wgStrictFileExtensions && !$this->checkFileExtension( $ext, $wgFileExtensions ) ) ) {
- return $this->uploadError( wfMsg( 'badfiletype', htmlspecialchars( $ext ) ) );
+ if( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
+ ($wgStrictFileExtensions &&
+ !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) {
+ return $this->uploadError( wfMsg( 'badfiletype', htmlspecialchars( $fullExt ) ) );
}
/**
* type but it's corrupt or data of the wrong type, we should
* probably not accept it.
*/
- if( !$this->verify( $this->mUploadTempName, $ext ) ) {
+ if( !$this->mStashed && !$this->verify( $this->mUploadTempName, $finalExt ) ) {
return $this->uploadError( wfMsg( 'uploadcorrupt' ) );
}
global $wgCheckFileExtensions;
if ( $wgCheckFileExtensions ) {
- if ( ! $this->checkFileExtension( $ext, $wgFileExtensions ) ) {
- $warning .= '<li>'.wfMsg( 'badfiletype', htmlspecialchars( $ext ) ).'</li>';
+ if ( ! $this->checkFileExtension( $finalExt, $wgFileExtensions ) ) {
+ $warning .= '<li>'.wfMsg( 'badfiletype', htmlspecialchars( $fullExt ) ).'</li>';
}
}
$this->mUploadDescription,
$this->mUploadCopyStatus,
$this->mUploadSource );
+
+ /* refresh image metadata cache */
+ new Image( $this->mUploadSaveName, true );
+
$this->showSuccess();
}
}
if( is_file( $this->mSavedFile ) ) {
$this->mUploadOldVersion = gmdate( 'YmdHis' ) . "!{$saveName}";
+ wfSuppressWarnings();
+ $success = rename( $this->mSavedFile, "${archive}/{$this->mUploadOldVersion}" );
+ wfRestoreWarnings();
- if( !rename( $this->mSavedFile, "${archive}/{$this->mUploadOldVersion}" ) ) {
+ if( ! $success ) {
$wgOut->fileRenameError( $this->mSavedFile,
"${archive}/{$this->mUploadOldVersion}" );
return false;
}
if( $useRename ) {
- if( !rename( $tempName, $this->mSavedFile ) ) {
+ wfSuppressWarnings();
+ $success = rename( $tempName, $this->mSavedFile );
+ wfRestoreWarnings();
+
+ if( ! $success ) ) {
$wgOut->fileCopyError( $tempName, $this->mSavedFile );
return false;
}
} else {
- if( !move_uploaded_file( $tempName, $this->mSavedFile ) ) {
+ wfSuppressWarnings();
+ $success = move_uploaded_file( $tempName, $this->mSavedFile );
+ wfRestoreWarnings();
+
+ if( ! $success ) {
$wgOut->fileCopyError( $tempName, $this->mSavedFile );
return false;
}
* @access private
*/
function saveTempUploadedFile( $saveName, $tempName ) {
- global $wgOut;
-
+ global $wgOut;
$archive = wfImageArchiveDir( $saveName, 'temp' );
$stash = $archive . '/' . gmdate( "YmdHis" ) . '!' . $saveName;
* @return int
* @access private
*/
- function stashSession() {
+ function stashSession() {
$stash = $this->saveTempUploadedFile(
$this->mUploadSaveName, $this->mUploadTempName );
* @access private
*/
function unsaveUploadedFile() {
- if ( ! @unlink( $this->mUploadTempName ) ) {
+ wfSuppressWarnings();
+ $success = unlink( $this->mUploadTempName );
+ wfRestoreWarnings();
+ if ( ! $success ) {
$wgOut->fileDeleteError( $this->mUploadTempName );
}
}
$sk = $wgUser->getSkin();
$ilink = $sk->makeMediaLink( $this->mUploadSaveName, Image::wfImageUrl( $this->mUploadSaveName ) );
- $dname = $wgContLang->getNsText( Namespace::getImage() ) . ':'.$this->mUploadSaveName;
+ $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mUploadSaveName;
$dlink = $sk->makeKnownLink( $dname, $dname );
$wgOut->addHTML( '<h2>' . wfMsg( 'successfulupload' ) . "</h2>\n" );
global $wgOut;
$sub = wfMsg( 'uploadwarning' );
$wgOut->addHTML( "<h2>{$sub}</h2>\n" );
- $wgOut->addHTML( "<h4 style='error'>{$error}</h4>\n" );
+ $wgOut->addHTML( "<h4 class='error'>{$error}</h4>\n" );
}
/**
$sub = wfMsg( 'uploadwarning' );
$wgOut->addHTML( "<h2>{$sub}</h2>\n" );
- $wgOut->addHTML( "<ul class='warning'>{$warning}</ul><br/>\n" );
+ $wgOut->addHTML( "<ul class='warning'>{$warning}</ul><br />\n" );
$save = wfMsg( 'savefile' );
$reupload = wfMsg( 'reupload' );
<input type=hidden name=\"wpUploadAffirm\" value=\"1\" />
<input type=hidden name=\"wpIgnoreWarning\" value=\"1\" />
<input type=hidden name=\"wpSessionKey\" value=\"" . htmlspecialchars( $this->mSessionKey ) . "\" />
+ <input type=hidden name=\"wpUploadDescription\" value=\"" . htmlspecialchars( $this->mUploadDescription ) . "\" />
{$copyright}
<table border='0'><tr>
<tr><td align='right'>
if ( '' != $msg ) {
$sub = wfMsg( 'uploaderror' );
$wgOut->addHTML( "<h2>{$sub}</h2>\n" .
- "<h4 style='error'>{$msg}</h4>\n" );
+ "<h4 class='error'>{$msg}</h4>\n" );
} else {
$sub = wfMsg( 'uploadfile' );
$wgOut->addHTML( "<h2>{$sub}</h2>\n" );
/* -------------------------------------------------------------- */
+ /**
+ * Split a file into a base name and all dot-delimited 'extensions'
+ * on the end. Some web server configurations will fall back to
+ * earlier pseudo-'extensions' to determine type and execute
+ * scripts, so the blacklist needs to check them all.
+ *
+ * @return array
+ */
+ function splitExtensions( $filename ) {
+ $bits = explode( '.', $filename );
+ $basename = array_shift( $bits );
+ return array( $basename, $bits );
+ }
+
/**
* Perform case-insensitive match against a list of file extensions.
* Returns true if the extension is in the list.
return in_array( strtolower( $ext ), $list );
}
+ /**
+ * Perform case-insensitive match against a list of file extensions.
+ * Returns true if any of the extensions are in the list.
+ *
+ * @param array $ext
+ * @param array $list
+ * @return bool
+ */
+ function checkFileExtensionList( $ext, $list ) {
+ foreach( $ext as $e ) {
+ if( in_array( strtolower( $e ), $list ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Returns false if the file is of a known type but can't be recognized,
* indicating a corrupt file.
* @return bool
*/
function verify( $tmpfile, $extension ) {
- if( $this->triggersIEbug( $tmpfile ) ) {
+ if( $this->triggersIEbug( $tmpfile ) ||
+ $this->triggersSafariBug( $tmpfile ) ) {
return false;
}
return true;
}
- $data = @getimagesize( $tmpfile );
+ wfSuppressWarnings();
+ $data = getimagesize( $tmpfile );
+ wfRestoreWarnings();
if( false === $data ) {
# Didn't recognize the image type.
# Either the image is corrupt or someone's slipping us some
*/
function triggersIEbug( $filename ) {
$file = fopen( $filename, 'rb' );
- $chunk = strtolower( fread( $file, 200 ) );
+ $chunk = strtolower( fread( $file, 256 ) );
fclose( $file );
- $tags = array( '<html', '<head', '<body', '<script' );
+ $tags = array(
+ '<body',
+ '<head',
+ '<html',
+ '<img',
+ '<pre',
+ '<script',
+ '<table',
+ '<title' );
foreach( $tags as $tag ) {
if( false !== strpos( $chunk, $tag ) ) {
return true;
}
return false;
}
+
+ /**
+ * Apple's Safari browser performs some unsafe file type autodetection
+ * which can cause legitimate files to be interpreted as HTML if the
+ * web server is not correctly configured to send the right content-type
+ * (or if you're really uploading plain text and octet streams!)
+ *
+ * Returns true if Safari would mistake the given file for HTML
+ * when served with a generic content-type.
+ *
+ * @param string $filename
+ * @return bool
+ */
+ function triggersSafariBug( $filename ) {
+ $file = fopen( $filename, 'rb' );
+ $chunk = strtolower( fread( $file, 1024 ) );
+ fclose( $file );
+
+ $tags = array(
+ '<html',
+ '<script',
+ '<title' );
+ foreach( $tags as $tag ) {
+ if( false !== strpos( $chunk, $tag ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
?>