25 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
33 use Wikimedia\AtEase\AtEase;
52 use ProtectedHookAccessorTrait;
127 self::EMPTY_FILE =>
'empty-file',
128 self::FILE_TOO_LARGE =>
'file-too-large',
129 self::FILETYPE_MISSING =>
'filetype-missing',
130 self::FILETYPE_BADTYPE =>
'filetype-banned',
131 self::MIN_LENGTH_PARTNAME =>
'filename-tooshort',
132 self::ILLEGAL_FILENAME =>
'illegal-filename',
133 self::OVERWRITE_EXISTING_FILE =>
'overwrite',
134 self::VERIFICATION_ERROR =>
'verification-error',
135 self::HOOK_ABORTED =>
'hookaborted',
136 self::WINDOWS_NONASCII_FILENAME =>
'windows-nonascii-filename',
137 self::FILENAME_TOO_LONG =>
'filename-toolong',
139 return $code_to_status[$error] ??
'unknown-error';
149 $enableUploads = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::EnableUploads );
151 return $enableUploads &&
wfIniGetBool(
'file_uploads' );
163 foreach ( [
'upload',
'edit' ] as $permission ) {
164 if ( !$performer->
isAllowed( $permission ) ) {
179 return $user->pingLimiter(
'upload' );
183 private static $uploadHandlers = [
'Stash',
'File',
'Url' ];
193 $type =
$type ?: $request->getVal(
'wpSourceType',
'File' );
205 (
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
207 ->onUploadCreateFromRequest(
$type, $className );
208 if ( $className ===
null ) {
209 $className =
'UploadFrom' .
$type;
210 wfDebug( __METHOD__ .
": class name: $className" );
211 if ( !in_array(
$type, self::$uploadHandlers ) ) {
217 if ( !$className::isEnabled() ) {
222 if ( !$className::isValidRequest( $request ) ) {
227 $handler =
new $className;
229 $handler->initializeFromRequest( $request );
268 $this->mDesiredDestName = $name;
270 throw new MWException( __METHOD__ .
" given storage path `$tempPath`." );
274 $this->mRemoveTempFile = $removeTempFile;
289 $this->mTempPath = $tempPath ??
'';
290 $this->mFileSize = $fileSize ?:
null;
291 if ( strlen( $this->mTempPath ) && file_exists( $this->mTempPath ) ) {
292 $this->tempFileObj =
new TempFSFile( $this->mTempPath );
294 $this->mFileSize = filesize( $this->mTempPath );
297 $this->tempFileObj =
null;
315 return empty( $this->mFileSize );
340 $repo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
345 $tmpFile = $repo->getLocalCopy( $srcPath );
347 $tmpFile->bind( $this );
349 $path = $tmpFile ? $tmpFile->getPath() :
false;
386 if ( $this->mFileSize > $maxSize ) {
399 if ( $verification !==
true ) {
402 'details' => $verification
410 if ( $result !==
true ) {
425 if ( $nt ===
null ) {
427 if ( $this->mTitleError === self::ILLEGAL_FILENAME ) {
430 if ( $this->mTitleError === self::FILETYPE_BADTYPE ) {
432 if ( count( $this->mBlackListedExtensions ) ) {
454 $verifyMimeType = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::VerifyMimeType );
455 if ( $verifyMimeType ) {
456 wfDebug(
"mime: <$mime> extension: <{$this->mFinalExtension}>" );
457 $mimeTypeExclusions = MediaWikiServices::getInstance()->getMainConfig()
458 ->get( MainConfigNames::MimeTypeExclusions );
459 if ( self::checkFileExtension(
$mime, $mimeTypeExclusions ) ) {
460 return [
'filetype-badmime',
$mime ];
473 $config = MediaWikiServices::getInstance()->getMainConfig();
474 $verifyMimeType = $config->get( MainConfigNames::VerifyMimeType );
475 $disableUploadScriptChecks = $config->get( MainConfigNames::DisableUploadScriptChecks );
477 if ( $status !==
true ) {
481 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
482 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
483 $mime = $this->mFileProps[
'mime'];
485 if ( $verifyMimeType ) {
486 # XXX: Missing extension will be caught by validateName() via getTitle()
487 if ( (
string)$this->mFinalExtension !==
'' &&
488 !self::verifyExtension(
$mime, $this->mFinalExtension )
494 # check for htmlish code and javascript
495 if ( !$disableUploadScriptChecks ) {
496 if ( $this->mFinalExtension ===
'svg' ||
$mime ===
'image/svg+xml' ) {
498 if ( $svgStatus !==
false ) {
506 $handlerStatus = $handler->verifyUpload( $this->mTempPath );
507 if ( !$handlerStatus->isOK() ) {
508 $errors = $handlerStatus->getErrorsArray();
510 return reset( $errors );
515 $this->getHookRunner()->onUploadVerifyFile( $this,
$mime, $error );
516 if ( $error !==
true ) {
517 if ( !is_array( $error ) ) {
523 wfDebug( __METHOD__ .
": all clear; passing." );
538 $config = MediaWikiServices::getInstance()->getMainConfig();
539 $disableUploadScriptChecks = $config->get( MainConfigNames::DisableUploadScriptChecks );
540 # getTitle() sets some internal parameters like $this->mFinalExtension
543 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
544 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
546 # check MIME type, if desired
547 $mime = $this->mFileProps[
'file-mime'];
549 if ( $status !==
true ) {
553 # check for htmlish code and javascript
554 if ( !$disableUploadScriptChecks ) {
555 if ( self::detectScript( $this->mTempPath,
$mime, $this->mFinalExtension ) ) {
556 return [
'uploadscripted' ];
558 if ( $this->mFinalExtension ===
'svg' ||
$mime ===
'image/svg+xml' ) {
560 if ( $svgStatus !==
false ) {
566 # Scan the uploaded file for viruses
569 return [
'uploadvirus', $virus ];
581 $names = [ $entry[
'name'] ];
588 $nullPos = strpos( $entry[
'name'],
"\000" );
589 if ( $nullPos !==
false ) {
590 $names[] = substr( $entry[
'name'], 0, $nullPos );
595 if ( preg_grep(
'!\.class/?$!', $names ) ) {
596 $this->mJavaDetected =
true;
630 if ( $nt ===
null ) {
634 $status = PermissionStatus::newEmpty();
637 if ( !$status->isGood() ) {
638 return $status->toLegacyErrorArray();
641 $overwriteError = $this->checkOverwrite( $performer );
642 if ( $overwriteError !==
true ) {
643 return [ $overwriteError ];
659 if ( $user ===
null ) {
667 $localFile->
load( File::READ_LATEST );
668 $filename = $localFile->
getName();
671 $badFileName = $this->checkBadFileName( $filename, $this->mDesiredDestName );
672 if ( $badFileName !==
null ) {
673 $warnings[
'badfilename'] = $badFileName;
676 $unwantedFileExtensionDetails = $this->checkUnwantedFileExtensions( (
string)$this->mFinalExtension );
677 if ( $unwantedFileExtensionDetails !==
null ) {
678 $warnings[
'filetype-unwanted-type'] = $unwantedFileExtensionDetails;
681 $fileSizeWarnings = $this->checkFileSize( $this->mFileSize );
682 if ( $fileSizeWarnings ) {
683 $warnings = array_merge( $warnings, $fileSizeWarnings );
686 $localFileExistsWarnings = $this->checkLocalFileExists( $localFile, $hash );
687 if ( $localFileExistsWarnings ) {
688 $warnings = array_merge( $warnings, $localFileExistsWarnings );
691 if ( $this->checkLocalFileWasDeleted( $localFile ) ) {
692 $warnings[
'was-deleted'] = $filename;
697 $ignoreLocalDupes = isset( $warnings[
'exists'] );
698 $dupes = $this->checkAgainstExistingDupes( $hash, $ignoreLocalDupes );
700 $warnings[
'duplicate'] = $dupes;
703 $archivedDupes = $this->checkAgainstArchiveDupes( $hash, $user );
704 if ( $archivedDupes !==
null ) {
705 $warnings[
'duplicate-archive'] = $archivedDupes;
723 array_walk_recursive( $warnings,
static function ( &$param, $key ) {
724 if ( $param instanceof
File ) {
726 'fileName' => $param->getName(),
727 'timestamp' => $param->getTimestamp()
729 } elseif ( is_object( $param ) ) {
730 throw new InvalidArgumentException(
731 'UploadBase::makeWarningsSerializable: ' .
732 'Unexpected object of class ' . get_class( $param ) );
747 private function checkBadFileName( $filename, $desiredFileName ) {
748 $comparableName = str_replace(
' ',
'_', $desiredFileName );
749 $comparableName = Title::capitalize( $comparableName,
NS_FILE );
751 if ( $desiredFileName != $filename && $comparableName != $filename ) {
766 private function checkUnwantedFileExtensions( $fileExtension ) {
767 $checkFileExtensions = MediaWikiServices::getInstance()->getMainConfig()
768 ->get( MainConfigNames::CheckFileExtensions );
769 $fileExtensions = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::FileExtensions );
770 if ( $checkFileExtensions ) {
771 $extensions = array_unique( $fileExtensions );
772 if ( !self::checkFileExtension( $fileExtension, $extensions ) ) {
789 private function checkFileSize( $fileSize ) {
790 $uploadSizeWarning = MediaWikiServices::getInstance()->getMainConfig()
791 ->get( MainConfigNames::UploadSizeWarning );
795 if ( $uploadSizeWarning && ( $fileSize > $uploadSizeWarning ) ) {
796 $warnings[
'large-file'] = [
802 if ( $fileSize == 0 ) {
803 $warnings[
'empty-file'] =
true;
815 private function checkLocalFileExists(
LocalFile $localFile, $hash ) {
819 if ( $exists !==
false ) {
820 $warnings[
'exists'] = $exists;
823 if ( $hash !==
false && $hash === $localFile->
getSha1() ) {
824 $warnings[
'no-change'] = $localFile;
829 foreach ( $history as $oldFile ) {
830 if ( $hash === $oldFile->getSha1() ) {
831 $warnings[
'duplicate-version'][] = $oldFile;
839 private function checkLocalFileWasDeleted(
LocalFile $localFile ) {
849 private function checkAgainstExistingDupes( $hash, $ignoreLocalDupes ) {
850 if ( $hash ===
false ) {
853 $dupes = MediaWikiServices::getInstance()->getRepoGroup()->findBySha1( $hash );
855 foreach ( $dupes as $key => $dupe ) {
859 $title->equals( $dupe->getTitle() )
861 unset( $dupes[$key] );
875 private function checkAgainstArchiveDupes( $hash,
Authority $performer ) {
876 if ( $hash ===
false ) {
880 if ( $archivedFile->getID() > 0 ) {
882 return $archivedFile->getName();
908 $comment, $pageText, $watch, $user, $tags = [], ?
string $watchlistExpiry =
null
915 $this->getHookRunner()->onUploadVerifyUpload( $this, $user, $props, $comment, $pageText, $error );
917 if ( !is_array( $error ) ) {
934 if ( $status->isGood() ) {
936 MediaWikiServices::getInstance()->getWatchlistManager()->addWatchIgnoringRights(
942 $this->getHookRunner()->onUploadComplete( $this );
966 if ( $this->mTitle !==
false ) {
969 if ( !is_string( $this->mDesiredDestName ) ) {
971 $this->mTitle =
null;
978 $title = Title::newFromText( $this->mDesiredDestName );
980 $this->mFilteredName =
$title->getDBkey();
985 # oi_archive_name is max 255 bytes, which include a timestamp and an
986 # exclamation mark, so restrict file name to 240 bytes.
987 if ( strlen( $this->mFilteredName ) > 240 ) {
989 $this->mTitle =
null;
1001 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1002 if ( $nt ===
null ) {
1004 $this->mTitle =
null;
1008 $this->mFilteredName = $nt->
getDBkey();
1016 if (
$ext !== [] ) {
1017 $this->mFinalExtension = trim( end(
$ext ) );
1019 $this->mFinalExtension =
'';
1024 if ( $this->mTempPath !==
null ) {
1025 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
1026 $mime = $magic->guessMimeType( $this->mTempPath );
1027 if (
$mime !==
'unknown/unknown' ) {
1028 # Get a space separated list of extensions
1029 $mimeExt = $magic->getExtensionFromMimeTypeOrNull(
$mime );
1030 if ( $mimeExt !==
null ) {
1031 # Set the extension to the canonical extension
1032 $this->mFinalExtension = $mimeExt;
1034 # Fix up the other variables
1035 $this->mFilteredName .=
".{$this->mFinalExtension}";
1036 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1044 $config = MediaWikiServices::getInstance()->getMainConfig();
1045 $checkFileExtensions = $config->get( MainConfigNames::CheckFileExtensions );
1046 $strictFileExtensions = $config->get( MainConfigNames::StrictFileExtensions );
1047 $fileExtensions = $config->get( MainConfigNames::FileExtensions );
1048 $prohibitedFileExtensions = $config->get( MainConfigNames::ProhibitedFileExtensions );
1052 if ( $this->mFinalExtension ==
'' ) {
1054 $this->mTitle =
null;
1059 if ( $blackListedExtensions ||
1060 ( $checkFileExtensions && $strictFileExtensions &&
1061 !self::checkFileExtension( $this->mFinalExtension, $fileExtensions ) ) ) {
1062 $this->mBlackListedExtensions = $blackListedExtensions;
1064 $this->mTitle =
null;
1070 if ( !preg_match(
'/^[\x0-\x7f]*$/', $nt->getText() )
1071 && !MediaWikiServices::getInstance()->getRepoGroup()
1072 ->getLocalRepo()->backendSupportsUnicodePaths()
1075 $this->mTitle =
null;
1080 # If there was more than one "extension", reassemble the base
1081 # filename to prevent bogus complaints about length
1082 if ( count(
$ext ) > 1 ) {
1083 $iterations = count(
$ext ) - 1;
1084 for ( $i = 0; $i < $iterations; $i++ ) {
1085 $partname .=
'.' .
$ext[$i];
1089 if ( strlen( $partname ) < 1 ) {
1091 $this->mTitle =
null;
1096 $this->mTitle = $nt;
1108 if ( $this->mLocalFile ===
null ) {
1110 $this->mLocalFile = $nt ===
null
1112 : MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $nt );
1138 if ( !$isPartial ) {
1148 return Status::newFatal(
'uploadstash-exception', get_class( $e ), $e->getMessage() );
1159 $this->getHookRunner()->onUploadStashFile( $this, $user, $props, $error );
1160 if ( $error && !is_array( $error ) ) {
1161 $error = [ $error ];
1174 $stash = MediaWikiServices::getInstance()->getRepoGroup()
1175 ->getLocalRepo()->getUploadStash( $user );
1177 $this->mStashFile =
$file;
1187 if ( $this->mRemoveTempFile && $this->tempFileObj ) {
1189 wfDebug( __METHOD__ .
": Marked temporary file '{$this->mTempPath}' for removal" );
1190 $this->tempFileObj->autocollect();
1208 $bits = explode(
'.', $filename );
1209 $basename = array_shift( $bits );
1211 return [ $basename, $bits ];
1223 return in_array( strtolower(
$ext ??
'' ), $list,
true );
1235 return array_intersect( array_map(
'strtolower',
$ext ), $list );
1246 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
1249 if ( !$magic->isRecognizableExtension( $extension ) ) {
1250 wfDebug( __METHOD__ .
": passing file with unknown detected mime type; " .
1251 "unrecognized extension '$extension', can't verify" );
1256 wfDebug( __METHOD__ .
": rejecting file with unknown detected mime type; " .
1257 "recognized extension '$extension', so probably invalid file" );
1261 $match = $magic->isMatchingExtension( $extension,
$mime );
1263 if ( $match ===
null ) {
1264 if ( $magic->getMimeTypesFromExtension( $extension ) !== [] ) {
1265 wfDebug( __METHOD__ .
": No extension known for $mime, but we know a mime for $extension" );
1270 wfDebug( __METHOD__ .
": no file extension known for mime type $mime, passing file" );
1275 wfDebug( __METHOD__ .
": mime type $mime matches extension $extension, passing file" );
1282 .
": mime type $mime mismatches file extension $extension, rejecting file" );
1299 # ugly hack: for text files, always look at the entire file.
1300 # For binary field, just check the first K.
1302 if ( str_starts_with(
$mime ??
'',
'text/' ) ) {
1303 $chunk = file_get_contents(
$file );
1305 $fp = fopen(
$file,
'rb' );
1309 $chunk = fread( $fp, 1024 );
1313 $chunk = strtolower( $chunk );
1319 # decode from UTF-16 if needed (could be used for obfuscation).
1320 if ( str_starts_with( $chunk,
"\xfe\xff" ) ) {
1322 } elseif ( str_starts_with( $chunk,
"\xff\xfe" ) ) {
1328 if ( $enc !==
null ) {
1329 $chunk = iconv( $enc,
"ASCII//IGNORE", $chunk );
1332 $chunk = trim( $chunk );
1335 wfDebug( __METHOD__ .
": checking for embedded scripts and HTML stuff" );
1337 # check for HTML doctype
1338 if ( preg_match(
"/<!DOCTYPE *X?HTML/i", $chunk ) ) {
1344 if ( $extension ===
'svg' || str_starts_with(
$mime ??
'',
'image/svg' ) ) {
1345 if ( self::checkXMLEncodingMissmatch(
$file ) ) {
1359 '<html', # also in safari
1360 '<script', # also in safari
1363 foreach ( $tags as $tag ) {
1364 if ( strpos( $chunk, $tag ) !==
false ) {
1365 wfDebug( __METHOD__ .
": found something that may make it be mistaken for html: $tag" );
1375 # resolve entity-refs to look at attributes. may be harsh on big files... cache result?
1378 # look for script-types
1379 if ( preg_match(
'!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!im', $chunk ) ) {
1380 wfDebug( __METHOD__ .
": found script types" );
1385 # look for html-style script-urls
1386 if ( preg_match(
'!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!im', $chunk ) ) {
1387 wfDebug( __METHOD__ .
": found html-style script urls" );
1392 # look for css-style script-urls
1393 if ( preg_match(
'!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!im', $chunk ) ) {
1394 wfDebug( __METHOD__ .
": found css-style script urls" );
1399 wfDebug( __METHOD__ .
": no scripts found" );
1412 $svgMetadataCutoff = MediaWikiServices::getInstance()->getMainConfig()
1413 ->get( MainConfigNames::SVGMetadataCutoff );
1414 $contents = file_get_contents(
$file,
false,
null, 0, $svgMetadataCutoff );
1415 $encodingRegex =
'!encoding[ \t\n\r]*=[ \t\n\r]*[\'"](.*?)[\'"]!si';
1417 if ( preg_match(
"!<\?xml\b(.*?)\?>!si", $contents,
$matches ) ) {
1418 if ( preg_match( $encodingRegex,
$matches[1], $encMatch )
1419 && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1421 wfDebug( __METHOD__ .
": Found unsafe XML encoding '{$encMatch[1]}'" );
1425 } elseif ( preg_match(
"!<\?xml\b!i", $contents ) ) {
1428 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1431 } elseif ( str_starts_with( $contents,
"\x4C\x6F\xA7\x94" ) ) {
1433 wfDebug( __METHOD__ .
": EBCDIC Encoded XML" );
1440 $attemptEncodings = [
'UTF-16',
'UTF-16BE',
'UTF-32',
'UTF-32BE' ];
1441 foreach ( $attemptEncodings as $encoding ) {
1442 AtEase::suppressWarnings();
1443 $str = iconv( $encoding,
'UTF-8', $contents );
1444 AtEase::restoreWarnings();
1445 if ( $str !=
'' && preg_match(
"!<\?xml\b(.*?)\?>!si", $str,
$matches ) ) {
1446 if ( preg_match( $encodingRegex,
$matches[1], $encMatch )
1447 && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1449 wfDebug( __METHOD__ .
": Found unsafe XML encoding '{$encMatch[1]}'" );
1453 } elseif ( $str !=
'' && preg_match(
"!<\?xml\b!i", $str ) ) {
1456 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1471 $this->mSVGNSError =
false;
1474 [ $this,
'checkSvgScriptCallback' ],
1477 'processing_instruction_handler' => [ __CLASS__,
'checkSvgPICallback' ],
1478 'external_dtd_handler' => [ __CLASS__,
'checkSvgExternalDTD' ],
1481 if ( $check->wellFormed !==
true ) {
1484 return $partial ? false : [
'uploadinvalidxml' ];
1487 if ( $check->filterMatch ) {
1488 if ( $this->mSVGNSError ) {
1491 return $check->filterMatchType;
1505 if ( preg_match(
'/xml-stylesheet/i', $target ) ) {
1506 return [
'upload-scripted-pi-callback' ];
1527 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd',
1528 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd',
1529 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd',
1530 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd',
1532 'http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd',
1534 if (
$type !==
'PUBLIC'
1535 || !in_array( $systemId, $allowedDTDs )
1536 || !str_starts_with( $publicId,
"-//W3C//" )
1538 return [
'upload-scripted-dtd' ];
1551 [ $namespace, $strippedElement ] = self::splitXmlNamespace( $element );
1555 static $validNamespaces = [
1558 'http://creativecommons.org/ns#',
1559 'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd',
1560 'http://ns.adobe.com/adobeillustrator/10.0/',
1561 'http://ns.adobe.com/adobesvgviewerextensions/3.0/',
1562 'http://ns.adobe.com/extensibility/1.0/',
1563 'http://ns.adobe.com/flows/1.0/',
1564 'http://ns.adobe.com/illustrator/1.0/',
1565 'http://ns.adobe.com/imagereplacement/1.0/',
1566 'http://ns.adobe.com/pdf/1.3/',
1567 'http://ns.adobe.com/photoshop/1.0/',
1568 'http://ns.adobe.com/saveforweb/1.0/',
1569 'http://ns.adobe.com/variables/1.0/',
1570 'http://ns.adobe.com/xap/1.0/',
1571 'http://ns.adobe.com/xap/1.0/g/',
1572 'http://ns.adobe.com/xap/1.0/g/img/',
1573 'http://ns.adobe.com/xap/1.0/mm/',
1574 'http://ns.adobe.com/xap/1.0/rights/',
1575 'http://ns.adobe.com/xap/1.0/stype/dimensions#',
1576 'http://ns.adobe.com/xap/1.0/stype/font#',
1577 'http://ns.adobe.com/xap/1.0/stype/manifestitem#',
1578 'http://ns.adobe.com/xap/1.0/stype/resourceevent#',
1579 'http://ns.adobe.com/xap/1.0/stype/resourceref#',
1580 'http://ns.adobe.com/xap/1.0/t/pg/',
1581 'http://purl.org/dc/elements/1.1/',
1582 'http://purl.org/dc/elements/1.1',
1583 'http://schemas.microsoft.com/visio/2003/svgextensions/',
1584 'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd',
1585 'http://taptrix.com/inkpad/svg_extensions',
1586 'http://web.resource.org/cc/',
1587 'http://www.freesoftware.fsf.org/bkchem/cdml',
1588 'http://www.inkscape.org/namespaces/inkscape',
1589 'http://www.opengis.net/gml',
1590 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
1591 'http://www.w3.org/2000/svg',
1592 'http://www.w3.org/tr/rec-rdf-syntax/',
1593 'http://www.w3.org/2000/01/rdf-schema#',
1594 'http://www.w3.org/2000/02/svg/testsuite/description/',
1599 $isBuggyInkscape = preg_match(
'/^&(#38;)*ns_[a-z_]+;$/', $namespace );
1601 if ( !( $isBuggyInkscape || in_array( $namespace, $validNamespaces ) ) ) {
1602 wfDebug( __METHOD__ .
": Non-svg namespace '$namespace' in uploaded file." );
1604 $this->mSVGNSError = $namespace;
1612 if ( $strippedElement ===
'script' ) {
1613 wfDebug( __METHOD__ .
": Found script element '$element' in uploaded file." );
1615 return [
'uploaded-script-svg', $strippedElement ];
1618 # e.g., <svg xmlns="http://www.w3.org/2000/svg">
1619 # <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
1620 if ( $strippedElement ===
'handler' ) {
1621 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1623 return [
'uploaded-script-svg', $strippedElement ];
1626 # SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
1627 if ( $strippedElement ===
'stylesheet' ) {
1628 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1630 return [
'uploaded-script-svg', $strippedElement ];
1633 # Block iframes, in case they pass the namespace check
1634 if ( $strippedElement ===
'iframe' ) {
1635 wfDebug( __METHOD__ .
": iframe in uploaded file." );
1637 return [
'uploaded-script-svg', $strippedElement ];
1641 if ( $strippedElement ===
'style'
1644 wfDebug( __METHOD__ .
": hostile css in style element." );
1646 return [
'uploaded-hostile-svg' ];
1649 foreach ( $attribs as $attrib => $value ) {
1651 [ $attributeNamespace, $stripped ] = self::splitXmlNamespace( $attrib );
1652 $value = strtolower( $value );
1656 $namespace ===
'http://www.inkscape.org/namespaces/inkscape' &&
1657 $attributeNamespace ===
''
1658 ) && str_starts_with( $stripped,
'on' )
1661 .
": Found event-handler attribute '$attrib'='$value' in uploaded file." );
1663 return [
'uploaded-event-handler-on-svg', $attrib, $value ];
1666 # Do not allow relative links, or unsafe url schemas.
1667 # For <a> tags, only data:, http: and https: and same-document
1668 # fragment links are allowed. For all other tags, only data:
1669 # and fragment are allowed.
1670 if ( $stripped ===
'href'
1672 && !str_starts_with( $value,
'data:' )
1673 && !str_starts_with( $value,
'#' )
1675 if ( !( $strippedElement ===
'a'
1676 && preg_match(
'!^https?://!i', $value ) )
1678 wfDebug( __METHOD__ .
": Found href attribute <$strippedElement "
1679 .
"'$attrib'='$value' in uploaded file." );
1681 return [
'uploaded-href-attribute-svg', $strippedElement, $attrib, $value ];
1685 # only allow data: targets that should be safe. This prevents vectors like,
1686 # image/svg, text/xml, application/xml, and text/html, which can contain scripts
1687 if ( $stripped ===
'href' && strncasecmp(
'data:', $value, 5 ) === 0 ) {
1690 $parameters =
'(?>;[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+=(?>[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+|"(?>[\0-\x0c\x0e-\x21\x23-\x5b\x5d-\x7f]+|\\\\[\0-\x7f])*"))*(?:;base64)?';
1692 if ( !preg_match(
"!^data:\s*image/(gif|jpeg|jpg|png)$parameters,!i", $value ) ) {
1693 wfDebug( __METHOD__ .
": Found href to unwhitelisted data: uri "
1694 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1695 return [
'uploaded-href-unsafe-target-svg', $strippedElement, $attrib, $value ];
1699 # Change href with animate from (http:
1700 if ( $stripped ===
'attributename'
1701 && $strippedElement ===
'animate'
1702 && $this->stripXmlNamespace( $value ) ===
'href'
1704 wfDebug( __METHOD__ .
": Found animate that might be changing href using from "
1705 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1707 return [
'uploaded-animate-svg', $strippedElement, $attrib, $value ];
1710 # use set/animate to add event-handler attribute to parent
1711 if ( ( $strippedElement ===
'set' || $strippedElement ===
'animate' )
1712 && $stripped ===
'attributename'
1713 && substr( $value, 0, 2 ) ===
'on'
1715 wfDebug( __METHOD__ .
": Found svg setting event-handler attribute with "
1716 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1718 return [
'uploaded-setting-event-handler-svg', $strippedElement, $stripped, $value ];
1721 # use set to add href attribute to parent element
1722 if ( $strippedElement ===
'set'
1723 && $stripped ===
'attributename'
1724 && str_contains( $value,
'href' )
1726 wfDebug( __METHOD__ .
": Found svg setting href attribute '$value' in uploaded file." );
1728 return [
'uploaded-setting-href-svg' ];
1731 # use set to add a remote / data / script target to an element
1732 if ( $strippedElement ===
'set'
1733 && $stripped ===
'to'
1734 && preg_match(
'!(http|https|data|script):!im', $value )
1736 wfDebug( __METHOD__ .
": Found svg setting attribute to '$value' in uploaded file." );
1738 return [
'uploaded-wrong-setting-svg', $value ];
1741 # use handler attribute with remote / data / script
1742 if ( $stripped ===
'handler' && preg_match(
'!(http|https|data|script):!im', $value ) ) {
1743 wfDebug( __METHOD__ .
": Found svg setting handler with remote/data/script "
1744 .
"'$attrib'='$value' in uploaded file." );
1746 return [
'uploaded-setting-handler-svg', $attrib, $value ];
1749 # use CSS styles to bring in remote code
1750 if ( $stripped ===
'style'
1753 wfDebug( __METHOD__ .
": Found svg setting a style with "
1754 .
"remote url '$attrib'='$value' in uploaded file." );
1755 return [
'uploaded-remote-url-svg', $attrib, $value ];
1758 # Several attributes can include css, css character escaping isn't allowed
1759 $cssAttrs = [
'font',
'clip-path',
'fill',
'filter',
'marker',
1760 'marker-end',
'marker-mid',
'marker-start',
'mask',
'stroke' ];
1761 if ( in_array( $stripped, $cssAttrs,
true )
1762 && self::checkCssFragment( $value )
1764 wfDebug( __METHOD__ .
": Found svg setting a style with "
1765 .
"remote url '$attrib'='$value' in uploaded file." );
1766 return [
'uploaded-remote-url-svg', $attrib, $value ];
1769 # image filters can pull in url, which could be svg that executes scripts
1770 # Only allow url( "#foo" ). Do not allow url( http:
1771 if ( $strippedElement ===
'image'
1772 && $stripped ===
'filter'
1773 && preg_match(
'!url\s*\(\s*["\']?[^#]!im', $value )
1775 wfDebug( __METHOD__ .
": Found image filter with url: "
1776 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1778 return [
'uploaded-image-filter-svg', $strippedElement, $stripped, $value ];
1791 private static function checkCssFragment( $value ) {
1792 # Forbid external stylesheets, for both reliability and to protect viewer's privacy
1793 if ( stripos( $value,
'@import' ) !==
false ) {
1797 # We allow @font-face to embed fonts with data: urls, so we snip the string
1798 # 'url' out so this case won't match when we check for urls below
1799 $pattern =
'!(@font-face\s*{[^}]*src:)url(\("data:;base64,)!im';
1800 $value = preg_replace( $pattern,
'$1$2', $value );
1802 # Check for remote and executable CSS. Unlike in Sanitizer::checkCss, the CSS
1803 # properties filter and accelerator don't seem to be useful for xss in SVG files.
1804 # Expression and -o-link don't seem to work either, but filtering them here in case.
1805 # Additionally, we catch remote urls like url("http:..., url('http:..., url(http:...,
1806 # but not local ones such as url("#..., url('#..., url(#....
1807 if ( preg_match(
'!expression
1809 | -o-link-source\s*:
1810 | -o-replace\s*:!imx', $value ) ) {
1814 if ( preg_match_all(
1815 "!(\s*(url|image|image-set)\s*\(\s*[\"']?\s*[^#]+.*?\))!sim",
1820 # TODO: redo this in one regex. Until then, url("#whatever") matches the first
1821 foreach (
$matches[1] as $match ) {
1822 if ( !preg_match(
"!\s*(url|image|image-set)\s*\(\s*(#|'#|\"#)!im", $match ) ) {
1828 if ( preg_match(
'/[\000-\010\013\016-\037\177]/', $value ) ) {
1840 private static function splitXmlNamespace( $element ) {
1842 $parts = explode(
':', strtolower( $element ) );
1843 $name = array_pop( $parts );
1844 $ns = implode(
':', $parts );
1846 return [ $ns, $name ];
1853 private function stripXmlNamespace( $name ) {
1855 $parts = explode(
':', strtolower( $name ) );
1857 return array_pop( $parts );
1872 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
1873 $antivirus = $mainConfig->get( MainConfigNames::Antivirus );
1874 $antivirusSetup = $mainConfig->get( MainConfigNames::AntivirusSetup );
1875 $antivirusRequired = $mainConfig->get( MainConfigNames::AntivirusRequired );
1876 if ( !$antivirus ) {
1877 wfDebug( __METHOD__ .
": virus scanner disabled" );
1882 if ( !$antivirusSetup[$antivirus] ) {
1883 wfDebug( __METHOD__ .
": unknown virus scanner: {$antivirus}" );
1884 $wgOut->wrapWikiMsg(
"<div class=\"error\">\n$1\n</div>",
1885 [
'virus-badscanner', $antivirus ] );
1887 return wfMessage(
'virus-unknownscanner' )->text() .
" {$antivirus}";
1890 # look up scanner configuration
1891 $command = $antivirusSetup[$antivirus][
'command'];
1892 $exitCodeMap = $antivirusSetup[$antivirus][
'codemap'];
1893 $msgPattern = $antivirusSetup[$antivirus][
'messagepattern'] ??
null;
1895 if ( !str_contains( $command,
"%f" ) ) {
1896 # simple pattern: append file to scan
1897 $command .=
" " . Shell::escape(
$file );
1899 # complex pattern: replace "%f" with file to scan
1900 $command = str_replace(
"%f", Shell::escape(
$file ), $command );
1903 wfDebug( __METHOD__ .
": running virus scan: $command " );
1905 # execute virus scanner
1908 # NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
1909 # that does not seem to be worth the pain.
1910 # Ask me (Duesentrieb) about it if it's ever needed.
1913 # map exit code to AV_xxx constants.
1914 $mappedCode = $exitCode;
1915 if ( $exitCodeMap ) {
1916 if ( isset( $exitCodeMap[$exitCode] ) ) {
1917 $mappedCode = $exitCodeMap[$exitCode];
1918 } elseif ( isset( $exitCodeMap[
"*"] ) ) {
1919 $mappedCode = $exitCodeMap[
"*"];
1927 # scan failed (code was mapped to false by $exitCodeMap)
1928 wfDebug( __METHOD__ .
": failed to scan $file (code $exitCode)." );
1930 $output = $antivirusRequired
1931 ?
wfMessage(
'virus-scanfailed', [ $exitCode ] )->text()
1934 # scan failed because filetype is unknown (probably immune)
1935 wfDebug( __METHOD__ .
": unsupported file type $file (code $exitCode)." );
1939 wfDebug( __METHOD__ .
": file passed virus scan." );
1942 $output = trim( $output );
1945 $output =
true; #
if there
's no output, return true
1946 } elseif ( $msgPattern ) {
1948 if ( preg_match( $msgPattern, $output, $groups ) && $groups[1] ) {
1949 $output = $groups[1];
1953 wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output" );
1967 private function checkOverwrite( Authority $performer ) {
1968 // First check whether the local file can be overwritten
1969 $file = $this->getLocalFile();
1970 $file->load( File::READ_LATEST );
1971 if ( $file->exists() ) {
1972 if ( !self::userCanReUpload( $performer, $file ) ) {
1973 return [ 'fileexists-forbidden
', $file->getName() ];
1979 $services = MediaWikiServices::getInstance();
1981 /* Check shared conflicts: if the local file does not exist, but
1982 * RepoGroup::findFile finds a file, it exists in a shared repository.
1984 $file = $services->getRepoGroup()->findFile( $this->getTitle(), [ 'latest
' => true ] );
1985 if ( $file && !$performer->isAllowed( 'reupload-shared
' )
1987 return [ 'fileexists-shared-forbidden
', $file->getName() ];
2000 public static function userCanReUpload( Authority $performer, File $img ) {
2001 if ( $performer->isAllowed( 'reupload
' ) ) {
2002 return true; // non-conditional
2005 if ( !$performer->isAllowed( 'reupload-own
' ) ) {
2009 if ( !( $img instanceof LocalFile ) ) {
2013 return $performer->getUser()->equals( $img->getUploader( File::RAW ) );
2027 public static function getExistsWarning( $file ) {
2028 if ( $file->exists() ) {
2029 return [ 'warning
' => 'exists
', 'file
' => $file ];
2032 if ( $file->getTitle()->getArticleID() ) {
2033 return [ 'warning
' => 'page-exists
', 'file
' => $file ];
2036 if ( !strpos( $file->getName(), '.
' ) ) {
2037 $partname = $file->getName();
2040 $n = strrpos( $file->getName(), '.
' );
2041 $extension = substr( $file->getName(), $n + 1 );
2042 $partname = substr( $file->getName(), 0, $n );
2044 $normalizedExtension = File::normalizeExtension( $extension );
2045 $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
2047 if ( $normalizedExtension != $extension ) {
2048 // We're not
using the normalized form of the extension.
2053 $nt_lc = Title::makeTitle(
NS_FILE,
"{$partname}.{$normalizedExtension}" );
2054 $file_lc = $localRepo->newFile( $nt_lc );
2056 if ( $file_lc->exists() ) {
2058 'warning' =>
'exists-normalized',
2060 'normalizedFile' => $file_lc
2066 $similarFiles = $localRepo->findFilesByPrefix(
"{$partname}.", 1 );
2067 if ( count( $similarFiles ) ) {
2069 'warning' =>
'exists-normalized',
2071 'normalizedFile' => $similarFiles[0],
2075 if ( self::isThumbName(
$file->getName() ) ) {
2076 # Check for filenames like 50px- or 180px-, these are mostly thumbnails
2077 $nt_thb = Title::newFromText(
2078 substr( $partname, strpos( $partname,
'-' ) + 1 ) .
'.' . $extension,
2081 $file_thb = $localRepo->newFile( $nt_thb );
2082 if ( $file_thb->exists() ) {
2084 'warning' =>
'thumb',
2086 'thumbFile' => $file_thb
2092 'warning' =>
'thumb-name',
2094 'thumbFile' => $file_thb
2098 foreach ( self::getFilenamePrefixBlacklist() as $prefix ) {
2099 if ( str_starts_with( $partname, $prefix ) ) {
2101 'warning' =>
'bad-prefix',
2117 $n = strrpos( $filename,
'.' );
2118 $partname = $n ? substr( $filename, 0, $n ) : $filename;
2121 substr( $partname, 3, 3 ) ===
'px-' ||
2122 substr( $partname, 2, 3 ) ===
'px-'
2124 preg_match(
"/[0-9]{2}/", substr( $partname, 0, 2 ) );
2134 $message =
wfMessage(
'filename-prefix-blacklist' )->inContentLanguage();
2135 if ( !$message->isDisabled() ) {
2136 $lines = explode(
"\n", $message->plain() );
2137 foreach (
$lines as $line ) {
2139 $comment = substr( trim( $line ), 0, 1 );
2140 if ( $comment ===
'#' || $comment ==
'' ) {
2144 $comment = strpos( $line,
'#' );
2145 if ( $comment > 0 ) {
2146 $line = substr( $line, 0, $comment - 1 );
2148 $list[] = trim( $line );
2187 $code = $error[
'status'];
2188 unset( $code[
'status'] );
2201 $maxUploadSize = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::MaxUploadSize );
2203 if ( is_array( $maxUploadSize ) ) {
2204 if ( $forType !==
null && isset( $maxUploadSize[$forType] ) ) {
2205 return $maxUploadSize[$forType];
2207 return $maxUploadSize[
'*'];
2209 return intval( $maxUploadSize );
2221 ini_get(
'upload_max_filesize' ),
2225 ini_get(
'post_max_size' ),
2228 return min( $phpMaxFileSize, $phpMaxPostSize );
2243 $store = self::getUploadSessionStore();
2244 $key = self::getUploadSessionKey( $store, $user, $statusKey );
2246 return $store->
get( $key );
2262 $store = self::getUploadSessionStore();
2263 $key = self::getUploadSessionKey( $store, $user, $statusKey );
2265 if ( $value ===
false ) {
2268 $store->
set( $key, $value, $store::TTL_DAY );
2289 private static function getUploadSessionStore() {
2290 return MediaWikiServices::getInstance()->getMainObjectStash();
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfIniGetBool( $setting)
Safety wrapper around ini_get() for boolean settings.
wfShorthandToInteger(?string $string='', int $default=-1)
Converts shorthand byte notation to integer form.
wfShellExecWithStderr( $cmd, &$retval=null, $environ=[], $limits=[])
Execute a shell command, returning both stdout and stderr.
wfStripIllegalFilenameChars( $name)
Replace all invalid characters with '-'.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) $wgOut
static getPropertyNames( $filter=[])
Returns all possible parameters to iiprop.
static getInfo( $file, $prop, $result, $thumbParams=null, $opts=false)
Get result information for an image revision.
static getPropertyNames( $filter=null)
Returns all possible parameters to siiprop.
Deleted file in the 'filearchive' table.
Class representing a cache/ephemeral data store.
get( $key, $flags=0)
Get an item.
delete( $key, $flags=0)
Delete an item if it exists.
makeKey( $collection,... $components)
Make a cache key for the global keyspace and given components.
set( $key, $value, $exptime=0, $flags=0)
Set an item.
static getSha1Base36FromPath( $path)
Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case encoding,...
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
static isVirtualUrl( $url)
Determine if a string is an mwrepo:// URL.
Implements some public methods and some protected utility functions which are required by multiple ch...
getName()
Return the name of this file.
wasDeleted()
Was this file ever deleted from the wiki?
Local file in the wiki's own database.
exists()
canRender inherited
getHistory( $limit=null, $start=null, $end=null, $inc=true)
purgeDescription inherited
load( $flags=0)
Load file metadata from cache or DB, unless already loaded.
MimeMagic helper wrapper.
A class containing constants representing the names of configuration variables.
static listParam(array $list, $type='text')
static getMain()
Get the RequestContext object associated with the main request.
static decodeCharReferences( $text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string.
static normalizeCss( $value)
Normalize CSS into a format we can easily search for hostile input.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
static newGood( $value=null)
Factory function for good results.
This class is used to hold the location and do limited manipulation of files stored temporarily (this...
UploadBase and subclasses are the backend of MediaWiki's file uploads.
getSourceType()
Returns the upload type.
static makeWarningsSerializable( $warnings)
Convert the warnings array returned by checkWarnings() to something that can be serialized.
static setSessionStatus(UserIdentity $user, $statusKey, $value)
Set the current status of a chunked upload (used for polling)
UploadStashFile null $mStashFile
static verifyExtension( $mime, $extension)
Checks if the MIME type of the uploaded file matches the file extension.
postProcessUpload()
Perform extra steps after a successful upload.
checkSvgScriptCallback( $element, $attribs, $data=null)
verifyPermissions(Authority $performer)
Alias for verifyTitlePermissions.
getLocalFile()
Return the local file and initializes if necessary.
string null $mFilteredName
static createFromRequest(&$request, $type=null)
Create a form of UploadBase depending on wpSourceType and initializes it.
runUploadStashFileHook(User $user)
zipEntryCallback( $entry)
Callback for ZipDirectoryReader to detect Java class files.
static checkSvgPICallback( $target, $data)
Callback to filter SVG Processing Instructions.
static isValidRequest( $request)
Check whether a request if valid for this handler.
convertVerifyErrorToStatus( $error)
string null $mFinalExtension
verifyPartialFile()
A verification routine suitable for partial files.
static detectScript( $file, $mime, $extension)
Heuristic for detecting files that could contain JavaScript instructions or things that may look like...
verifyFile()
Verifies that it's ok to include the uploaded file.
static isEnabled()
Returns true if uploads are enabled.
static isThumbName( $filename)
Helper function that checks whether the filename looks like a thumbnail.
getVerificationErrorCode( $error)
performUpload( $comment, $pageText, $watch, $user, $tags=[], ?string $watchlistExpiry=null)
Really perform the upload.
string null $mDesiredDestName
verifyTitlePermissions(Authority $performer)
Check whether the user can edit, upload and create the image.
static getFilenamePrefixBlacklist()
Get a list of blacklisted filename prefixes from [[MediaWiki:Filename-prefix-blacklist]].
const OVERWRITE_EXISTING_FILE
setTempFile( $tempPath, $fileSize=null)
static getSessionStatus(UserIdentity $user, $statusKey)
Get the current status of a chunked upload (used for polling)
static checkXMLEncodingMissmatch( $file)
Check a whitelist of xml encodings that are known not to be interpreted differently by the server's x...
doStashFile(User $user=null)
Implementation for stashFile() and tryStashFile().
string[] $mBlackListedExtensions
static isAllowed(Authority $performer)
Returns true if the user can use this upload module or else a string identifying the missing permissi...
const WINDOWS_NONASCII_FILENAME
cleanupTempFile()
If we've modified the upload file we need to manually remove it on exit to clean up.
validateName()
Verify that the name is valid and, if necessary, that we can overwrite.
isEmptyFile()
Return true if the file is empty.
static checkFileExtension( $ext, $list)
Perform case-insensitive match against a list of file extensions.
tryStashFile(User $user, $isPartial=false)
Like stashFile(), but respects extensions' wishes to prevent the stashing.
getTitle()
Returns the title of the file to be uploaded.
initializePathInfo( $name, $tempPath, $fileSize, $removeTempFile=false)
static getMaxUploadSize( $forType=null)
Get MediaWiki's maximum uploaded file size for given type of upload, based on $wgMaxUploadSize.
bool null $mRemoveTempFile
static checkSvgExternalDTD( $type, $publicId, $systemId)
Verify that DTD urls referenced are only the standard dtds.
getTempFileSha1Base36()
Get the base 36 SHA1 of the file.
getImageInfo( $result)
Gets image info about the file just uploaded.
detectScriptInSvg( $filename, $partial)
static splitExtensions( $filename)
Split a file into a base name and all dot-delimited 'extensions' on the end.
fetchFile()
Fetch the file.
checkWarnings( $user=null)
Check for non fatal problems with the file.
static isThrottled( $user)
Returns true if the user has surpassed the upload rate limit, false otherwise.
getFileSize()
Return the file size.
verifyUpload()
Verify whether the upload is sensible.
const MIN_LENGTH_PARTNAME
static checkFileExtensionList( $ext, $list)
Perform case-insensitive match against a list of file extensions.
static detectVirus( $file)
Generic wrapper function for a virus scanner program.
string null $mTempPath
Local file system path to the file to upload (or a local copy)
TempFSFile null $tempFileObj
Wrapper to handle deleting the temp file.
LocalFile null $mLocalFile
static getExistsWarning( $file)
Helper function that does various existence checks for a file.
static getMaxPhpUploadSize()
Get the PHP maximum uploaded file size, based on ini settings.
verifyMimeType( $mime)
Verify the MIME type.
initializeFromRequest(&$request)
Initialize from a WebRequest.
string false $mSVGNSError
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!is_readable( $file)) $ext
if(!file_exists( $CREDITS)) $lines