24use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
30use Wikimedia\AtEase\AtEase;
49 use ProtectedHookAccessorTrait;
66 protected $mTitleError = 0;
109 public const FILETYPE_MISSING = 8;
110 public const FILETYPE_BADTYPE = 9;
111 public const VERIFICATION_ERROR = 10;
112 public const HOOK_ABORTED = 11;
113 public const FILE_TOO_LARGE = 12;
114 public const WINDOWS_NONASCII_FILENAME = 13;
115 public const FILENAME_TOO_LONG = 14;
121 public function getVerificationErrorCode( $error ) {
123 self::EMPTY_FILE =>
'empty-file',
124 self::FILE_TOO_LARGE =>
'file-too-large',
125 self::FILETYPE_MISSING =>
'filetype-missing',
126 self::FILETYPE_BADTYPE =>
'filetype-banned',
127 self::MIN_LENGTH_PARTNAME =>
'filename-tooshort',
128 self::ILLEGAL_FILENAME =>
'illegal-filename',
129 self::OVERWRITE_EXISTING_FILE =>
'overwrite',
130 self::VERIFICATION_ERROR =>
'verification-error',
131 self::HOOK_ABORTED =>
'hookaborted',
132 self::WINDOWS_NONASCII_FILENAME =>
'windows-nonascii-filename',
133 self::FILENAME_TOO_LONG =>
'filename-toolong',
135 return $code_to_status[$error] ??
'unknown-error';
145 $enableUploads = MediaWikiServices::getInstance()->getMainConfig()->get(
'EnableUploads' );
147 return $enableUploads &&
wfIniGetBool(
'file_uploads' );
159 foreach ( [
'upload',
'edit' ] as $permission ) {
160 if ( !$performer->
isAllowed( $permission ) ) {
175 return $user->pingLimiter(
'upload' );
179 private static $uploadHandlers = [
'Stash',
'File',
'Url' ];
189 $type =
$type ?: $request->getVal(
'wpSourceType',
'File' );
201 Hooks::runner()->onUploadCreateFromRequest(
$type, $className );
202 if ( $className ===
null ) {
203 $className =
'UploadFrom' .
$type;
204 wfDebug( __METHOD__ .
": class name: $className" );
205 if ( !in_array(
$type, self::$uploadHandlers ) ) {
211 if ( !$className::isEnabled() ) {
216 if ( !$className::isValidRequest( $request ) ) {
221 $handler =
new $className;
223 $handler->initializeFromRequest( $request );
262 $this->mDesiredDestName = $name;
264 throw new MWException( __METHOD__ .
" given storage path `$tempPath`." );
268 $this->mRemoveTempFile = $removeTempFile;
283 $this->mTempPath = $tempPath ??
'';
284 $this->mFileSize = $fileSize ?:
null;
285 if ( strlen( $this->mTempPath ) && file_exists( $this->mTempPath ) ) {
286 $this->tempFileObj =
new TempFSFile( $this->mTempPath );
288 $this->mFileSize = filesize( $this->mTempPath );
291 $this->tempFileObj =
null;
301 return Status::newGood();
309 return empty( $this->mFileSize );
317 return $this->mFileSize;
334 $repo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
339 $tmpFile = $repo->getLocalCopy( $srcPath );
341 $tmpFile->bind( $this );
343 $path = $tmpFile ? $tmpFile->getPath() :
false;
373 return [
'status' => self::EMPTY_FILE ];
380 if ( $this->mFileSize > $maxSize ) {
382 'status' => self::FILE_TOO_LARGE,
393 if ( $verification !==
true ) {
395 'status' => self::VERIFICATION_ERROR,
396 'details' => $verification
404 if ( $result !==
true ) {
408 return [
'status' => self::OK ];
419 if ( $nt ===
null ) {
420 $result = [
'status' => $this->mTitleError ];
421 if ( $this->mTitleError == self::ILLEGAL_FILENAME ) {
422 $result[
'filtered'] = $this->mFilteredName;
424 if ( $this->mTitleError == self::FILETYPE_BADTYPE ) {
425 $result[
'finalExt'] = $this->mFinalExtension;
426 if ( count( $this->mBlackListedExtensions ) ) {
427 $result[
'blacklistedExt'] = $this->mBlackListedExtensions;
448 $verifyMimeType = MediaWikiServices::getInstance()->getMainConfig()->get(
'VerifyMimeType' );
449 $verifyMimeTypeIE = MediaWikiServices::getInstance()->getMainConfig()->get(
'VerifyMimeTypeIE' );
450 if ( $verifyMimeType ) {
451 wfDebug(
"mime: <$mime> extension: <{$this->mFinalExtension}>" );
452 $mimeTypeExclusions = MediaWikiServices::getInstance()->getMainConfig()->get(
'MimeTypeExclusions' );
453 if ( self::checkFileExtension(
$mime, $mimeTypeExclusions ) ) {
454 return [
'filetype-badmime',
$mime ];
457 if ( $verifyMimeTypeIE ) {
458 # Check what Internet Explorer would detect
459 $fp = fopen( $this->mTempPath,
'rb' );
461 $chunk = fread( $fp, 256 );
464 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
465 $extMime = $magic->getMimeTypeFromExtensionOrNull( (
string)$this->mFinalExtension ) ??
'';
466 $ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
467 foreach ( $ieTypes as $ieType ) {
468 if ( self::checkFileExtension( $ieType, $mimeTypeExclusions ) ) {
469 return [
'filetype-bad-ie-mime', $ieType ];
485 $config = MediaWikiServices::getInstance()->getMainConfig();
486 $verifyMimeType = $config->get(
'VerifyMimeType' );
487 $disableUploadScriptChecks = $config->get(
'DisableUploadScriptChecks' );
489 if ( $status !==
true ) {
493 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
494 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
495 $mime = $this->mFileProps[
'mime'];
497 if ( $verifyMimeType ) {
498 # XXX: Missing extension will be caught by validateName() via getTitle()
499 if ( (
string)$this->mFinalExtension !==
'' &&
502 return [
'filetype-mime-mismatch', $this->mFinalExtension,
$mime ];
506 # check for htmlish code and javascript
507 if ( !$disableUploadScriptChecks ) {
508 if ( $this->mFinalExtension ==
'svg' ||
$mime ==
'image/svg+xml' ) {
510 if ( $svgStatus !==
false ) {
518 $handlerStatus = $handler->verifyUpload( $this->mTempPath );
519 if ( !$handlerStatus->isOK() ) {
520 $errors = $handlerStatus->getErrorsArray();
522 return reset( $errors );
527 $this->getHookRunner()->onUploadVerifyFile( $this,
$mime, $error );
528 if ( $error !==
true ) {
529 if ( !is_array( $error ) ) {
535 wfDebug( __METHOD__ .
": all clear; passing." );
549 $config = MediaWikiServices::getInstance()->getMainConfig();
550 $allowJavaUploads = $config->get(
'AllowJavaUploads' );
551 $disableUploadScriptChecks = $config->get(
'DisableUploadScriptChecks' );
552 # getTitle() sets some internal parameters like $this->mFinalExtension
555 $mwProps =
new MWFileProps( MediaWikiServices::getInstance()->getMimeAnalyzer() );
556 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
558 # check MIME type, if desired
559 $mime = $this->mFileProps[
'file-mime'];
561 if ( $status !==
true ) {
565 # check for htmlish code and javascript
566 if ( !$disableUploadScriptChecks ) {
567 if ( self::detectScript( $this->mTempPath,
$mime, $this->mFinalExtension ) ) {
568 return [
'uploadscripted' ];
570 if ( $this->mFinalExtension ==
'svg' ||
$mime ==
'image/svg+xml' ) {
572 if ( $svgStatus !==
false ) {
578 # Check for Java applets, which if uploaded can bypass cross-site
580 if ( !$allowJavaUploads ) {
581 $this->mJavaDetected =
false;
583 [ $this,
'zipEntryCallback' ] );
584 if ( !$zipStatus->isOK() ) {
585 $errors = $zipStatus->getErrorsArray();
586 $error = reset( $errors );
587 if ( $error[0] !==
'zip-wrong-format' ) {
591 if ( $this->mJavaDetected ) {
592 return [
'uploadjava' ];
596 # Scan the uploaded file for viruses
599 return [
'uploadvirus', $virus ];
611 $names = [ $entry[
'name'] ];
618 $nullPos = strpos( $entry[
'name'],
"\000" );
619 if ( $nullPos !==
false ) {
620 $names[] = substr( $entry[
'name'], 0, $nullPos );
625 if ( preg_grep(
'!\.class/?$!', $names ) ) {
626 $this->mJavaDetected =
true;
660 if ( $nt ===
null ) {
664 $status = PermissionStatus::newEmpty();
667 if ( !$status->isGood() ) {
668 return $status->toLegacyErrorArray();
672 if ( $overwriteError !==
true ) {
673 return [ $overwriteError ];
689 if ( $user ===
null ) {
691 $user = RequestContext::getMain()->getUser();
697 $localFile->load( File::READ_LATEST );
698 $filename = $localFile->getName();
701 $badFileName = $this->
checkBadFileName( $filename, $this->mDesiredDestName );
702 if ( $badFileName !==
null ) {
703 $warnings[
'badfilename'] = $badFileName;
707 if ( $unwantedFileExtensionDetails !==
null ) {
708 $warnings[
'filetype-unwanted-type'] = $unwantedFileExtensionDetails;
711 $fileSizeWarnings = $this->
checkFileSize( $this->mFileSize );
712 if ( $fileSizeWarnings ) {
713 $warnings = array_merge( $warnings, $fileSizeWarnings );
717 if ( $localFileExistsWarnings ) {
718 $warnings = array_merge( $warnings, $localFileExistsWarnings );
722 $warnings[
'was-deleted'] = $filename;
727 $ignoreLocalDupes = isset( $warnings[
'exists'] );
730 $warnings[
'duplicate'] = $dupes;
734 if ( $archivedDupes !==
null ) {
735 $warnings[
'duplicate-archive'] = $archivedDupes;
753 array_walk_recursive( $warnings,
static function ( &$param, $key ) {
754 if ( $param instanceof
File ) {
756 'fileName' => $param->getName(),
757 'timestamp' => $param->getTimestamp()
759 } elseif ( is_object( $param ) ) {
760 throw new InvalidArgumentException(
761 'UploadBase::makeWarningsSerializable: ' .
762 'Unexpected object of class ' . get_class( $param ) );
778 $comparableName = str_replace(
' ',
'_', $desiredFileName );
779 $comparableName = Title::capitalize( $comparableName,
NS_FILE );
781 if ( $desiredFileName != $filename && $comparableName != $filename ) {
798 $checkFileExtensions = MediaWikiServices::getInstance()->getMainConfig()->get(
'CheckFileExtensions' );
799 $fileExtensions = MediaWikiServices::getInstance()->getMainConfig()->get(
'FileExtensions' );
800 if ( $checkFileExtensions ) {
801 $extensions = array_unique( $fileExtensions );
805 $wgLang->commaList( $extensions ),
820 $uploadSizeWarning = MediaWikiServices::getInstance()->getMainConfig()->get(
'UploadSizeWarning' );
824 if ( $uploadSizeWarning && ( $fileSize > $uploadSizeWarning ) ) {
825 $warnings[
'large-file'] = [
831 if ( $fileSize == 0 ) {
832 $warnings[
'empty-file'] =
true;
847 $exists = self::getExistsWarning( $localFile );
848 if ( $exists !==
false ) {
849 $warnings[
'exists'] = $exists;
852 if ( $hash !==
false && $hash === $localFile->
getSha1() ) {
853 $warnings[
'no-change'] = $localFile;
858 foreach ( $history as $oldFile ) {
859 if ( $hash === $oldFile->getSha1() ) {
860 $warnings[
'duplicate-version'][] = $oldFile;
879 if ( $hash ===
false ) {
882 $dupes = MediaWikiServices::getInstance()->getRepoGroup()->findBySha1( $hash );
884 foreach ( $dupes as $key => $dupe ) {
888 $title->equals( $dupe->getTitle() )
890 unset( $dupes[$key] );
905 if ( $hash ===
false ) {
909 if ( $archivedFile->getID() > 0 ) {
910 if ( $archivedFile->userCan( File::DELETED_FILE, $performer ) ) {
911 return $archivedFile->getName();
938 $comment, $pageText, $watch, $user, $tags = [], ?
string $watchlistExpiry =
null
941 $props = $this->mFileProps;
944 $this->getHookRunner()->onUploadVerifyUpload( $this, $user, $props, $comment, $pageText, $error );
946 if ( !is_array( $error ) ) {
949 return Status::newFatal( ...$error );
963 if ( $status->isGood() ) {
965 MediaWikiServices::getInstance()->getWatchlistManager()->addWatchIgnoringRights(
971 $this->getHookRunner()->onUploadComplete( $this );
995 if ( $this->mTitle !==
false ) {
998 if ( !is_string( $this->mDesiredDestName ) ) {
999 $this->mTitleError = self::ILLEGAL_FILENAME;
1000 $this->mTitle =
null;
1007 $title = Title::newFromText( $this->mDesiredDestName );
1009 $this->mFilteredName =
$title->getDBkey();
1011 $this->mFilteredName = $this->mDesiredDestName;
1014 # oi_archive_name is max 255 bytes, which include a timestamp and an
1015 # exclamation mark, so restrict file name to 240 bytes.
1016 if ( strlen( $this->mFilteredName ) > 240 ) {
1017 $this->mTitleError = self::FILENAME_TOO_LONG;
1018 $this->mTitle =
null;
1030 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1031 if ( $nt ===
null ) {
1032 $this->mTitleError = self::ILLEGAL_FILENAME;
1033 $this->mTitle =
null;
1037 $this->mFilteredName = $nt->
getDBkey();
1045 if (
$ext !== [] ) {
1046 $this->mFinalExtension = trim( end(
$ext ) );
1048 $this->mFinalExtension =
'';
1053 if ( $this->mTempPath !==
null ) {
1054 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
1055 $mime = $magic->guessMimeType( $this->mTempPath );
1056 if (
$mime !==
'unknown/unknown' ) {
1057 # Get a space separated list of extensions
1058 $mimeExt = $magic->getExtensionFromMimeTypeOrNull(
$mime );
1059 if ( $mimeExt !==
null ) {
1060 # Set the extension to the canonical extension
1061 $this->mFinalExtension = $mimeExt;
1063 # Fix up the other variables
1064 $this->mFilteredName .=
".{$this->mFinalExtension}";
1065 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1066 $ext = [ $this->mFinalExtension ];
1073 $config = MediaWikiServices::getInstance()->getMainConfig();
1074 $checkFileExtensions = $config->get(
'CheckFileExtensions' );
1075 $strictFileExtensions = $config->get(
'StrictFileExtensions' );
1076 $fileExtensions = $config->get(
'FileExtensions' );
1077 $prohibitedFileExtensions = $config->get(
'ProhibitedFileExtensions' );
1079 $blackListedExtensions = self::checkFileExtensionList(
$ext, $prohibitedFileExtensions );
1081 if ( $this->mFinalExtension ==
'' ) {
1082 $this->mTitleError = self::FILETYPE_MISSING;
1083 $this->mTitle =
null;
1086 } elseif ( $blackListedExtensions ||
1087 ( $checkFileExtensions && $strictFileExtensions &&
1090 $this->mBlackListedExtensions = $blackListedExtensions;
1091 $this->mTitleError = self::FILETYPE_BADTYPE;
1092 $this->mTitle =
null;
1098 if ( !preg_match(
'/^[\x0-\x7f]*$/', $nt->getText() )
1099 && !MediaWikiServices::getInstance()->getRepoGroup()
1100 ->getLocalRepo()->backendSupportsUnicodePaths()
1102 $this->mTitleError = self::WINDOWS_NONASCII_FILENAME;
1103 $this->mTitle =
null;
1108 # If there was more than one "extension", reassemble the base
1109 # filename to prevent bogus complaints about length
1110 if ( count(
$ext ) > 1 ) {
1111 $iterations = count(
$ext ) - 1;
1112 for ( $i = 0; $i < $iterations; $i++ ) {
1113 $partname .=
'.' .
$ext[$i];
1117 if ( strlen( $partname ) < 1 ) {
1118 $this->mTitleError = self::MIN_LENGTH_PARTNAME;
1119 $this->mTitle =
null;
1124 $this->mTitle = $nt;
1136 if ( $this->mLocalFile ===
null ) {
1138 $this->mLocalFile = $nt ===
null
1140 : MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $nt );
1143 return $this->mLocalFile;
1150 return $this->mStashFile;
1166 if ( !$isPartial ) {
1169 return Status::newFatal( ...$error );
1174 return Status::newGood(
$file );
1176 return Status::newFatal(
'uploadstash-exception', get_class( $e ), $e->getMessage() );
1185 $props = $this->mFileProps;
1187 $this->getHookRunner()->onUploadStashFile( $this, $user, $props, $error );
1188 if ( $error && !is_array( $error ) ) {
1189 $error = [ $error ];
1202 $stash = MediaWikiServices::getInstance()->getRepoGroup()
1203 ->getLocalRepo()->getUploadStash( $user );
1205 $this->mStashFile =
$file;
1215 if ( $this->mRemoveTempFile && $this->tempFileObj ) {
1217 wfDebug( __METHOD__ .
": Marked temporary file '{$this->mTempPath}' for removal" );
1218 $this->tempFileObj->autocollect();
1223 return $this->mTempPath;
1236 $bits = explode(
'.', $filename );
1237 $basename = array_shift( $bits );
1239 return [ $basename, $bits ];
1251 return in_array( strtolower(
$ext ), $list );
1263 return array_intersect( array_map(
'strtolower',
$ext ), $list );
1274 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
1277 if ( !$magic->isRecognizableExtension( $extension ) ) {
1278 wfDebug( __METHOD__ .
": passing file with unknown detected mime type; " .
1279 "unrecognized extension '$extension', can't verify" );
1283 wfDebug( __METHOD__ .
": rejecting file with unknown detected mime type; " .
1284 "recognized extension '$extension', so probably invalid file" );
1290 $match = $magic->isMatchingExtension( $extension,
$mime );
1292 if ( $match ===
null ) {
1293 if ( $magic->getMimeTypesFromExtension( $extension ) !== [] ) {
1294 wfDebug( __METHOD__ .
": No extension known for $mime, but we know a mime for $extension" );
1298 wfDebug( __METHOD__ .
": no file extension known for mime type $mime, passing file" );
1302 } elseif ( $match ) {
1303 wfDebug( __METHOD__ .
": mime type $mime matches extension $extension, passing file" );
1309 .
": mime type $mime mismatches file extension $extension, rejecting file" );
1327 # ugly hack: for text files, always look at the entire file.
1328 # For binary field, just check the first K.
1330 $isText = strpos(
$mime,
'text/' ) === 0;
1332 $chunk = file_get_contents(
$file );
1334 $fp = fopen(
$file,
'rb' );
1338 $chunk = fread( $fp, 1024 );
1342 $chunk = strtolower( $chunk );
1348 # decode from UTF-16 if needed (could be used for obfuscation).
1349 if ( substr( $chunk, 0, 2 ) ==
"\xfe\xff" ) {
1351 } elseif ( substr( $chunk, 0, 2 ) ==
"\xff\xfe" ) {
1357 if ( $enc !==
null ) {
1358 $chunk = iconv( $enc,
"ASCII//IGNORE", $chunk );
1361 $chunk = trim( $chunk );
1364 wfDebug( __METHOD__ .
": checking for embedded scripts and HTML stuff" );
1366 # check for HTML doctype
1367 if ( preg_match(
"/<!DOCTYPE *X?HTML/i", $chunk ) ) {
1373 if ( $extension ==
'svg' || strpos(
$mime,
'image/svg' ) === 0 ) {
1374 if ( self::checkXMLEncodingMissmatch(
$file ) ) {
1388 '<html', # also in safari
1389 '<script', # also in safari
1392 foreach ( $tags as $tag ) {
1393 if ( strpos( $chunk, $tag ) !==
false ) {
1394 wfDebug( __METHOD__ .
": found something that may make it be mistaken for html: $tag" );
1404 # resolve entity-refs to look at attributes. may be harsh on big files... cache result?
1405 $chunk = Sanitizer::decodeCharReferences( $chunk );
1407 # look for script-types
1408 if ( preg_match(
'!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim', $chunk ) ) {
1409 wfDebug( __METHOD__ .
": found script types" );
1414 # look for html-style script-urls
1415 if ( preg_match(
'!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
1416 wfDebug( __METHOD__ .
": found html-style script urls" );
1421 # look for css-style script-urls
1422 if ( preg_match(
'!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
1423 wfDebug( __METHOD__ .
": found css-style script urls" );
1428 wfDebug( __METHOD__ .
": no scripts found" );
1441 $svgMetadataCutoff = MediaWikiServices::getInstance()->getMainConfig()->get(
'SVGMetadataCutoff' );
1442 $contents = file_get_contents(
$file,
false,
null, 0, $svgMetadataCutoff );
1443 $encodingRegex =
'!encoding[ \t\n\r]*=[ \t\n\r]*[\'"](.*?)[\'"]!si';
1445 if ( preg_match(
"!<\?xml\b(.*?)\?>!si", $contents,
$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 ( preg_match(
"!<\?xml\b!si", $contents ) ) {
1456 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1459 } elseif ( substr( $contents, 0, 4 ) ==
"\x4C\x6F\xA7\x94" ) {
1461 wfDebug( __METHOD__ .
": EBCDIC Encoded XML" );
1468 $attemptEncodings = [
'UTF-16',
'UTF-16BE',
'UTF-32',
'UTF-32BE' ];
1469 foreach ( $attemptEncodings as $encoding ) {
1470 AtEase::suppressWarnings();
1471 $str = iconv( $encoding,
'UTF-8', $contents );
1472 AtEase::restoreWarnings();
1473 if ( $str !=
'' && preg_match(
"!<\?xml\b(.*?)\?>!si", $str,
$matches ) ) {
1474 if ( preg_match( $encodingRegex,
$matches[1], $encMatch )
1475 && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1477 wfDebug( __METHOD__ .
": Found unsafe XML encoding '{$encMatch[1]}'" );
1481 } elseif ( $str !=
'' && preg_match(
"!<\?xml\b!si", $str ) ) {
1484 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1499 $this->mSVGNSError =
false;
1502 [ $this,
'checkSvgScriptCallback' ],
1505 'processing_instruction_handler' => [ __CLASS__,
'checkSvgPICallback' ],
1506 'external_dtd_handler' => [ __CLASS__,
'checkSvgExternalDTD' ],
1509 if ( $check->wellFormed !==
true ) {
1512 return $partial ? false : [
'uploadinvalidxml' ];
1513 } elseif ( $check->filterMatch ) {
1514 if ( $this->mSVGNSError ) {
1515 return [
'uploadscriptednamespace', $this->mSVGNSError ];
1518 return $check->filterMatchType;
1532 if ( preg_match(
'/xml-stylesheet/i', $target ) ) {
1533 return [
'upload-scripted-pi-callback' ];
1554 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd',
1555 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd',
1556 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd',
1557 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd',
1559 'http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd',
1561 if (
$type !==
'PUBLIC'
1562 || !in_array( $systemId, $allowedDTDs )
1563 || strpos( $publicId,
"-//W3C//" ) !== 0
1565 return [
'upload-scripted-dtd' ];
1582 static $validNamespaces = [
1585 'http://creativecommons.org/ns#',
1586 'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd',
1587 'http://ns.adobe.com/adobeillustrator/10.0/',
1588 'http://ns.adobe.com/adobesvgviewerextensions/3.0/',
1589 'http://ns.adobe.com/extensibility/1.0/',
1590 'http://ns.adobe.com/flows/1.0/',
1591 'http://ns.adobe.com/illustrator/1.0/',
1592 'http://ns.adobe.com/imagereplacement/1.0/',
1593 'http://ns.adobe.com/pdf/1.3/',
1594 'http://ns.adobe.com/photoshop/1.0/',
1595 'http://ns.adobe.com/saveforweb/1.0/',
1596 'http://ns.adobe.com/variables/1.0/',
1597 'http://ns.adobe.com/xap/1.0/',
1598 'http://ns.adobe.com/xap/1.0/g/',
1599 'http://ns.adobe.com/xap/1.0/g/img/',
1600 'http://ns.adobe.com/xap/1.0/mm/',
1601 'http://ns.adobe.com/xap/1.0/rights/',
1602 'http://ns.adobe.com/xap/1.0/stype/dimensions#',
1603 'http://ns.adobe.com/xap/1.0/stype/font#',
1604 'http://ns.adobe.com/xap/1.0/stype/manifestitem#',
1605 'http://ns.adobe.com/xap/1.0/stype/resourceevent#',
1606 'http://ns.adobe.com/xap/1.0/stype/resourceref#',
1607 'http://ns.adobe.com/xap/1.0/t/pg/',
1608 'http://purl.org/dc/elements/1.1/',
1609 'http://purl.org/dc/elements/1.1',
1610 'http://schemas.microsoft.com/visio/2003/svgextensions/',
1611 'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd',
1612 'http://taptrix.com/inkpad/svg_extensions',
1613 'http://web.resource.org/cc/',
1614 'http://www.freesoftware.fsf.org/bkchem/cdml',
1615 'http://www.inkscape.org/namespaces/inkscape',
1616 'http://www.opengis.net/gml',
1617 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
1618 'http://www.w3.org/2000/svg',
1619 'http://www.w3.org/tr/rec-rdf-syntax/',
1620 'http://www.w3.org/2000/01/rdf-schema#',
1625 $isBuggyInkscape = preg_match(
'/^&(#38;)*ns_[a-z_]+;$/', $namespace );
1627 if ( !( $isBuggyInkscape || in_array( $namespace, $validNamespaces ) ) ) {
1628 wfDebug( __METHOD__ .
": Non-svg namespace '$namespace' in uploaded file." );
1630 $this->mSVGNSError = $namespace;
1638 if ( $strippedElement ==
'script' ) {
1639 wfDebug( __METHOD__ .
": Found script element '$element' in uploaded file." );
1641 return [
'uploaded-script-svg', $strippedElement ];
1644 # e.g., <svg xmlns="http://www.w3.org/2000/svg">
1645 # <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
1646 if ( $strippedElement ==
'handler' ) {
1647 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1649 return [
'uploaded-script-svg', $strippedElement ];
1652 # SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
1653 if ( $strippedElement ==
'stylesheet' ) {
1654 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1656 return [
'uploaded-script-svg', $strippedElement ];
1659 # Block iframes, in case they pass the namespace check
1660 if ( $strippedElement ==
'iframe' ) {
1661 wfDebug( __METHOD__ .
": iframe in uploaded file." );
1663 return [
'uploaded-script-svg', $strippedElement ];
1667 if ( $strippedElement ==
'style'
1668 && self::checkCssFragment( Sanitizer::normalizeCss( $data ) )
1670 wfDebug( __METHOD__ .
": hostile css in style element." );
1671 return [
'uploaded-hostile-svg' ];
1674 foreach ( $attribs as $attrib => $value ) {
1676 $value = strtolower( $value );
1678 if ( substr( $stripped, 0, 2 ) ==
'on' ) {
1680 .
": Found event-handler attribute '$attrib'='$value' in uploaded file." );
1682 return [
'uploaded-event-handler-on-svg', $attrib, $value ];
1685 # Do not allow relative links, or unsafe url schemas.
1686 # For <a> tags, only data:, http: and https: and same-document
1687 # fragment links are allowed. For all other tags, only data:
1688 # and fragment are allowed.
1689 if ( $stripped ==
'href'
1691 && strpos( $value,
'data:' ) !== 0
1692 && strpos( $value,
'#' ) !== 0
1694 if ( !( $strippedElement ===
'a'
1695 && preg_match(
'!^https?://!i', $value ) )
1697 wfDebug( __METHOD__ .
": Found href attribute <$strippedElement "
1698 .
"'$attrib'='$value' in uploaded file." );
1700 return [
'uploaded-href-attribute-svg', $strippedElement, $attrib, $value ];
1704 # only allow data: targets that should be safe. This prevents vectors like,
1705 # image/svg, text/xml, application/xml, and text/html, which can contain scripts
1706 if ( $stripped ==
'href' && strncasecmp(
'data:', $value, 5 ) === 0 ) {
1709 $parameters =
'(?>;[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+=(?>[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+|"(?>[\0-\x0c\x0e-\x21\x23-\x5b\x5d-\x7f]+|\\\\[\0-\x7f])*"))*(?:;base64)?';
1711 if ( !preg_match(
"!^data:\s*image/(gif|jpeg|jpg|png)$parameters,!i", $value ) ) {
1712 wfDebug( __METHOD__ .
": Found href to unwhitelisted data: uri "
1713 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1714 return [
'uploaded-href-unsafe-target-svg', $strippedElement, $attrib, $value ];
1718 # Change href with animate from (http:
1719 if ( $stripped ===
'attributename'
1720 && $strippedElement ===
'animate'
1723 wfDebug( __METHOD__ .
": Found animate that might be changing href using from "
1724 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1726 return [
'uploaded-animate-svg', $strippedElement, $attrib, $value ];
1729 # use set/animate to add event-handler attribute to parent
1730 if ( ( $strippedElement ==
'set' || $strippedElement ==
'animate' )
1731 && $stripped ==
'attributename'
1732 && substr( $value, 0, 2 ) ==
'on'
1734 wfDebug( __METHOD__ .
": Found svg setting event-handler attribute with "
1735 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1737 return [
'uploaded-setting-event-handler-svg', $strippedElement, $stripped, $value ];
1740 # use set to add href attribute to parent element
1741 if ( $strippedElement ==
'set'
1742 && $stripped ==
'attributename'
1743 && strpos( $value,
'href' ) !==
false
1745 wfDebug( __METHOD__ .
": Found svg setting href attribute '$value' in uploaded file." );
1747 return [
'uploaded-setting-href-svg' ];
1750 # use set to add a remote / data / script target to an element
1751 if ( $strippedElement ==
'set'
1752 && $stripped ==
'to'
1753 && preg_match(
'!(http|https|data|script):!sim', $value )
1755 wfDebug( __METHOD__ .
": Found svg setting attribute to '$value' in uploaded file." );
1757 return [
'uploaded-wrong-setting-svg', $value ];
1760 # use handler attribute with remote / data / script
1761 if ( $stripped ==
'handler' && preg_match(
'!(http|https|data|script):!sim', $value ) ) {
1762 wfDebug( __METHOD__ .
": Found svg setting handler with remote/data/script "
1763 .
"'$attrib'='$value' in uploaded file." );
1765 return [
'uploaded-setting-handler-svg', $attrib, $value ];
1768 # use CSS styles to bring in remote code
1769 if ( $stripped ==
'style'
1770 && self::checkCssFragment( Sanitizer::normalizeCss( $value ) )
1772 wfDebug( __METHOD__ .
": Found svg setting a style with "
1773 .
"remote url '$attrib'='$value' in uploaded file." );
1774 return [
'uploaded-remote-url-svg', $attrib, $value ];
1777 # Several attributes can include css, css character escaping isn't allowed
1778 $cssAttrs = [
'font',
'clip-path',
'fill',
'filter',
'marker',
1779 'marker-end',
'marker-mid',
'marker-start',
'mask',
'stroke' ];
1780 if ( in_array( $stripped, $cssAttrs )
1781 && self::checkCssFragment( $value )
1783 wfDebug( __METHOD__ .
": Found svg setting a style with "
1784 .
"remote url '$attrib'='$value' in uploaded file." );
1785 return [
'uploaded-remote-url-svg', $attrib, $value ];
1788 # image filters can pull in url, which could be svg that executes scripts
1789 # Only allow url( "#foo" ). Do not allow url( http:
1790 if ( $strippedElement ==
'image'
1791 && $stripped ==
'filter'
1792 && preg_match(
'!url\s*\(\s*["\']?[^#]!sim', $value )
1794 wfDebug( __METHOD__ .
": Found image filter with url: "
1795 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1797 return [
'uploaded-image-filter-svg', $strippedElement, $stripped, $value ];
1811 # Forbid external stylesheets, for both reliability and to protect viewer's privacy
1812 if ( stripos( $value,
'@import' ) !==
false ) {
1816 # We allow @font-face to embed fonts with data: urls, so we snip the string
1817 # 'url' out so this case won't match when we check for urls below
1818 $pattern =
'!(@font-face\s*{[^}]*src:)url(\("data:;base64,)!im';
1819 $value = preg_replace( $pattern,
'$1$2', $value );
1821 # Check for remote and executable CSS. Unlike in Sanitizer::checkCss, the CSS
1822 # properties filter and accelerator don't seem to be useful for xss in SVG files.
1823 # Expression and -o-link don't seem to work either, but filtering them here in case.
1824 # Additionally, we catch remote urls like url("http:..., url('http:..., url(http:...,
1825 # but not local ones such as url("#..., url('#..., url(#....
1826 if ( preg_match(
'!expression
1828 | -o-link-source\s*:
1829 | -o-replace\s*:!imx', $value ) ) {
1833 if ( preg_match_all(
1834 "!(\s*(url|image|image-set)\s*\(\s*[\"']?\s*[^#]+.*?\))!sim",
1839 # TODO: redo this in one regex. Until then, url("#whatever") matches the first
1840 foreach (
$matches[1] as $match ) {
1841 if ( !preg_match(
"!\s*(url|image|image-set)\s*\(\s*(#|'#|\"#)!im", $match ) ) {
1847 if ( preg_match(
'/[\000-\010\013\016-\037\177]/', $value ) ) {
1861 $parts = explode(
':', strtolower( $element ) );
1862 $name = array_pop( $parts );
1863 $ns = implode(
':', $parts );
1865 return [ $ns, $name ];
1874 $parts = explode(
':', strtolower( $name ) );
1876 return array_pop( $parts );
1891 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
1892 $antivirus = $mainConfig->get(
'Antivirus' );
1893 $antivirusSetup = $mainConfig->get(
'AntivirusSetup' );
1894 $antivirusRequired = $mainConfig->get(
'AntivirusRequired' );
1895 if ( !$antivirus ) {
1896 wfDebug( __METHOD__ .
": virus scanner disabled" );
1901 if ( !$antivirusSetup[$antivirus] ) {
1902 wfDebug( __METHOD__ .
": unknown virus scanner: {$antivirus}" );
1903 $wgOut->wrapWikiMsg(
"<div class=\"error\">\n$1\n</div>",
1904 [
'virus-badscanner', $antivirus ] );
1906 return wfMessage(
'virus-unknownscanner' )->text() .
" {$antivirus}";
1909 # look up scanner configuration
1910 $command = $antivirusSetup[$antivirus][
'command'];
1911 $exitCodeMap = $antivirusSetup[$antivirus][
'codemap'];
1912 $msgPattern = $antivirusSetup[$antivirus][
'messagepattern'] ??
null;
1914 if ( strpos(
$command,
"%f" ) ===
false ) {
1915 # simple pattern: append file to scan
1918 # complex pattern: replace "%f" with file to scan
1922 wfDebug( __METHOD__ .
": running virus scan: $command " );
1924 # execute virus scanner
1927 # NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
1928 # that does not seem to be worth the pain.
1929 # Ask me (Duesentrieb) about it if it's ever needed.
1932 # map exit code to AV_xxx constants.
1933 $mappedCode = $exitCode;
1934 if ( $exitCodeMap ) {
1935 if ( isset( $exitCodeMap[$exitCode] ) ) {
1936 $mappedCode = $exitCodeMap[$exitCode];
1937 } elseif ( isset( $exitCodeMap[
"*"] ) ) {
1938 $mappedCode = $exitCodeMap[
"*"];
1946 # scan failed (code was mapped to false by $exitCodeMap)
1947 wfDebug( __METHOD__ .
": failed to scan $file (code $exitCode)." );
1949 $output = $antivirusRequired
1950 ?
wfMessage(
'virus-scanfailed', [ $exitCode ] )->text()
1953 # scan failed because filetype is unknown (probably imune)
1954 wfDebug( __METHOD__ .
": unsupported file type $file (code $exitCode)." );
1958 wfDebug( __METHOD__ .
": file passed virus scan." );
1961 $output = trim( $output );
1964 $output =
true; #
if there
's no output, return true
1965 } elseif ( $msgPattern ) {
1967 if ( preg_match( $msgPattern, $output, $groups ) && $groups[1] ) {
1968 $output = $groups[1];
1972 wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output" );
1986 private function checkOverwrite( Authority $performer ) {
1987 // First check whether the local file can be overwritten
1988 $file = $this->getLocalFile();
1989 $file->load( File::READ_LATEST );
1990 if ( $file->exists() ) {
1991 if ( !self::userCanReUpload( $performer, $file ) ) {
1992 return [ 'fileexists-forbidden
', $file->getName() ];
1998 $services = MediaWikiServices::getInstance();
2000 /* Check shared conflicts: if the local file does not exist, but
2001 * RepoGroup::findFile finds a file, it exists in a shared repository.
2003 $file = $services->getRepoGroup()->findFile( $this->getTitle(), [ 'latest
' => true ] );
2004 if ( $file && !$performer->isAllowed( 'reupload-shared
' )
2006 return [ 'fileexists-shared-forbidden
', $file->getName() ];
2019 public static function userCanReUpload( Authority $performer, File $img ) {
2020 if ( $performer->isAllowed( 'reupload
' ) ) {
2021 return true; // non-conditional
2022 } elseif ( !$performer->isAllowed( 'reupload-own
' ) ) {
2026 if ( !( $img instanceof LocalFile ) ) {
2030 return $performer->getUser()->equals( $img->getUploader( File::RAW ) );
2044 public static function getExistsWarning( $file ) {
2045 if ( $file->exists() ) {
2046 return [ 'warning
' => 'exists
', 'file
' => $file ];
2049 if ( $file->getTitle()->getArticleID() ) {
2050 return [ 'warning
' => 'page-exists
', 'file
' => $file ];
2053 if ( !strpos( $file->getName(), '.
' ) ) {
2054 $partname = $file->getName();
2057 $n = strrpos( $file->getName(), '.
' );
2058 $extension = substr( $file->getName(), $n + 1 );
2059 $partname = substr( $file->getName(), 0, $n );
2061 $normalizedExtension = File::normalizeExtension( $extension );
2062 $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
2064 if ( $normalizedExtension != $extension ) {
2065 // We're not
using the normalized form of the extension.
2070 $nt_lc = Title::makeTitle(
NS_FILE,
"{$partname}.{$normalizedExtension}" );
2071 $file_lc = $localRepo->newFile( $nt_lc );
2073 if ( $file_lc->exists() ) {
2075 'warning' =>
'exists-normalized',
2077 'normalizedFile' => $file_lc
2083 $similarFiles = $localRepo->findFilesByPrefix(
"{$partname}.", 1 );
2084 if ( count( $similarFiles ) ) {
2086 'warning' =>
'exists-normalized',
2088 'normalizedFile' => $similarFiles[0],
2092 if ( self::isThumbName(
$file->getName() ) ) {
2093 # Check for filenames like 50px- or 180px-, these are mostly thumbnails
2095 substr( $partname, strpos( $partname,
'-' ) + 1 ) .
'.' . $extension,
2098 $file_thb = $localRepo->newFile( $nt_thb );
2099 if ( $file_thb->exists() ) {
2101 'warning' =>
'thumb',
2103 'thumbFile' => $file_thb
2108 'warning' =>
'thumb-name',
2110 'thumbFile' => $file_thb
2115 foreach ( self::getFilenamePrefixBlacklist() as $prefix ) {
2116 if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
2118 'warning' =>
'bad-prefix',
2134 $n = strrpos( $filename,
'.' );
2135 $partname = $n ? substr( $filename, 0, $n ) : $filename;
2138 substr( $partname, 3, 3 ) ==
'px-' ||
2139 substr( $partname, 2, 3 ) ==
'px-'
2141 preg_match(
"/[0-9]{2}/", substr( $partname, 0, 2 ) );
2151 $message =
wfMessage(
'filename-prefix-blacklist' )->inContentLanguage();
2152 if ( !$message->isDisabled() ) {
2153 $lines = explode(
"\n", $message->plain() );
2156 $comment = substr( trim(
$line ), 0, 1 );
2157 if ( $comment ==
'#' || $comment ==
'' ) {
2161 $comment = strpos(
$line,
'#' );
2162 if ( $comment > 0 ) {
2165 $list[] = trim(
$line );
2204 $code = $error[
'status'];
2205 unset( $code[
'status'] );
2218 $maxUploadSize = MediaWikiServices::getInstance()->getMainConfig()->get(
'MaxUploadSize' );
2220 if ( is_array( $maxUploadSize ) ) {
2221 if ( $forType !==
null && isset( $maxUploadSize[$forType] ) ) {
2222 return $maxUploadSize[$forType];
2224 return $maxUploadSize[
'*'];
2227 return intval( $maxUploadSize );
2240 ini_get(
'upload_max_filesize' ),
2244 ini_get(
'post_max_size' ),
2247 return min( $phpMaxFileSize, $phpMaxPostSize );
2262 $store = self::getUploadSessionStore();
2263 $key = self::getUploadSessionKey( $store, $user, $statusKey );
2265 return $store->get( $key );
2281 $store = self::getUploadSessionStore();
2282 $key = self::getUploadSessionKey( $store, $user, $statusKey );
2284 if ( $value ===
false ) {
2285 $store->delete( $key );
2287 $store->set( $key, $value, $store::TTL_DAY );
2309 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
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) $wgLang
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.
Class representing a row of the 'filearchive' table.
Class representing a cache/ephemeral data store.
makeKey( $collection,... $components)
Make a cache key for the global keyspace and given components.
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...
wasDeleted()
Was this file ever deleted from the wiki?
Class to represent a local file in the wiki's own database.
exists()
canRender inherited
getHistory( $limit=null, $start=null, $end=null, $inc=true)
purgeDescription inherited
MimeMagic helper wrapper.
This class is used to hold the location and do limited manipulation of files stored temporarily (this...
Represents a title within MediaWiki.
getDBkey()
Get the main part with underscores.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
UploadBase and subclasses are the backend of MediaWiki's file uploads.
static string[] $uploadHandlers
Upload handlers.
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.
checkLocalFileExists(LocalFile $localFile, $hash)
getLocalFile()
Return the local file and initializes if necessary.
stripXmlNamespace( $name)
string null $mFilteredName
checkBadFileName( $filename, $desiredFileName)
Check whether the resulting filename is different from the desired one, but ignore things like ucfirs...
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.
static getUploadSessionStore()
getVerificationErrorCode( $error)
performUpload( $comment, $pageText, $watch, $user, $tags=[], ?string $watchlistExpiry=null)
Really perform the upload.
string null $mDesiredDestName
static checkCssFragment( $value)
Check a block of CSS or CSS fragment for anything that looks like it is bringing in remote code.
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...
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.
checkFileSize( $fileSize)
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.
checkAgainstArchiveDupes( $hash, Authority $performer)
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.
static splitXmlNamespace( $element)
Divide the element name passed by the xml parser to the callback into URI and prifix.
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 getUploadSessionKey(BagOStuff $store, UserIdentity $user, $statusKey)
static isThrottled( $user)
Returns true if the user has surpassed the upload rate limit, false otherwise.
checkLocalFileWasDeleted(LocalFile $localFile)
getFileSize()
Return the file size.
verifyUpload()
Verify whether the upload is sensible.
checkOverwrite(Authority $performer)
Check if there's an overwrite conflict and, if so, if restrictions forbid this user from performing t...
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.
checkUnwantedFileExtensions( $fileExtension)
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
checkAgainstExistingDupes( $hash, $ignoreLocalDupes)
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static read( $fileName, $callback, $options=[])
Read a ZIP file and call a function for each file discovered in it.
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