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;
}
}
*/
function execute() {
global $wgUser, $wgOut;
- global $wgDisableUploads;
+ global $wgEnableUploads;
/** Show an error message if file upload is disabled */
- if( $wgDisableUploads ) {
+ if( ! $wgEnableUploads ) {
$wgOut->addWikiText( wfMsg( 'uploaddisabled' ) );
return;
}
/** Various rights checks */
- if( ( $wgUser->getID() == 0 )
+ if( ( $wgUser->isAnon() )
OR $wgUser->isBlocked() ) {
$wgOut->errorpage( 'uploadnologin', 'uploadnologintext' );
return;
# 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;
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>';
}
}
global $wgUploadSizeWarning;
if ( $wgUploadSizeWarning && ( $this->mUploadSize > $wgUploadSizeWarning ) ) {
- $warning .= '<li>'.wfMsg( 'largefile' ).'</li>';
+ # TODO: Format $wgUploadSizeWarning to something that looks better than the raw byte
+ # value, perhaps add GB,MB and KB suffixes?
+ $warning .= '<li>'.wfMsg( 'largefile', $wgUploadSizeWarning, $this->mUploadSize ).'</li>';
}
if ( $this->mUploadSize == 0 ) {
$warning .= '<li>'.wfMsg( 'emptyfile' ).'</li>';
* Update the upload log and create the description page
* if it's a new file.
*/
- wfRecordUpload( $this->mUploadSaveName,
- $this->mUploadOldVersion,
- $this->mUploadSize,
- $this->mUploadDescription,
- $this->mUploadCopyStatus,
- $this->mUploadSource );
- $this->showSuccess();
+ $img = Image::newFromName( $this->mUploadSaveName );
+ $success = $img->recordUpload( $this->mUploadOldVersion,
+ $this->mUploadDescription,
+ $this->mUploadCopyStatus,
+ $this->mUploadSource );
+
+ if ( $success ) {
+ $this->showSuccess();
+ } else {
+ // Image::recordUpload() fails if the image went missing, which is
+ // unlikely, hence the lack of a specialised message
+ $wgOut->fileNotFoundError( $this->mUploadSaveName );
+ }
}
}
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 );
}
}
global $wgUser, $wgOut, $wgContLang;
$sk = $wgUser->getSkin();
- $ilink = $sk->makeMediaLink( $this->mUploadSaveName, Image::wfImageUrl( $this->mUploadSaveName ) );
- $dname = $wgContLang->getNsText( Namespace::getImage() ) . ':'.$this->mUploadSaveName;
+ $ilink = $sk->makeMediaLink( $this->mUploadSaveName, Image::imageUrl( $this->mUploadSaveName ) );
+ $dname = $wgContLang->getNsText( NS_IMAGE ) . ':'.$this->mUploadSaveName;
$dlink = $sk->makeKnownLink( $dname, $dname );
$wgOut->addHTML( '<h2>' . wfMsg( 'successfulupload' ) . "</h2>\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' );
if ( $wgUseCopyrightUpload )
{
$copyright = "
- <input type='hidden' name=\"wpUploadCopyStatus\" value=\"" . htmlspecialchars( $this->mUploadCopyStatus ) . "\" />
- <input type='hidden' name=\"wpUploadSource\" value=\"" . htmlspecialchars( $this->mUploadSource ) . "\" />
+ <input type='hidden' name='wpUploadCopyStatus' value=\"" . htmlspecialchars( $this->mUploadCopyStatus ) . "\" />
+ <input type='hidden' name='wpUploadSource' value=\"" . htmlspecialchars( $this->mUploadSource ) . "\" />
";
} else {
$copyright = "";
}
$wgOut->addHTML( "
- <form id=\"uploadwarning\" method=\"post\" enctype=\"multipart/form-data\"
- action=\"{$action}\">
- <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 ) . "\" />
+ <form id='uploadwarning' method='post' enctype='multipart/form-data' action='$action'>
+ <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'>
- <input tabindex='2' type='submit' name=\"wpUpload\" value=\"{$save}\" />
- </td><td align='left'>{$iw}</td></tr>
- <tr><td align='right'>
- <input tabindex='2' type='submit' name=\"wpReUpload\" value=\"{$reupload}\" />
- </td><td align='left'>{$reup}</td></tr></table></form>\n" );
+ <table border='0'>
+ <tr>
+ <tr>
+ <td align='right'>
+ <input tabindex='2' type='submit' name='wpUpload' value='$save' />
+ </td>
+ <td align='left'>$iw</td>
+ </tr>
+ <tr>
+ <td align='right'>
+ <input tabindex='2' type='submit' name='wpReUpload' value='{$reupload}' />
+ </td>
+ <td align='left'>$reup</td>
+ </tr>
+ </tr>
+ </table></form>\n" );
}
/**
function mainUploadForm( $msg='' ) {
global $wgOut, $wgUser, $wgLang, $wgUploadDirectory, $wgRequest;
global $wgUseCopyrightUpload;
+
+ $cols = intval($wgUser->getOption( 'cols' ));
+ $ew = $wgUser->getOption( 'editwidth' );
+ if ( $ew ) $ew = " style=\"width:100%\"";
+ else $ew = '';
if ( '' != $msg ) {
$sub = wfMsg( 'uploaderror' );
$source = "
<td align='right'>
- <input tabindex='3' type='checkbox' name=\"wpUploadAffirm\" value=\"1\" id=\"wpUploadAffirm\" />
- </td><td align='left'><label for=\"wpUploadAffirm\">{$ca}</label></td>
+ <input tabindex='3' type='checkbox' name='wpUploadAffirm' value='1' id='wpUploadAffirm' />
+ </td><td align='left'><label for='wpUploadAffirm'>{$ca}</label></td>
" ;
if ( $wgUseCopyrightUpload )
{
htmlspecialchars($this->mUploadCopyStatus). "\" size='40' /></td>
</tr><tr>
<td align='right'>". wfMsg ( 'filesource' ) . ":</td>
- <td><input tabindex='4' type='text' name=\"wpUploadSource\" value=\"" .
+ <td><input tabindex='4' type='text' name='wpUploadSource' value=\"" .
htmlspecialchars($this->mUploadSource). "\" size='40' /></td>
" ;
}
$wgOut->addHTML( "
- <form id=\"upload\" method=\"post\" enctype=\"multipart/form-data\"
- action=\"{$action}\">
+ <form id='upload' method='post' enctype='multipart/form-data' action='$action'>
<table border='0'><tr>
<td align='right'>{$fn}:</td><td align='left'>
- <input tabindex='1' type='file' name=\"wpUploadFile\" size='40' />
+ <input tabindex='1' type='file' name='wpUploadFile' size='40' />
</td></tr><tr>
<td align='right'>{$fd}:</td><td align='left'>
- <input tabindex='2' type='text' name=\"wpUploadDescription\" value=\""
- . htmlspecialchars( $this->mUploadDescription ) . "\" size='40' />
+ <textarea tabindex='2' name='wpUploadDescription' rows='6' cols='{$cols}'{$ew}>"
+ . htmlspecialchars( $this->mUploadDescription ) .
+ "</textarea>
</td></tr><tr>
{$source}
</tr>
<tr><td></td><td align='left'>
- <input tabindex='5' type='submit' name=\"wpUpload\" value=\"{$ulb}\" />
+ <input tabindex='5' type='submit' name='wpUpload' value=\"{$ulb}\" />
</td></tr></table></form>\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;
+ }
+
}
?>