var $mUploadAffirm, $mUploadFile, $mUploadDescription, $mIgnoreWarning;
var $mUploadSaveName, $mUploadTempName, $mUploadSize, $mUploadOldVersion;
var $mUploadCopyStatus, $mUploadSource, $mReUpload, $mAction, $mUpload;
- var $mOname, $mSessionKey;
- /**#@- */
+ var $mOname, $mSessionKey, $mStashed, $mDestFile;
+ /**#@-*/
/**
* Constructor : initialise object
* @param $request Data posted.
*/
function UploadForm( &$request ) {
+ $this->mDestFile = $request->getText( 'wpDestFile' );
+
if( !$request->wasPosted() ) {
- # GET requests just give the main form; no data.
+ # GET requests just give the main form; no data except wpDestfile.
return;
}
-
+
$this->mUploadAffirm = $request->getCheck( 'wpUploadAffirm' );
$this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning');
$this->mReUpload = $request->getCheck( 'wpReUpload' );
$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, $wgUploadDirectory;
/** 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 )
- OR $wgUser->isBlocked() ) {
+ if( !$wgUser->isAllowed( 'upload' ) || $wgUser->isBlocked() ) {
$wgOut->errorpage( 'uploadnologin', 'uploadnologintext' );
return;
}
return;
}
+ /** Check if the image directory is writeable, this is a common mistake */
+ if ( !is_writeable( $wgUploadDirectory ) ) {
+ $wgOut->addWikiText( wfMsg( 'upload_directory_read_only', $wgUploadDirectory ) );
+ return;
+ }
+
if( $this->mReUpload ) {
$this->unsaveUploadedFile();
$this->mainUploadForm();
*/
function processUpload() {
global $wgUser, $wgOut, $wgLang, $wgContLang;
- global $wgUploadDirectory;
+ global $wgUploadDirectory, $wgCopyrightAffirmation;
global $wgUseCopyrightUpload, $wgCheckCopyrightUpload;
/**
* If there was no filename or a zero size given, give up quick.
*/
- if( ( trim( $this->mOname ) == '' ) || empty( $this->mUploadSize ) ) {
+ if( trim( $this->mOname ) == '' || empty( $this->mUploadSize ) ) {
return $this->mainUploadForm('<li>'.wfMsg( 'emptyfile' ).'</li>');
}
-
+
+ if ( !$wgCopyrightAffirmation )
+ $this->mUploadAffirm = true;
/**
* When using detailed copyright, if user filled field, assume he
* confirmed the upload
}
# Chop off any directories in the given filename
- $basename = basename( $this->mOname );
+ if ( $this->mDestFile ) {
+ $basename = basename( $this->mDestFile );
+ } else {
+ $basename = basename( $this->mOname );
+ }
/**
* We'll want to blacklist against *any* 'extension', and use
return $this->uploadError( wfMsg( 'protectedpage' ) );
}
- /* Don't allow users to override the blacklist */
+ /* Don't allow users to override the blacklist (check file extension) */
global $wgStrictFileExtensions;
global $wgFileExtensions, $wgFileBlacklist;
if( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) ||
* type but it's corrupt or data of the wrong type, we should
* probably not accept it.
*/
- if( !$this->verify( $this->mUploadTempName, $finalExt ) ) {
- return $this->uploadError( wfMsg( 'uploadcorrupt' ) );
+ if( !$this->mStashed ) {
+ $veri= $this->verify($this->mUploadTempName, $finalExt);
+
+ if( $veri !== true ) { //it's a wiki error...
+ return $this->uploadError( $veri->toString() );
+ }
}
/**
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 );
+ }
}
}
function saveUploadedFile( $saveName, $tempName, $useRename = false ) {
global $wgUploadDirectory, $wgOut;
+ $fname= "SpecialUpload::saveUploadedFile";
+
$dest = wfImageDir( $saveName );
$archive = wfImageArchiveDir( $saveName );
$this->mSavedFile = "{$dest}/{$saveName}";
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;
}
- } else {
+ else wfDebug("$fname: moved file ".$this->mSavedFile." to ${archive}/{$this->mUploadOldVersion}\n");
+ }
+ else {
$this->mUploadOldVersion = '';
}
if( $useRename ) {
- if( !rename( $tempName, $this->mSavedFile ) ) {
+ wfSuppressWarnings();
+ $success = rename( $tempName, $this->mSavedFile );
+ wfRestoreWarnings();
+
+ if( ! $success ) {
$wgOut->fileCopyError( $tempName, $this->mSavedFile );
return false;
+ } else {
+ wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
}
} 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;
}
+ else wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
}
+
chmod( $this->mSavedFile, 0644 );
return true;
}
* @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 ) . "\" />
+ <input type='hidden' name='wpDestFile' value=\"" . htmlspecialchars( $this->mDestFile ) . "\" />
{$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;
+ global $wgUseCopyrightUpload, $wgCopyrightAffirmation;
+
+ $cols = intval($wgUser->getOption( 'cols' ));
+ $ew = $wgUser->getOption( 'editwidth' );
+ if ( $ew ) $ew = " style=\"width:100%\"";
+ else $ew = '';
if ( '' != $msg ) {
$sub = wfMsg( 'uploaderror' );
$wgOut->addHTML( "<h2>{$sub}</h2>\n" .
"<h4 class='error'>{$msg}</h4>\n" );
- } else {
- $sub = wfMsg( 'uploadfile' );
- $wgOut->addHTML( "<h2>{$sub}</h2>\n" );
}
$wgOut->addWikiText( wfMsg( 'uploadtext' ) );
$sk = $wgUser->getSkin();
- $fn = wfMsg( 'filename' );
+
+ $sourcefilename = wfMsg( 'sourcefilename' );
+ $destfilename = wfMsg( 'destfilename' );
+
$fd = wfMsg( 'filedesc' );
$ulb = wfMsg( 'uploadbtn' );
$titleObj = Title::makeTitle( NS_SPECIAL, 'Upload' );
$action = $titleObj->escapeLocalURL();
+ $encDestFile = htmlspecialchars( $this->mDestFile );
+ $source = null;
+
+ if ( $wgCopyrightAffirmation ) {
$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 )
{
$source = "
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' />
+
+ <td align='right'>{$sourcefilename}:</td><td align='left'>
+ <input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' onchange='fillDestFilename()' size='40' />
+ </td></tr><tr>
+
+ <td align='right'>{$destfilename}:</td><td align='left'>
+ <input tabindex='1' type='text' name='wpDestFile' id='wpDestFile' size='40' value=\"$encDestFile\" />
</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" );
}
}
/**
- * Returns false if the file is of a known type but can't be recognized,
- * indicating a corrupt file.
- * Returns true otherwise; unknown file types are not checked if given
- * with an unrecognized extension.
+ * Verifies that it's ok to include the uploaded file
*
- * @param string $tmpfile Pathname to the temporary upload file
+ * @param string $tmpfile the full path opf the temporary file to verify
* @param string $extension The filename extension that the file is to be served with
- * @return bool
+ * @return mixed true of the file is verified, a WikiError object otherwise.
*/
function verify( $tmpfile, $extension ) {
- if( $this->triggersIEbug( $tmpfile ) ) {
- return false;
- }
+ #magically determine mime type
+ $magic=& wfGetMimeMagic();
+ $mime= $magic->guessMimeType($tmpfile,false);
- $fname = 'SpecialUpload::verify';
- $mergeExtensions = array(
- 'jpg' => 'jpeg',
- 'tif' => 'tiff' );
- $extensionTypes = array(
- # See http://www.php.net/getimagesize
- 1 => 'gif',
- 2 => 'jpeg',
- 3 => 'png',
- 4 => 'swf',
- 5 => 'psd',
- 6 => 'bmp',
- 7 => 'tiff',
- 8 => 'tiff',
- 9 => 'jpc',
- 10 => 'jp2',
- 11 => 'jpx',
- 12 => 'jb2',
- 13 => 'swc',
- 14 => 'iff',
- 15 => 'wbmp',
- 16 => 'xbm' );
-
- $extension = strtolower( $extension );
- if( isset( $mergeExtensions[$extension] ) ) {
- $extension = $mergeExtensions[$extension];
- }
- wfDebug( "$fname: Testing file '$tmpfile' with given extension '$extension'\n" );
-
- if( !in_array( $extension, $extensionTypes ) ) {
- # Not a recognized image type. We don't know how to verify these.
- # They're allowed by policy or they wouldn't get this far, so we'll
- # let them slide for now.
- wfDebug( "$fname: Unknown extension; passing.\n" );
- return true;
- }
+ $fname= "SpecialUpload::verify";
- $data = @getimagesize( $tmpfile );
- if( false === $data ) {
- # Didn't recognize the image type.
- # Either the image is corrupt or someone's slipping us some
- # bogus data such as HTML+JavaScript trying to take advantage
- # of an Internet Explorer security flaw.
- wfDebug( "$fname: getimagesize() doesn't recognize the file; rejecting.\n" );
- return false;
- }
+ #check mime type, if desired
+ global $wgVerifyMimeType;
+ if ($wgVerifyMimeType) {
+
+ #check mime type against file extension
+ if( !$this->verifyExtension( $mime, $extension ) ) {
+ return new WikiErrorMsg( 'uploadcorrupt' );
+ }
- $imageType = $data[2];
- if( !isset( $extensionTypes[$imageType] ) ) {
- # Now we're kind of confused. Perhaps new image types added
- # to PHP's support that we don't know about.
- # We'll let these slide for now.
- wfDebug( "$fname: getimagesize() knows the file, but we don't recognize the type; passing.\n" );
- return true;
+ #check mime type blacklist
+ global $wgMimeTypeBlacklist;
+ if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist)
+ && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
+ return new WikiErrorMsg( 'badfiletype', htmlspecialchars( $mime ) );
+ }
+ }
+
+ #check for htmlish code and javascript
+ if( $this->detectScript ( $tmpfile, $mime ) ) {
+ return new WikiErrorMsg( 'uploadscripted' );
}
- $ext = strtolower( $extension );
- if( $extension != $extensionTypes[$imageType] ) {
- # The given filename extension doesn't match the
- # file type. Probably just a mistake, but it's a stupid
- # one and we shouldn't let it pass. KILL THEM!
- wfDebug( "$fname: file extension does not match recognized type; rejecting.\n" );
- return false;
+ /**
+ * Scan the uploaded file for viruses
+ */
+ $virus= $this->detectVirus($tmpfile);
+ if ( $virus ) {
+ return new WikiErrorMsg( 'uploadvirus', htmlspecialchars($virus) );
}
wfDebug( "$fname: all clear; passing.\n" );
}
/**
- * Internet Explorer for Windows performs some really stupid file type
- * autodetection which can cause it to interpret valid image files as HTML
- * and potentially execute JavaScript, creating a cross-site scripting
- * attack vectors.
- *
- * Returns true if IE is likely to mistake the given file for HTML.
+ * Checks if the mime type of the uploaded file matches the file extension.
*
- * @param string $filename
+ * @param string $mime the mime type of the uploaded file
+ * @param string $extension The filename extension that the file is to be served with
* @return bool
*/
- function triggersIEbug( $filename ) {
- $file = fopen( $filename, 'rb' );
- $chunk = strtolower( fread( $file, 200 ) );
- fclose( $file );
+ function verifyExtension( $mime, $extension ) {
+ $fname = 'SpecialUpload::verifyExtension';
+
+ if (!$mime || $mime=="unknown" || $mime=="unknown/unknown") {
+ wfDebug( "$fname: passing file with unknown mime type\n" );
+ return true;
+ }
+
+ $magic=& wfGetMimeMagic();
- $tags = array( '<html', '<head', '<body', '<script' );
+ $match= $magic->isMatchingExtension($extension,$mime);
+
+ if ($match===NULL) {
+ wfDebug( "$fname: no file extension known for mime type $mime, passing file\n" );
+ return true;
+ } elseif ($match===true) {
+ wfDebug( "$fname: mime type $mime matches extension $extension, passing file\n" );
+
+ #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
+ return true;
+
+ } else {
+ wfDebug( "$fname: mime type $mime mismatches file extension $extension, rejecting file\n" );
+ return false;
+ }
+ }
+
+ /** Heuristig for detecting files that *could* contain JavaScript instructions or
+ * things that may look like HTML to a browser and are thus
+ * potentially harmful. The present implementation will produce false positives in some situations.
+ *
+ * @param string $file Pathname to the temporary upload file
+ * @param string $mime The mime type of the file
+ * @return bool true if the file contains something looking like embedded scripts
+ */
+ function detectScript($file,$mime) {
+
+ #ugly hack: for text files, always look at the entire file.
+ #For binarie field, just check the first K.
+
+ if (strpos($mime,'text/')===0) $chunk = file_get_contents( $file );
+ else {
+ $fp = fopen( $file, 'rb' );
+ $chunk = fread( $fp, 1024 );
+ fclose( $fp );
+ }
+
+ $chunk= strtolower( $chunk );
+
+ if (!$chunk) return false;
+
+ #decode from UTF-16 if needed (could be used for obfuscation).
+ if (substr($chunk,0,2)=="\xfe\xff") $enc= "UTF-16BE";
+ elseif (substr($chunk,0,2)=="\xff\xfe") $enc= "UTF-16LE";
+ else $enc= NULL;
+
+ if ($enc) $chunk= iconv($enc,"ASCII//IGNORE",$chunk);
+
+ $chunk= trim($chunk);
+
+ #FIXME: convert from UTF-16 if necessarry!
+
+ wfDebug("SpecialUpload::detectScript: checking for embedded scripts and HTML stuff\n");
+
+ #check for HTML doctype
+ if (eregi("<!DOCTYPE *X?HTML",$chunk)) return true;
+
+ /**
+ * Internet Explorer for Windows performs some really stupid file type
+ * autodetection which can cause it to interpret valid image files as HTML
+ * and potentially execute JavaScript, creating a cross-site scripting
+ * attack vectors.
+ *
+ * Apple's Safari browser also 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 IE is likely to mistake the given file for HTML.
+ * Also returns true if Safari would mistake the given file for HTML
+ * when served with a generic content-type.
+ */
+
+ $tags = array(
+ '<body',
+ '<head',
+ '<html', #also in safari
+ '<img',
+ '<pre',
+ '<script', #also in safari
+ '<table',
+ '<title' #also in safari
+ );
+
foreach( $tags as $tag ) {
if( false !== strpos( $chunk, $tag ) ) {
return true;
}
}
+
+ /*
+ * look for javascript
+ */
+
+ #resolve entity-refs to look at attributes. may be harsh on big files... cache result?
+ $chunk = Sanitizer::decodeCharReferences( $chunk );
+
+ #look for script-types
+ if (preg_match("!type\s*=\s*['\"]?\s*(\w*/)?(ecma|java)!sim",$chunk)) return true;
+
+ #look for html-style script-urls
+ if (preg_match("!(href|src|data)\s*=\s*['\"]?\s*(ecma|java)script:!sim",$chunk)) return true;
+
+ #look for css-style script-urls
+ if (preg_match("!url\s*\(\s*['\"]?\s*(ecma|java)script:!sim",$chunk)) return true;
+
+ wfDebug("SpecialUpload::detectScript: no scripts found\n");
return false;
}
+
+ /** Generic wrapper function for a virus scanner program.
+ * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
+ * $wgAntivirusRequired may be used to deny upload if the scan fails.
+ *
+ * @param string $file Pathname to the temporary upload file
+ * @return mixed false if not virus is found, NULL if the scan fails or is disabled,
+ * or a string containing feedback from the virus scanner if a virus was found.
+ * If textual feedback is missing but a virus was found, this function returns true.
+ */
+ function detectVirus($file) {
+ global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired;
+
+ $fname= "SpecialUpload::detectVirus";
+
+ if (!$wgAntivirus) { #disabled?
+ wfDebug("$fname: virus scanner disabled\n");
+
+ return NULL;
+ }
+
+ if (!$wgAntivirusSetup[$wgAntivirus]) {
+ wfDebug("$fname: unknown virus scanner: $wgAntivirus\n");
+
+ $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" ); #LOCALIZE
+
+ return "unknown antivirus: $wgAntivirus";
+ }
+
+ #look up scanner configuration
+ $virus_scanner= $wgAntivirusSetup[$wgAntivirus]["command"]; #command pattern
+ $virus_scanner_codes= $wgAntivirusSetup[$wgAntivirus]["codemap"]; #exit-code map
+ $msg_pattern= $wgAntivirusSetup[$wgAntivirus]["messagepattern"]; #message pattern
+
+ $scanner= $virus_scanner; #copy, so we can resolve the pattern
+
+ if (strpos($scanner,"%f")===false) $scanner.= " ".wfEscapeShellArg($file); #simple pattern: append file to scan
+ else $scanner= str_replace("%f",wfEscapeShellArg($file),$scanner); #complex pattern: replace "%f" with file to scan
+
+ wfDebug("$fname: running virus scan: $scanner \n");
+
+ #execute virus scanner
+ $code= false;
+
+ #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.
+ if (wfIsWindows()) exec("$scanner",$output,$code);
+ else exec("$scanner 2>&1",$output,$code);
+
+ $exit_code= $code; #remeber for user feedback
+
+ if ($virus_scanner_codes) { #map exit code to AV_xxx constants.
+ if (isset($virus_scanner_codes[$code])) $code= $virus_scanner_codes[$code]; #explicite mapping
+ else if (isset($virus_scanner_codes["*"])) $code= $virus_scanner_codes["*"]; #fallback mapping
+ }
+
+ if ($code===AV_SCAN_FAILED) { #scan failed (code was mapped to false by $virus_scanner_codes)
+ wfDebug("$fname: failed to scan $file (code $exit_code).\n");
+
+ if ($wgAntivirusRequired) return "scan failed (code $exit_code)";
+ else return NULL;
+ }
+ else if ($code===AV_SCAN_ABORTED) { #scan failed because filetype is unknown (probably imune)
+ wfDebug("$fname: unsupported file type $file (code $exit_code).\n");
+ return NULL;
+ }
+ else if ($code===AV_NO_VIRUS) {
+ wfDebug("$fname: file passed virus scan.\n");
+ return false; #no virus found
+ }
+ else {
+ $output= join("\n",$output);
+ $output= trim($output);
+
+ if (!$output) $output= true; #if ther's no output, return true
+ else if ($msg_pattern) {
+ $groups= array();
+ if (preg_match($msg_pattern,$output,$groups)) {
+ if ($groups[1]) $output= $groups[1];
+ }
+ }
+
+ wfDebug("$fname: FOUND VIRUS! scanner feedback: $output");
+ return $output;
+ }
+ }
+
+
}
?>