25use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
37use Wikimedia\AtEase\AtEase;
56 use ProtectedHookAccessorTrait;
73 protected $mTitleError = 0;
117 public const FILETYPE_MISSING = 8;
118 public const FILETYPE_BADTYPE = 9;
119 public const VERIFICATION_ERROR = 10;
120 public const HOOK_ABORTED = 11;
121 public const FILE_TOO_LARGE = 12;
122 public const WINDOWS_NONASCII_FILENAME = 13;
123 public const FILENAME_TOO_LONG = 14;
125 private const CODE_TO_STATUS = [
126 self::EMPTY_FILE => 'empty-file',
127 self::FILE_TOO_LARGE => 'file-too-large',
128 self::FILETYPE_MISSING => 'filetype-missing',
129 self::FILETYPE_BADTYPE => 'filetype-banned',
130 self::MIN_LENGTH_PARTNAME => 'filename-tooshort',
131 self::ILLEGAL_FILENAME => 'illegal-filename',
132 self::OVERWRITE_EXISTING_FILE => 'overwrite',
133 self::VERIFICATION_ERROR => 'verification-error',
134 self::HOOK_ABORTED => 'hookaborted',
135 self::WINDOWS_NONASCII_FILENAME => 'windows-nonascii-filename',
136 self::FILENAME_TOO_LONG => 'filename-toolong',
143 public function getVerificationErrorCode( $error ) {
144 return self::CODE_TO_STATUS[$error] ??
'unknown-error';
154 $enableUploads = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::EnableUploads );
156 return $enableUploads &&
wfIniGetBool(
'file_uploads' );
168 foreach ( [
'upload',
'edit' ] as $permission ) {
169 if ( !$performer->
isAllowed( $permission ) ) {
188 return $user->pingLimiter(
'upload' );
192 private static $uploadHandlers = [
'Stash',
'File',
'Url' ];
202 $type = $type ?: $request->getVal(
'wpSourceType',
'File' );
209 $type = ucfirst( $type );
214 (
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
216 ->onUploadCreateFromRequest( $type, $className );
217 if ( $className ===
null ) {
218 $className =
'UploadFrom' . $type;
219 wfDebug( __METHOD__ .
": class name: $className" );
220 if ( !in_array( $type, self::$uploadHandlers ) ) {
225 if ( !$className::isEnabled() || !$className::isValidRequest( $request ) ) {
230 $handler =
new $className;
232 $handler->initializeFromRequest( $request );
271 $this->mDesiredDestName = $name;
273 throw new MWException( __METHOD__ .
" given storage path `$tempPath`." );
277 $this->mRemoveTempFile = $removeTempFile;
292 $this->mTempPath = $tempPath ??
'';
293 $this->mFileSize = $fileSize ?:
null;
294 if ( strlen( $this->mTempPath ) && file_exists( $this->mTempPath ) ) {
295 $this->tempFileObj =
new TempFSFile( $this->mTempPath );
297 $this->mFileSize = filesize( $this->mTempPath );
300 $this->tempFileObj =
null;
310 return Status::newGood();
318 return !$this->mFileSize;
326 return $this->mFileSize;
343 $repo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
348 $tmpFile = $repo->getLocalCopy( $srcPath );
350 $tmpFile->bind( $this );
352 $path = $tmpFile ? $tmpFile->getPath() :
false;
382 return [
'status' => self::EMPTY_FILE ];
389 if ( $this->mFileSize > $maxSize ) {
391 'status' => self::FILE_TOO_LARGE,
402 if ( $verification !==
true ) {
404 'status' => self::VERIFICATION_ERROR,
405 'details' => $verification
413 if ( $result !==
true ) {
417 return [
'status' => self::OK ];
428 if ( $nt ===
null ) {
429 $result = [
'status' => $this->mTitleError ];
430 if ( $this->mTitleError === self::ILLEGAL_FILENAME ) {
431 $result[
'filtered'] = $this->mFilteredName;
433 if ( $this->mTitleError === self::FILETYPE_BADTYPE ) {
434 $result[
'finalExt'] = $this->mFinalExtension;
435 if ( count( $this->mBlackListedExtensions ) ) {
436 $result[
'blacklistedExt'] = $this->mBlackListedExtensions;
456 $verifyMimeType = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::VerifyMimeType );
457 if ( $verifyMimeType ) {
458 wfDebug(
"mime: <$mime> extension: <{$this->mFinalExtension}>" );
459 $mimeTypeExclusions = MediaWikiServices::getInstance()->getMainConfig()
460 ->get( MainConfigNames::MimeTypeExclusions );
461 if ( self::checkFileExtension(
$mime, $mimeTypeExclusions ) ) {
462 return [
'filetype-badmime',
$mime ];
475 $config = MediaWikiServices::getInstance()->getMainConfig();
476 $verifyMimeType = $config->get( MainConfigNames::VerifyMimeType );
477 $disableUploadScriptChecks = $config->get( MainConfigNames::DisableUploadScriptChecks );
479 if ( $status !==
true ) {
483 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
484 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
485 $mime = $this->mFileProps[
'mime'];
487 if ( $verifyMimeType ) {
488 # XXX: Missing extension will be caught by validateName() via getTitle()
489 if ( (
string)$this->mFinalExtension !==
'' &&
490 !self::verifyExtension(
$mime, $this->mFinalExtension )
492 return [
'filetype-mime-mismatch', $this->mFinalExtension,
$mime ];
496 # check for htmlish code and javascript
497 if ( !$disableUploadScriptChecks ) {
498 if ( $this->mFinalExtension ===
'svg' ||
$mime ===
'image/svg+xml' ) {
500 if ( $svgStatus !==
false ) {
508 $handlerStatus = $handler->verifyUpload( $this->mTempPath );
509 if ( !$handlerStatus->isOK() ) {
510 $errors = $handlerStatus->getErrorsArray();
512 return reset( $errors );
517 $this->getHookRunner()->onUploadVerifyFile( $this,
$mime, $error );
518 if ( $error !==
true ) {
519 if ( !is_array( $error ) ) {
525 wfDebug( __METHOD__ .
": all clear; passing." );
540 $config = MediaWikiServices::getInstance()->getMainConfig();
541 $disableUploadScriptChecks = $config->get( MainConfigNames::DisableUploadScriptChecks );
542 # getTitle() sets some internal parameters like $this->mFinalExtension
545 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
546 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
548 # check MIME type, if desired
549 $mime = $this->mFileProps[
'file-mime'];
551 if ( $status !==
true ) {
555 # check for htmlish code and javascript
556 if ( !$disableUploadScriptChecks ) {
557 if ( self::detectScript( $this->mTempPath,
$mime, $this->mFinalExtension ) ) {
558 return [
'uploadscripted' ];
560 if ( $this->mFinalExtension ===
'svg' ||
$mime ===
'image/svg+xml' ) {
562 if ( $svgStatus !==
false ) {
568 # Scan the uploaded file for viruses
569 $virus = self::detectVirus( $this->mTempPath );
571 return [
'uploadvirus', $virus ];
583 $names = [ $entry[
'name'] ];
590 $nullPos = strpos( $entry[
'name'],
"\000" );
591 if ( $nullPos !==
false ) {
592 $names[] = substr( $entry[
'name'], 0, $nullPos );
597 if ( preg_grep(
'!\.class/?$!', $names ) ) {
598 $this->mJavaDetected =
true;
632 if ( $nt ===
null ) {
636 $status = PermissionStatus::newEmpty();
639 if ( !$status->isGood() ) {
640 return $status->toLegacyErrorArray();
643 $overwriteError = $this->checkOverwrite( $performer );
644 if ( $overwriteError !==
true ) {
645 return [ $overwriteError ];
661 if ( $user ===
null ) {
663 $user = RequestContext::getMain()->getUser();
669 $localFile->
load( File::READ_LATEST );
670 $filename = $localFile->
getName();
673 $badFileName = $this->checkBadFileName( $filename, $this->mDesiredDestName );
674 if ( $badFileName !==
null ) {
675 $warnings[
'badfilename'] = $badFileName;
678 $unwantedFileExtensionDetails = $this->checkUnwantedFileExtensions( (
string)$this->mFinalExtension );
679 if ( $unwantedFileExtensionDetails !==
null ) {
680 $warnings[
'filetype-unwanted-type'] = $unwantedFileExtensionDetails;
683 $fileSizeWarnings = $this->checkFileSize( $this->mFileSize );
684 if ( $fileSizeWarnings ) {
685 $warnings = array_merge( $warnings, $fileSizeWarnings );
688 $localFileExistsWarnings = $this->checkLocalFileExists( $localFile, $hash );
689 if ( $localFileExistsWarnings ) {
690 $warnings = array_merge( $warnings, $localFileExistsWarnings );
693 if ( $this->checkLocalFileWasDeleted( $localFile ) ) {
694 $warnings[
'was-deleted'] = $filename;
699 $ignoreLocalDupes = isset( $warnings[
'exists'] );
700 $dupes = $this->checkAgainstExistingDupes( $hash, $ignoreLocalDupes );
702 $warnings[
'duplicate'] = $dupes;
705 $archivedDupes = $this->checkAgainstArchiveDupes( $hash, $user );
706 if ( $archivedDupes !==
null ) {
707 $warnings[
'duplicate-archive'] = $archivedDupes;
725 array_walk_recursive( $warnings,
static function ( &$param, $key ) {
726 if ( $param instanceof
File ) {
728 'fileName' => $param->getName(),
729 'timestamp' => $param->getTimestamp()
731 } elseif ( is_object( $param ) ) {
732 throw new InvalidArgumentException(
733 'UploadBase::makeWarningsSerializable: ' .
734 'Unexpected object of class ' . get_class( $param ) );
749 private function checkBadFileName( $filename, $desiredFileName ) {
750 $comparableName = str_replace(
' ',
'_', $desiredFileName );
751 $comparableName = Title::capitalize( $comparableName,
NS_FILE );
753 if ( $desiredFileName != $filename && $comparableName != $filename ) {
768 private function checkUnwantedFileExtensions( $fileExtension ) {
769 $checkFileExtensions = MediaWikiServices::getInstance()->getMainConfig()
770 ->get( MainConfigNames::CheckFileExtensions );
771 $fileExtensions = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::FileExtensions );
772 if ( $checkFileExtensions ) {
773 $extensions = array_unique( $fileExtensions );
774 if ( !self::checkFileExtension( $fileExtension, $extensions ) ) {
791 private function checkFileSize( $fileSize ) {
792 $uploadSizeWarning = MediaWikiServices::getInstance()->getMainConfig()
793 ->get( MainConfigNames::UploadSizeWarning );
797 if ( $uploadSizeWarning && ( $fileSize > $uploadSizeWarning ) ) {
798 $warnings[
'large-file'] = [
804 if ( $fileSize == 0 ) {
805 $warnings[
'empty-file'] =
true;
817 private function checkLocalFileExists(
LocalFile $localFile, $hash ) {
820 $exists = self::getExistsWarning( $localFile );
821 if ( $exists !==
false ) {
822 $warnings[
'exists'] = $exists;
825 if ( $hash !==
false && $hash === $localFile->
getSha1() ) {
826 $warnings[
'no-change'] = $localFile;
831 foreach ( $history as $oldFile ) {
832 if ( $hash === $oldFile->getSha1() ) {
833 $warnings[
'duplicate-version'][] = $oldFile;
841 private function checkLocalFileWasDeleted(
LocalFile $localFile ) {
851 private function checkAgainstExistingDupes( $hash, $ignoreLocalDupes ) {
852 if ( $hash ===
false ) {
855 $dupes = MediaWikiServices::getInstance()->getRepoGroup()->findBySha1( $hash );
857 foreach ( $dupes as $key => $dupe ) {
861 $title->equals( $dupe->getTitle() )
863 unset( $dupes[$key] );
877 private function checkAgainstArchiveDupes( $hash,
Authority $performer ) {
878 if ( $hash ===
false ) {
882 if ( $archivedFile->getID() > 0 ) {
883 if ( $archivedFile->userCan( File::DELETED_FILE, $performer ) ) {
884 return $archivedFile->getName();
910 $comment, $pageText, $watch, $user, $tags = [], ?
string $watchlistExpiry =
null
913 $props = $this->mFileProps;
916 $this->getHookRunner()->onUploadVerifyUpload( $this, $user, $props, $comment, $pageText, $error );
918 if ( !is_array( $error ) ) {
921 return Status::newFatal( ...$error );
927 $pageText !==
false ? $pageText :
'',
935 if ( $status->isGood() ) {
937 MediaWikiServices::getInstance()->getWatchlistManager()->addWatchIgnoringRights(
943 $this->getHookRunner()->onUploadComplete( $this );
967 if ( $this->mTitle !==
false ) {
970 if ( !is_string( $this->mDesiredDestName ) ) {
971 $this->mTitleError = self::ILLEGAL_FILENAME;
972 $this->mTitle =
null;
979 $title = Title::newFromText( $this->mDesiredDestName );
980 if ( $title && $title->getNamespace() ===
NS_FILE ) {
981 $this->mFilteredName = $title->getDBkey();
983 $this->mFilteredName = $this->mDesiredDestName;
986 # oi_archive_name is max 255 bytes, which include a timestamp and an
987 # exclamation mark, so restrict file name to 240 bytes.
988 if ( strlen( $this->mFilteredName ) > 240 ) {
989 $this->mTitleError = self::FILENAME_TOO_LONG;
990 $this->mTitle =
null;
1002 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1003 if ( $nt ===
null ) {
1004 $this->mTitleError = self::ILLEGAL_FILENAME;
1005 $this->mTitle =
null;
1009 $this->mFilteredName = $nt->
getDBkey();
1015 [ $partname,
$ext ] = self::splitExtensions( $this->mFilteredName );
1017 if (
$ext !== [] ) {
1018 $this->mFinalExtension = trim( end(
$ext ) );
1020 $this->mFinalExtension =
'';
1025 if ( $this->mTempPath !==
null ) {
1026 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
1027 $mime = $magic->guessMimeType( $this->mTempPath );
1028 if (
$mime !==
'unknown/unknown' ) {
1029 # Get a space separated list of extensions
1030 $mimeExt = $magic->getExtensionFromMimeTypeOrNull(
$mime );
1031 if ( $mimeExt !==
null ) {
1032 # Set the extension to the canonical extension
1033 $this->mFinalExtension = $mimeExt;
1035 # Fix up the other variables
1036 $this->mFilteredName .=
".{$this->mFinalExtension}";
1037 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1038 $ext = [ $this->mFinalExtension ];
1045 $config = MediaWikiServices::getInstance()->getMainConfig();
1046 $checkFileExtensions = $config->get( MainConfigNames::CheckFileExtensions );
1047 $strictFileExtensions = $config->get( MainConfigNames::StrictFileExtensions );
1048 $fileExtensions = $config->get( MainConfigNames::FileExtensions );
1049 $prohibitedFileExtensions = $config->get( MainConfigNames::ProhibitedFileExtensions );
1051 $badList = self::checkFileExtensionList(
$ext, $prohibitedFileExtensions );
1053 if ( $this->mFinalExtension ==
'' ) {
1054 $this->mTitleError = self::FILETYPE_MISSING;
1055 $this->mTitle =
null;
1061 ( $checkFileExtensions && $strictFileExtensions &&
1062 !self::checkFileExtension( $this->mFinalExtension, $fileExtensions ) )
1064 $this->mBlackListedExtensions = $badList;
1065 $this->mTitleError = self::FILETYPE_BADTYPE;
1066 $this->mTitle =
null;
1072 if ( !preg_match(
'/^[\x0-\x7f]*$/', $nt->getText() )
1073 && !MediaWikiServices::getInstance()->getRepoGroup()
1074 ->getLocalRepo()->backendSupportsUnicodePaths()
1076 $this->mTitleError = self::WINDOWS_NONASCII_FILENAME;
1077 $this->mTitle =
null;
1082 # If there was more than one file "extension", reassemble the base
1083 # filename to prevent bogus complaints about length
1084 if ( count(
$ext ) > 1 ) {
1085 $iterations = count(
$ext ) - 1;
1086 for ( $i = 0; $i < $iterations; $i++ ) {
1087 $partname .=
'.' .
$ext[$i];
1091 if ( strlen( $partname ) < 1 ) {
1092 $this->mTitleError = self::MIN_LENGTH_PARTNAME;
1093 $this->mTitle =
null;
1098 $this->mTitle = $nt;
1110 if ( $this->mLocalFile ===
null ) {
1112 $this->mLocalFile = $nt ===
null
1114 : MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $nt );
1117 return $this->mLocalFile;
1124 return $this->mStashFile;
1140 if ( !$isPartial ) {
1143 return Status::newFatal( ...$error );
1148 return Status::newGood(
$file );
1150 return Status::newFatal(
'uploadstash-exception', get_class( $e ), $e->getMessage() );
1159 $props = $this->mFileProps;
1161 $this->getHookRunner()->onUploadStashFile( $this, $user, $props, $error );
1162 if ( $error && !is_array( $error ) ) {
1163 $error = [ $error ];
1176 $stash = MediaWikiServices::getInstance()->getRepoGroup()
1177 ->getLocalRepo()->getUploadStash( $user );
1179 $this->mStashFile =
$file;
1189 if ( $this->mRemoveTempFile && $this->tempFileObj ) {
1191 wfDebug( __METHOD__ .
": Marked temporary file '{$this->mTempPath}' for removal" );
1192 $this->tempFileObj->autocollect();
1200 return $this->mTempPath;
1213 $bits = explode(
'.', $filename );
1214 $basename = array_shift( $bits );
1216 return [ $basename, $bits ];
1227 return in_array( strtolower(
$ext ??
'' ), $list,
true );
1239 return array_intersect( array_map(
'strtolower',
$ext ), $list );
1250 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
1253 if ( !$magic->isRecognizableExtension( $extension ) ) {
1254 wfDebug( __METHOD__ .
": passing file with unknown detected mime type; " .
1255 "unrecognized extension '$extension', can't verify" );
1260 wfDebug( __METHOD__ .
": rejecting file with unknown detected mime type; " .
1261 "recognized extension '$extension', so probably invalid file" );
1265 $match = $magic->isMatchingExtension( $extension,
$mime );
1267 if ( $match ===
null ) {
1268 if ( $magic->getMimeTypesFromExtension( $extension ) !== [] ) {
1269 wfDebug( __METHOD__ .
": No extension known for $mime, but we know a mime for $extension" );
1274 wfDebug( __METHOD__ .
": no file extension known for mime type $mime, passing file" );
1279 wfDebug( __METHOD__ .
": mime type $mime matches extension $extension, passing file" );
1286 .
": mime type $mime mismatches file extension $extension, rejecting file" );
1303 # ugly hack: for text files, always look at the entire file.
1304 # For binary field, just check the first K.
1306 if ( str_starts_with(
$mime ??
'',
'text/' ) ) {
1307 $chunk = file_get_contents(
$file );
1309 $fp = fopen(
$file,
'rb' );
1313 $chunk = fread( $fp, 1024 );
1317 $chunk = strtolower( $chunk );
1323 # decode from UTF-16 if needed (could be used for obfuscation).
1324 if ( str_starts_with( $chunk,
"\xfe\xff" ) ) {
1326 } elseif ( str_starts_with( $chunk,
"\xff\xfe" ) ) {
1332 if ( $enc !==
null ) {
1333 $chunk = iconv( $enc,
"ASCII//IGNORE", $chunk );
1336 $chunk = trim( $chunk );
1339 wfDebug( __METHOD__ .
": checking for embedded scripts and HTML stuff" );
1341 # check for HTML doctype
1342 if ( preg_match(
"/<!DOCTYPE *X?HTML/i", $chunk ) ) {
1348 if ( $extension ===
'svg' || str_starts_with(
$mime ??
'',
'image/svg' ) ) {
1349 if ( self::checkXMLEncodingMissmatch(
$file ) ) {
1363 '<html', # also in safari
1364 '<script', # also in safari
1367 foreach ( $tags as $tag ) {
1368 if ( strpos( $chunk, $tag ) !==
false ) {
1369 wfDebug( __METHOD__ .
": found something that may make it be mistaken for html: $tag" );
1379 # resolve entity-refs to look at attributes. may be harsh on big files... cache result?
1380 $chunk = Sanitizer::decodeCharReferences( $chunk );
1382 # look for script-types
1383 if ( preg_match(
'!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!im', $chunk ) ) {
1384 wfDebug( __METHOD__ .
": found script types" );
1389 # look for html-style script-urls
1390 if ( preg_match(
'!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!im', $chunk ) ) {
1391 wfDebug( __METHOD__ .
": found html-style script urls" );
1396 # look for css-style script-urls
1397 if ( preg_match(
'!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!im', $chunk ) ) {
1398 wfDebug( __METHOD__ .
": found css-style script urls" );
1403 wfDebug( __METHOD__ .
": no scripts found" );
1416 $svgMetadataCutoff = MediaWikiServices::getInstance()->getMainConfig()
1417 ->get( MainConfigNames::SVGMetadataCutoff );
1418 $contents = file_get_contents(
$file,
false,
null, 0, $svgMetadataCutoff );
1419 $encodingRegex =
'!encoding[ \t\n\r]*=[ \t\n\r]*[\'"](.*?)[\'"]!si';
1421 if ( preg_match(
"!<\?xml\b(.*?)\?>!si", $contents,
$matches ) ) {
1422 if ( preg_match( $encodingRegex,
$matches[1], $encMatch )
1423 && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1425 wfDebug( __METHOD__ .
": Found unsafe XML encoding '{$encMatch[1]}'" );
1429 } elseif ( preg_match(
"!<\?xml\b!i", $contents ) ) {
1432 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1435 } elseif ( str_starts_with( $contents,
"\x4C\x6F\xA7\x94" ) ) {
1437 wfDebug( __METHOD__ .
": EBCDIC Encoded XML" );
1444 $attemptEncodings = [
'UTF-16',
'UTF-16BE',
'UTF-32',
'UTF-32BE' ];
1445 foreach ( $attemptEncodings as $encoding ) {
1446 AtEase::suppressWarnings();
1447 $str = iconv( $encoding,
'UTF-8', $contents );
1448 AtEase::restoreWarnings();
1449 if ( $str !=
'' && preg_match(
"!<\?xml\b(.*?)\?>!si", $str,
$matches ) ) {
1450 if ( preg_match( $encodingRegex,
$matches[1], $encMatch )
1451 && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1453 wfDebug( __METHOD__ .
": Found unsafe XML encoding '{$encMatch[1]}'" );
1457 } elseif ( $str !=
'' && preg_match(
"!<\?xml\b!i", $str ) ) {
1460 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1475 $this->mSVGNSError =
false;
1478 [ $this,
'checkSvgScriptCallback' ],
1481 'processing_instruction_handler' => [ __CLASS__,
'checkSvgPICallback' ],
1482 'external_dtd_handler' => [ __CLASS__,
'checkSvgExternalDTD' ],
1485 if ( $check->wellFormed !==
true ) {
1488 return $partial ? false : [
'uploadinvalidxml' ];
1491 if ( $check->filterMatch ) {
1492 if ( $this->mSVGNSError ) {
1493 return [
'uploadscriptednamespace', $this->mSVGNSError ];
1495 return $check->filterMatchType;
1510 if ( preg_match(
'/xml-stylesheet/i', $target ) ) {
1511 return [
'upload-scripted-pi-callback' ];
1532 static $allowedDTDs = [
1533 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd',
1534 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd',
1535 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd',
1536 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd',
1538 'http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd',
1540 if ( $type !==
'PUBLIC'
1541 || !in_array( $systemId, $allowedDTDs )
1542 || !str_starts_with( $publicId,
"-//W3C//" )
1544 return [
'upload-scripted-dtd' ];
1557 [ $namespace, $strippedElement ] = self::splitXmlNamespace( $element );
1561 static $validNamespaces = [
1564 'http://creativecommons.org/ns#',
1565 'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd',
1566 'http://ns.adobe.com/adobeillustrator/10.0/',
1567 'http://ns.adobe.com/adobesvgviewerextensions/3.0/',
1568 'http://ns.adobe.com/extensibility/1.0/',
1569 'http://ns.adobe.com/flows/1.0/',
1570 'http://ns.adobe.com/illustrator/1.0/',
1571 'http://ns.adobe.com/imagereplacement/1.0/',
1572 'http://ns.adobe.com/pdf/1.3/',
1573 'http://ns.adobe.com/photoshop/1.0/',
1574 'http://ns.adobe.com/saveforweb/1.0/',
1575 'http://ns.adobe.com/variables/1.0/',
1576 'http://ns.adobe.com/xap/1.0/',
1577 'http://ns.adobe.com/xap/1.0/g/',
1578 'http://ns.adobe.com/xap/1.0/g/img/',
1579 'http://ns.adobe.com/xap/1.0/mm/',
1580 'http://ns.adobe.com/xap/1.0/rights/',
1581 'http://ns.adobe.com/xap/1.0/stype/dimensions#',
1582 'http://ns.adobe.com/xap/1.0/stype/font#',
1583 'http://ns.adobe.com/xap/1.0/stype/manifestitem#',
1584 'http://ns.adobe.com/xap/1.0/stype/resourceevent#',
1585 'http://ns.adobe.com/xap/1.0/stype/resourceref#',
1586 'http://ns.adobe.com/xap/1.0/t/pg/',
1587 'http://purl.org/dc/elements/1.1/',
1588 'http://purl.org/dc/elements/1.1',
1589 'http://schemas.microsoft.com/visio/2003/svgextensions/',
1590 'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd',
1591 'http://taptrix.com/inkpad/svg_extensions',
1592 'http://web.resource.org/cc/',
1593 'http://www.freesoftware.fsf.org/bkchem/cdml',
1594 'http://www.inkscape.org/namespaces/inkscape',
1595 'http://www.opengis.net/gml',
1596 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
1597 'http://www.w3.org/2000/svg',
1598 'http://www.w3.org/tr/rec-rdf-syntax/',
1599 'http://www.w3.org/2000/01/rdf-schema#',
1600 'http://www.w3.org/2000/02/svg/testsuite/description/',
1605 $isBuggyInkscape = preg_match(
'/^&(#38;)*ns_[a-z_]+;$/', $namespace );
1607 if ( !( $isBuggyInkscape || in_array( $namespace, $validNamespaces ) ) ) {
1608 wfDebug( __METHOD__ .
": Non-svg namespace '$namespace' in uploaded file." );
1610 $this->mSVGNSError = $namespace;
1616 if ( $strippedElement ===
'script' ) {
1617 wfDebug( __METHOD__ .
": Found script element '$element' in uploaded file." );
1619 return [
'uploaded-script-svg', $strippedElement ];
1624 if ( $strippedElement ===
'handler' ) {
1625 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1627 return [
'uploaded-script-svg', $strippedElement ];
1631 if ( $strippedElement ===
'stylesheet' ) {
1632 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1634 return [
'uploaded-script-svg', $strippedElement ];
1638 if ( $strippedElement ===
'iframe' ) {
1639 wfDebug( __METHOD__ .
": iframe in uploaded file." );
1641 return [
'uploaded-script-svg', $strippedElement ];
1645 if ( $strippedElement ===
'style'
1646 && self::checkCssFragment( Sanitizer::normalizeCss( $data ) )
1648 wfDebug( __METHOD__ .
": hostile css in style element." );
1650 return [
'uploaded-hostile-svg' ];
1653 static $cssAttrs = [
'font',
'clip-path',
'fill',
'filter',
'marker',
1654 'marker-end',
'marker-mid',
'marker-start',
'mask',
'stroke' ];
1656 foreach ( $attribs as $attrib => $value ) {
1658 [ $attributeNamespace, $stripped ] = self::splitXmlNamespace( $attrib );
1659 $value = strtolower( $value );
1663 $namespace ===
'http://www.inkscape.org/namespaces/inkscape' &&
1664 $attributeNamespace ===
''
1665 ) && str_starts_with( $stripped,
'on' )
1668 .
": Found event-handler attribute '$attrib'='$value' in uploaded file." );
1670 return [
'uploaded-event-handler-on-svg', $attrib, $value ];
1678 $stripped ===
'href'
1680 && !str_starts_with( $value,
'data:' )
1681 && !str_starts_with( $value,
'#' )
1682 && !( $strippedElement ===
'a' && preg_match(
'!^https?://!i', $value ) )
1684 wfDebug( __METHOD__ .
": Found href attribute <$strippedElement "
1685 .
"'$attrib'='$value' in uploaded file." );
1687 return [
'uploaded-href-attribute-svg', $strippedElement, $attrib, $value ];
1692 if ( $stripped ===
'href' && strncasecmp(
'data:', $value, 5 ) === 0 ) {
1696 $parameters =
'(?>;[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+=(?>[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+|"(?>[\0-\x0c\x0e-\x21\x23-\x5b\x5d-\x7f]+|\\\\[\0-\x7f])*"))*(?:;base64)?';
1698 if ( !preg_match(
"!^data:\s*image/(gif|jpeg|jpg|png)$parameters,!i", $value ) ) {
1699 wfDebug( __METHOD__ .
": Found href to allow listed data: uri "
1700 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1701 return [
'uploaded-href-unsafe-target-svg', $strippedElement, $attrib, $value ];
1706 if ( $stripped ===
'attributename'
1707 && $strippedElement ===
'animate'
1708 && $this->stripXmlNamespace( $value ) ===
'href'
1710 wfDebug( __METHOD__ .
": Found animate that might be changing href using from "
1711 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1713 return [
'uploaded-animate-svg', $strippedElement, $attrib, $value ];
1717 if ( ( $strippedElement ===
'set' || $strippedElement ===
'animate' )
1718 && $stripped ===
'attributename'
1719 && str_starts_with( $value,
'on' )
1721 wfDebug( __METHOD__ .
": Found svg setting event-handler attribute with "
1722 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1724 return [
'uploaded-setting-event-handler-svg', $strippedElement, $stripped, $value ];
1728 if ( $strippedElement ===
'set'
1729 && $stripped ===
'attributename'
1730 && str_contains( $value,
'href' )
1732 wfDebug( __METHOD__ .
": Found svg setting href attribute '$value' in uploaded file." );
1734 return [
'uploaded-setting-href-svg' ];
1738 if ( $strippedElement ===
'set'
1739 && $stripped ===
'to'
1740 && preg_match(
'!(http|https|data|script):!im', $value )
1742 wfDebug( __METHOD__ .
": Found svg setting attribute to '$value' in uploaded file." );
1744 return [
'uploaded-wrong-setting-svg', $value ];
1748 if ( $stripped ===
'handler' && preg_match(
'!(http|https|data|script):!im', $value ) ) {
1749 wfDebug( __METHOD__ .
": Found svg setting handler with remote/data/script "
1750 .
"'$attrib'='$value' in uploaded file." );
1752 return [
'uploaded-setting-handler-svg', $attrib, $value ];
1756 if ( $stripped ===
'style'
1757 && self::checkCssFragment( Sanitizer::normalizeCss( $value ) )
1759 wfDebug( __METHOD__ .
": Found svg setting a style with "
1760 .
"remote url '$attrib'='$value' in uploaded file." );
1761 return [
'uploaded-remote-url-svg', $attrib, $value ];
1765 if ( in_array( $stripped, $cssAttrs,
true )
1766 && self::checkCssFragment( $value )
1768 wfDebug( __METHOD__ .
": Found svg setting a style with "
1769 .
"remote url '$attrib'='$value' in uploaded file." );
1770 return [
'uploaded-remote-url-svg', $attrib, $value ];
1776 if ( $strippedElement ===
'image'
1777 && $stripped ===
'filter'
1778 && preg_match(
'!url\s*\(\s*["\']?[^#]!im', $value )
1780 wfDebug( __METHOD__ .
": Found image filter with url: "
1781 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1783 return [
'uploaded-image-filter-svg', $strippedElement, $stripped, $value ];
1796 private static function checkCssFragment( $value ) {
1797 # Forbid external stylesheets, for both reliability and to protect viewer's privacy
1798 if ( stripos( $value,
'@import' ) !==
false ) {
1802 # We allow @font-face to embed fonts with data: urls, so we snip the string
1803 # 'url' out so that this case won't match when we check for urls below
1804 $pattern =
'!(@font-face\s*{[^}]*src:)url(\("data:;base64,)!im';
1805 $value = preg_replace( $pattern,
'$1$2', $value );
1807 # Check for remote and executable CSS. Unlike in Sanitizer::checkCss, the CSS
1808 # properties filter and accelerator don't seem to be useful for xss in SVG files.
1809 # Expression and -o-link don't seem to work either, but filtering them here in case.
1810 # Additionally, we catch remote urls like url("http:..., url('http:..., url(http:...,
1811 # but not local ones such as url("#..., url('#..., url(#....
1812 if ( preg_match(
'!expression
1814 | -o-link-source\s*:
1815 | -o-replace\s*:!imx', $value ) ) {
1819 if ( preg_match_all(
1820 "!(\s*(url|image|image-set)\s*\(\s*[\"']?\s*[^#]+.*?\))!sim",
1825 # TODO: redo this in one regex. Until then, url("#whatever") matches the first
1826 foreach (
$matches[1] as $match ) {
1827 if ( !preg_match(
"!\s*(url|image|image-set)\s*\(\s*(#|'#|\"#)!im", $match ) ) {
1833 if ( preg_match(
'/[\000-\010\013\016-\037\177]/', $value ) ) {
1845 private static function splitXmlNamespace( $element ) {
1847 $parts = explode(
':', strtolower( $element ) );
1848 $name = array_pop( $parts );
1849 $ns = implode(
':', $parts );
1851 return [ $ns, $name ];
1858 private function stripXmlNamespace( $element ) {
1860 return self::splitXmlNamespace( $element )[1];
1875 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
1876 $antivirus = $mainConfig->get( MainConfigNames::Antivirus );
1877 $antivirusSetup = $mainConfig->get( MainConfigNames::AntivirusSetup );
1878 $antivirusRequired = $mainConfig->get( MainConfigNames::AntivirusRequired );
1879 if ( !$antivirus ) {
1880 wfDebug( __METHOD__ .
": virus scanner disabled" );
1885 if ( !$antivirusSetup[$antivirus] ) {
1886 wfDebug( __METHOD__ .
": unknown virus scanner: {$antivirus}" );
1887 $wgOut->wrapWikiMsg(
"<div class=\"error\">\n$1\n</div>",
1888 [
'virus-badscanner', $antivirus ] );
1890 return wfMessage(
'virus-unknownscanner' )->text() .
" {$antivirus}";
1893 # look up scanner configuration
1894 $command = $antivirusSetup[$antivirus][
'command'];
1895 $exitCodeMap = $antivirusSetup[$antivirus][
'codemap'];
1896 $msgPattern = $antivirusSetup[$antivirus][
'messagepattern'] ??
null;
1898 if ( !str_contains( $command,
"%f" ) ) {
1899 # simple pattern: append file to scan
1900 $command .=
" " . Shell::escape(
$file );
1902 # complex pattern: replace "%f" with file to scan
1903 $command = str_replace(
"%f", Shell::escape(
$file ), $command );
1906 wfDebug( __METHOD__ .
": running virus scan: $command " );
1908 # execute virus scanner
1911 # NOTE: there's a 50-line workaround to make stderr redirection work on windows, too.
1912 # that does not seem to be worth the pain.
1913 # Ask me (Duesentrieb) about it if it's ever needed.
1916 # map exit code to AV_xxx constants.
1917 $mappedCode = $exitCode;
1918 if ( $exitCodeMap ) {
1919 if ( isset( $exitCodeMap[$exitCode] ) ) {
1920 $mappedCode = $exitCodeMap[$exitCode];
1921 } elseif ( isset( $exitCodeMap[
"*"] ) ) {
1922 $mappedCode = $exitCodeMap[
"*"];
1926 # NB: AV_NO_VIRUS is 0, but AV_SCAN_FAILED is false,
1927 # so we need the strict equalities === and thus can't use a switch here
1929 # scan failed (code was mapped to false by $exitCodeMap)
1930 wfDebug( __METHOD__ .
": failed to scan $file (code $exitCode)." );
1932 $output = $antivirusRequired
1933 ?
wfMessage(
'virus-scanfailed', [ $exitCode ] )->text()
1936 # scan failed because filetype is unknown (probably immune)
1937 wfDebug( __METHOD__ .
": unsupported file type $file (code $exitCode)." );
1941 wfDebug( __METHOD__ .
": file passed virus scan." );
1944 $output = trim( $output );
1947 $output =
true; #
if there
's no output, return true
1948 } elseif ( $msgPattern ) {
1950 if ( preg_match( $msgPattern, $output, $groups ) && $groups[1] ) {
1951 $output = $groups[1];
1955 wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output" );
1969 private function checkOverwrite( Authority $performer ) {
1970 // First check whether the local file can be overwritten
1971 $file = $this->getLocalFile();
1972 $file->load( File::READ_LATEST );
1973 if ( $file->exists() ) {
1974 if ( !self::userCanReUpload( $performer, $file ) ) {
1975 return [ 'fileexists-forbidden
', $file->getName() ];
1981 $services = MediaWikiServices::getInstance();
1983 /* Check shared conflicts: if the local file does not exist, but
1984 * RepoGroup::findFile finds a file, it exists in a shared repository.
1986 $file = $services->getRepoGroup()->findFile( $this->getTitle(), [ 'latest
' => true ] );
1987 if ( $file && !$performer->isAllowed( 'reupload-shared
' ) ) {
1988 return [ 'fileexists-shared-forbidden
', $file->getName() ];
2001 public static function userCanReUpload( Authority $performer, File $img ) {
2002 if ( $performer->isAllowed( 'reupload
' ) ) {
2003 return true; // non-conditional
2006 if ( !$performer->isAllowed( 'reupload-own
' ) ) {
2010 if ( !( $img instanceof LocalFile ) ) {
2014 return $performer->getUser()->equals( $img->getUploader( File::RAW ) );
2028 public static function getExistsWarning( $file ) {
2029 if ( $file->exists() ) {
2030 return [ 'warning
' => 'exists
', 'file
' => $file ];
2033 if ( $file->getTitle()->getArticleID() ) {
2034 return [ 'warning
' => 'page-exists
', 'file
' => $file ];
2037 if ( !strpos( $file->getName(), '.
' ) ) {
2038 $partname = $file->getName();
2041 $n = strrpos( $file->getName(), '.
' );
2042 $extension = substr( $file->getName(), $n + 1 );
2043 $partname = substr( $file->getName(), 0, $n );
2045 $normalizedExtension = File::normalizeExtension( $extension );
2046 $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
2048 if ( $normalizedExtension != $extension ) {
2049 // We're not
using the normalized form of the extension.
2054 $nt_lc = Title::makeTitle(
NS_FILE,
"{$partname}.{$normalizedExtension}" );
2055 $file_lc = $localRepo->newFile( $nt_lc );
2057 if ( $file_lc->exists() ) {
2059 'warning' =>
'exists-normalized',
2061 'normalizedFile' => $file_lc
2067 $similarFiles = $localRepo->findFilesByPrefix(
"{$partname}.", 1 );
2068 if ( count( $similarFiles ) ) {
2070 'warning' =>
'exists-normalized',
2072 'normalizedFile' => $similarFiles[0],
2076 if ( self::isThumbName(
$file->getName() ) ) {
2078 $nt_thb = Title::newFromText(
2079 substr( $partname, strpos( $partname,
'-' ) + 1 ) .
'.' . $extension,
2082 $file_thb = $localRepo->newFile( $nt_thb );
2083 if ( $file_thb->exists() ) {
2085 'warning' =>
'thumb',
2087 'thumbFile' => $file_thb
2093 'warning' =>
'thumb-name',
2095 'thumbFile' => $file_thb
2099 foreach ( self::getFilenamePrefixBlacklist() as $prefix ) {
2100 if ( str_starts_with( $partname, $prefix ) ) {
2102 'warning' =>
'bad-prefix',
2118 $n = strrpos( $filename,
'.' );
2119 $partname = $n ? substr( $filename, 0, $n ) : $filename;
2122 substr( $partname, 3, 3 ) ===
'px-' ||
2123 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.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
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.
set( $key, $value, $exptime=0, $flags=0)
Set an item.
makeKey( $keygroup,... $components)
Make a cache key from the given components, in the default keyspace.
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')
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 disallowed 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 an allowed list of xml encodings that are known not to be interpreted differently by the server...
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...
cleanupTempFile()
If we've modified the upload file, then 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 a 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 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
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