24use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
46 use ProtectedHookAccessorTrait;
63 protected $mTitleError = 0;
106 public const FILETYPE_MISSING = 8;
107 public const FILETYPE_BADTYPE = 9;
108 public const VERIFICATION_ERROR = 10;
109 public const HOOK_ABORTED = 11;
110 public const FILE_TOO_LARGE = 12;
111 public const WINDOWS_NONASCII_FILENAME = 13;
112 public const FILENAME_TOO_LONG = 14;
118 public function getVerificationErrorCode( $error ) {
120 self::EMPTY_FILE =>
'empty-file',
121 self::FILE_TOO_LARGE =>
'file-too-large',
122 self::FILETYPE_MISSING =>
'filetype-missing',
123 self::FILETYPE_BADTYPE =>
'filetype-banned',
124 self::MIN_LENGTH_PARTNAME =>
'filename-tooshort',
125 self::ILLEGAL_FILENAME =>
'illegal-filename',
126 self::OVERWRITE_EXISTING_FILE =>
'overwrite',
127 self::VERIFICATION_ERROR =>
'verification-error',
128 self::HOOK_ABORTED =>
'hookaborted',
129 self::WINDOWS_NONASCII_FILENAME =>
'windows-nonascii-filename',
130 self::FILENAME_TOO_LONG =>
'filename-toolong',
132 return $code_to_status[$error] ??
'unknown-error';
156 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
157 foreach ( [
'upload',
'edit' ] as $permission ) {
158 if ( !$permissionManager->userHasRight( $user, $permission ) ) {
173 return $user->pingLimiter(
'upload' );
187 $type =
$type ?: $request->getVal(
'wpSourceType',
'File' );
199 Hooks::runner()->onUploadCreateFromRequest(
$type, $className );
200 if ( $className ===
null ) {
201 $className =
'UploadFrom' .
$type;
202 wfDebug( __METHOD__ .
": class name: $className" );
203 if ( !in_array(
$type, self::$uploadHandlers ) ) {
209 if ( !$className::isEnabled() ) {
214 if ( !$className::isValidRequest( $request ) ) {
219 $handler =
new $className;
221 $handler->initializeFromRequest( $request );
260 $this->mDesiredDestName = $name;
262 throw new MWException( __METHOD__ .
" given storage path `$tempPath`." );
266 $this->mRemoveTempFile = $removeTempFile;
281 $this->mTempPath = $tempPath ??
'';
282 $this->mFileSize = $fileSize ?:
null;
283 if ( strlen( $this->mTempPath ) && file_exists( $this->mTempPath ) ) {
284 $this->tempFileObj =
new TempFSFile( $this->mTempPath );
286 $this->mFileSize = filesize( $this->mTempPath );
289 $this->tempFileObj =
null;
299 return Status::newGood();
307 return empty( $this->mFileSize );
315 return $this->mFileSize;
332 $repo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
337 $tmpFile = $repo->getLocalCopy( $srcPath );
339 $tmpFile->bind( $this );
341 $path = $tmpFile ? $tmpFile->getPath() :
false;
371 return [
'status' => self::EMPTY_FILE ];
378 if ( $this->mFileSize > $maxSize ) {
380 'status' => self::FILE_TOO_LARGE,
391 if ( $verification !==
true ) {
393 'status' => self::VERIFICATION_ERROR,
394 'details' => $verification
402 if ( $result !==
true ) {
406 return [
'status' => self::OK ];
417 if ( $nt ===
null ) {
418 $result = [
'status' => $this->mTitleError ];
419 if ( $this->mTitleError == self::ILLEGAL_FILENAME ) {
420 $result[
'filtered'] = $this->mFilteredName;
422 if ( $this->mTitleError == self::FILETYPE_BADTYPE ) {
423 $result[
'finalExt'] = $this->mFinalExtension;
424 if ( count( $this->mBlackListedExtensions ) ) {
425 $result[
'blacklistedExt'] = $this->mBlackListedExtensions;
448 wfDebug(
"mime: <$mime> extension: <{$this->mFinalExtension}>" );
451 return [
'filetype-badmime',
$mime ];
455 # Check what Internet Explorer would detect
456 $fp = fopen( $this->mTempPath,
'rb' );
457 $chunk = fread( $fp, 256 );
460 $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
461 $extMime = $magic->getMimeTypeFromExtensionOrNull( $this->mFinalExtension );
462 $ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
463 foreach ( $ieTypes as $ieType ) {
465 return [
'filetype-bad-ie-mime', $ieType ];
483 if ( $status !==
true ) {
488 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
489 $mime = $this->mFileProps[
'mime'];
492 # XXX: Missing extension will be caught by validateName() via getTitle()
493 if ( $this->mFinalExtension !=
'' && !$this->
verifyExtension(
$mime, $this->mFinalExtension ) ) {
494 return [
'filetype-mime-mismatch', $this->mFinalExtension,
$mime ];
498 # check for htmlish code and javascript
500 if ( $this->mFinalExtension ==
'svg' ||
$mime ==
'image/svg+xml' ) {
502 if ( $svgStatus !==
false ) {
510 $handlerStatus = $handler->verifyUpload( $this->mTempPath );
511 if ( !$handlerStatus->isOK() ) {
512 $errors = $handlerStatus->getErrorsArray();
514 return reset( $errors );
519 $this->getHookRunner()->onUploadVerifyFile( $this,
$mime, $error );
520 if ( $error !==
true ) {
521 if ( !is_array( $error ) ) {
527 wfDebug( __METHOD__ .
": all clear; passing." );
543 # getTitle() sets some internal parameters like $this->mFinalExtension
547 $this->mFileProps = $mwProps->getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
549 # check MIME type, if desired
550 $mime = $this->mFileProps[
'file-mime'];
552 if ( $status !==
true ) {
556 # check for htmlish code and javascript
558 if ( self::detectScript( $this->mTempPath,
$mime, $this->mFinalExtension ) ) {
559 return [
'uploadscripted' ];
561 if ( $this->mFinalExtension ==
'svg' ||
$mime ==
'image/svg+xml' ) {
563 if ( $svgStatus !==
false ) {
569 # Check for Java applets, which if uploaded can bypass cross-site
572 $this->mJavaDetected =
false;
574 [ $this,
'zipEntryCallback' ] );
575 if ( !$zipStatus->isOK() ) {
576 $errors = $zipStatus->getErrorsArray();
577 $error = reset( $errors );
578 if ( $error[0] !==
'zip-wrong-format' ) {
582 if ( $this->mJavaDetected ) {
583 return [
'uploadjava' ];
587 # Scan the uploaded file for viruses
590 return [
'uploadvirus', $virus ];
602 $names = [ $entry[
'name'] ];
609 $nullPos = strpos( $entry[
'name'],
"\000" );
610 if ( $nullPos !==
false ) {
611 $names[] = substr( $entry[
'name'], 0, $nullPos );
616 if ( preg_grep(
'!\.class/?$!', $names ) ) {
617 $this->mJavaDetected =
true;
651 if ( $nt ===
null ) {
654 $permManager = MediaWikiServices::getInstance()->getPermissionManager();
655 $permErrors = $permManager->getPermissionErrors(
'edit', $user, $nt );
656 $permErrorsUpload = $permManager->getPermissionErrors(
'upload', $user, $nt );
657 if ( !$nt->exists() ) {
658 $permErrorsCreate = $permManager->getPermissionErrors(
'create', $user, $nt );
660 $permErrorsCreate = [];
662 if ( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
663 $permErrors = array_merge( $permErrors,
wfArrayDiff2( $permErrorsUpload, $permErrors ) );
664 $permErrors = array_merge( $permErrors,
wfArrayDiff2( $permErrorsCreate, $permErrors ) );
670 if ( $overwriteError !==
true ) {
671 return [ $overwriteError ];
687 if ( $user ===
null ) {
689 $user = RequestContext::getMain()->getUser();
695 $localFile->load( File::READ_LATEST );
696 $filename = $localFile->getName();
699 $badFileName = $this->
checkBadFileName( $filename, $this->mDesiredDestName );
700 if ( $badFileName !==
null ) {
701 $warnings[
'badfilename'] = $badFileName;
705 if ( $unwantedFileExtensionDetails !==
null ) {
706 $warnings[
'filetype-unwanted-type'] = $unwantedFileExtensionDetails;
709 $fileSizeWarnings = $this->
checkFileSize( $this->mFileSize );
710 if ( $fileSizeWarnings ) {
711 $warnings = array_merge( $warnings, $fileSizeWarnings );
715 if ( $localFileExistsWarnings ) {
716 $warnings = array_merge( $warnings, $localFileExistsWarnings );
720 $warnings[
'was-deleted'] = $filename;
725 $ignoreLocalDupes = isset( $warnings[
'exists'] );
728 $warnings[
'duplicate'] = $dupes;
732 if ( $archivedDupes !==
null ) {
733 $warnings[
'duplicate-archive'] = $archivedDupes;
751 array_walk_recursive( $warnings,
function ( &$param, $key ) {
752 if ( $param instanceof
File ) {
754 'fileName' => $param->getName(),
755 'timestamp' => $param->getTimestamp()
757 } elseif ( is_object( $param ) ) {
758 throw new InvalidArgumentException(
759 'UploadBase::makeWarningsSerializable: ' .
760 'Unexpected object of class ' . get_class( $param ) );
776 $comparableName = str_replace(
' ',
'_', $desiredFileName );
777 $comparableName = Title::capitalize( $comparableName,
NS_FILE );
779 if ( $desiredFileName != $filename && $comparableName != $filename ) {
802 $wgLang->commaList( $extensions ),
822 $warnings[
'large-file'] = [
828 if ( $fileSize == 0 ) {
829 $warnings[
'empty-file'] =
true;
844 $exists = self::getExistsWarning( $localFile );
845 if ( $exists !==
false ) {
846 $warnings[
'exists'] = $exists;
849 if ( $hash === $localFile->
getSha1() ) {
850 $warnings[
'no-change'] = $localFile;
855 foreach ( $history as $oldFile ) {
856 if ( $hash === $oldFile->getSha1() ) {
857 $warnings[
'duplicate-version'][] = $oldFile;
876 $dupes = MediaWikiServices::getInstance()->getRepoGroup()->findBySha1( $hash );
878 foreach ( $dupes as $key => $dupe ) {
882 $title->equals( $dupe->getTitle() )
884 unset( $dupes[$key] );
900 if ( $archivedFile->getID() > 0 ) {
901 if ( $archivedFile->userCan( File::DELETED_FILE, $user ) ) {
902 return $archivedFile->getName();
929 $comment, $pageText, $watch, $user, $tags = [], ?
string $watchlistExpiry =
null
932 $props = $this->mFileProps;
935 $this->getHookRunner()->onUploadVerifyUpload( $this, $user, $props, $comment, $pageText, $error );
937 if ( !is_array( $error ) ) {
940 return Status::newFatal( ...$error );
954 if ( $status->isGood() ) {
963 $this->getHookRunner()->onUploadComplete( $this );
987 if ( $this->mTitle !==
false ) {
990 if ( !is_string( $this->mDesiredDestName ) ) {
991 $this->mTitleError = self::ILLEGAL_FILENAME;
992 $this->mTitle =
null;
999 $title = Title::newFromText( $this->mDesiredDestName );
1001 $this->mFilteredName =
$title->getDBkey();
1003 $this->mFilteredName = $this->mDesiredDestName;
1006 # oi_archive_name is max 255 bytes, which include a timestamp and an
1007 # exclamation mark, so restrict file name to 240 bytes.
1008 if ( strlen( $this->mFilteredName ) > 240 ) {
1009 $this->mTitleError = self::FILENAME_TOO_LONG;
1010 $this->mTitle =
null;
1022 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1023 if ( $nt ===
null ) {
1024 $this->mTitleError = self::ILLEGAL_FILENAME;
1025 $this->mTitle =
null;
1029 $this->mFilteredName = $nt->
getDBkey();
1037 if (
$ext !== [] ) {
1038 $this->mFinalExtension = trim( end(
$ext ) );
1040 $this->mFinalExtension =
'';
1045 if ( $this->mTempPath !==
null ) {
1046 $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
1047 $mime = $magic->guessMimeType( $this->mTempPath );
1048 if (
$mime !==
'unknown/unknown' ) {
1049 # Get a space separated list of extensions
1050 $mimeExt = $magic->getExtensionFromMimeTypeOrNull(
$mime );
1051 if ( $mimeExt !==
null ) {
1052 # Set the extension to the canonical extension
1053 $this->mFinalExtension = $mimeExt;
1055 # Fix up the other variables
1056 $this->mFilteredName .=
".{$this->mFinalExtension}";
1057 $nt = Title::makeTitleSafe(
NS_FILE, $this->mFilteredName );
1058 $ext = [ $this->mFinalExtension ];
1070 if ( $this->mFinalExtension ==
'' ) {
1071 $this->mTitleError = self::FILETYPE_MISSING;
1072 $this->mTitle =
null;
1075 } elseif ( $blackListedExtensions ||
1079 $this->mBlackListedExtensions = $blackListedExtensions;
1080 $this->mTitleError = self::FILETYPE_BADTYPE;
1081 $this->mTitle =
null;
1087 if ( !preg_match(
'/^[\x0-\x7f]*$/', $nt->getText() )
1088 && !MediaWikiServices::getInstance()->getRepoGroup()
1089 ->getLocalRepo()->backendSupportsUnicodePaths()
1091 $this->mTitleError = self::WINDOWS_NONASCII_FILENAME;
1092 $this->mTitle =
null;
1097 # If there was more than one "extension", reassemble the base
1098 # filename to prevent bogus complaints about length
1099 if ( count(
$ext ) > 1 ) {
1100 $iterations = count(
$ext ) - 1;
1101 for ( $i = 0; $i < $iterations; $i++ ) {
1102 $partname .=
'.' .
$ext[$i];
1106 if ( strlen( $partname ) < 1 ) {
1107 $this->mTitleError = self::MIN_LENGTH_PARTNAME;
1108 $this->mTitle =
null;
1113 $this->mTitle = $nt;
1125 if ( $this->mLocalFile ===
null ) {
1127 $this->mLocalFile = $nt ===
null
1129 : MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $nt );
1132 return $this->mLocalFile;
1139 return $this->mStashFile;
1155 if ( !$isPartial ) {
1158 return Status::newFatal( ...$error );
1163 return Status::newGood(
$file );
1165 return Status::newFatal(
'uploadstash-exception', get_class( $e ), $e->getMessage() );
1174 $props = $this->mFileProps;
1176 $this->getHookRunner()->onUploadStashFile( $this, $user, $props, $error );
1177 if ( $error && !is_array( $error ) ) {
1178 $error = [ $error ];
1216 $stash = MediaWikiServices::getInstance()->getRepoGroup()
1217 ->getLocalRepo()->getUploadStash( $user );
1219 $this->mStashFile =
$file;
1229 if ( $this->mRemoveTempFile && $this->tempFileObj ) {
1231 wfDebug( __METHOD__ .
": Marked temporary file '{$this->mTempPath}' for removal" );
1232 $this->tempFileObj->autocollect();
1237 return $this->mTempPath;
1250 $bits = explode(
'.', $filename );
1251 $basename = array_shift( $bits );
1253 return [ $basename, $bits ];
1265 return in_array( strtolower(
$ext ), $list );
1277 return array_intersect( array_map(
'strtolower',
$ext ), $list );
1288 $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
1291 if ( !$magic->isRecognizableExtension( $extension ) ) {
1292 wfDebug( __METHOD__ .
": passing file with unknown detected mime type; " .
1293 "unrecognized extension '$extension', can't verify" );
1297 wfDebug( __METHOD__ .
": rejecting file with unknown detected mime type; " .
1298 "recognized extension '$extension', so probably invalid file" );
1304 $match = $magic->isMatchingExtension( $extension,
$mime );
1306 if ( $match ===
null ) {
1307 if ( $magic->getTypesForExtension( $extension ) !==
null ) {
1308 wfDebug( __METHOD__ .
": No extension known for $mime, but we know a mime for $extension" );
1312 wfDebug( __METHOD__ .
": no file extension known for mime type $mime, passing file" );
1316 } elseif ( $match ===
true ) {
1317 wfDebug( __METHOD__ .
": mime type $mime matches extension $extension, passing file" );
1323 .
": mime type $mime mismatches file extension $extension, rejecting file" );
1341 # ugly hack: for text files, always look at the entire file.
1342 # For binary field, just check the first K.
1344 $isText = strpos(
$mime,
'text/' ) === 0;
1346 $chunk = file_get_contents(
$file );
1348 $fp = fopen(
$file,
'rb' );
1349 $chunk = fread( $fp, 1024 );
1353 $chunk = strtolower( $chunk );
1359 # decode from UTF-16 if needed (could be used for obfuscation).
1360 if ( substr( $chunk, 0, 2 ) ==
"\xfe\xff" ) {
1362 } elseif ( substr( $chunk, 0, 2 ) ==
"\xff\xfe" ) {
1368 if ( $enc !==
null ) {
1369 $chunk = iconv( $enc,
"ASCII//IGNORE", $chunk );
1372 $chunk = trim( $chunk );
1375 wfDebug( __METHOD__ .
": checking for embedded scripts and HTML stuff" );
1377 # check for HTML doctype
1378 if ( preg_match(
"/<!DOCTYPE *X?HTML/i", $chunk ) ) {
1384 if ( $extension ==
'svg' || strpos(
$mime,
'image/svg' ) === 0 ) {
1385 if ( self::checkXMLEncodingMissmatch(
$file ) ) {
1399 '<html', # also in safari
1400 '<script', # also in safari
1403 foreach ( $tags as $tag ) {
1404 if ( strpos( $chunk, $tag ) !==
false ) {
1405 wfDebug( __METHOD__ .
": found something that may make it be mistaken for html: $tag" );
1415 # resolve entity-refs to look at attributes. may be harsh on big files... cache result?
1416 $chunk = Sanitizer::decodeCharReferences( $chunk );
1418 # look for script-types
1419 if ( preg_match(
'!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim', $chunk ) ) {
1420 wfDebug( __METHOD__ .
": found script types" );
1425 # look for html-style script-urls
1426 if ( preg_match(
'!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
1427 wfDebug( __METHOD__ .
": found html-style script urls" );
1432 # look for css-style script-urls
1433 if ( preg_match(
'!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
1434 wfDebug( __METHOD__ .
": found css-style script urls" );
1439 wfDebug( __METHOD__ .
": no scripts found" );
1454 $encodingRegex =
'!encoding[ \t\n\r]*=[ \t\n\r]*[\'"](.*?)[\'"]!si';
1456 if ( preg_match(
"!<\?xml\b(.*?)\?>!si", $contents,
$matches ) ) {
1457 if ( preg_match( $encodingRegex,
$matches[1], $encMatch )
1458 && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1460 wfDebug( __METHOD__ .
": Found unsafe XML encoding '{$encMatch[1]}'" );
1464 } elseif ( preg_match(
"!<\?xml\b!si", $contents ) ) {
1467 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1470 } elseif ( substr( $contents, 0, 4 ) ==
"\x4C\x6F\xA7\x94" ) {
1472 wfDebug( __METHOD__ .
": EBCDIC Encoded XML" );
1479 $attemptEncodings = [
'UTF-16',
'UTF-16BE',
'UTF-32',
'UTF-32BE' ];
1480 foreach ( $attemptEncodings as $encoding ) {
1481 Wikimedia\suppressWarnings();
1482 $str = iconv( $encoding,
'UTF-8', $contents );
1483 Wikimedia\restoreWarnings();
1484 if ( $str !=
'' && preg_match(
"!<\?xml\b(.*?)\?>!si", $str,
$matches ) ) {
1485 if ( preg_match( $encodingRegex,
$matches[1], $encMatch )
1486 && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1488 wfDebug( __METHOD__ .
": Found unsafe XML encoding '{$encMatch[1]}'" );
1492 } elseif ( $str !=
'' && preg_match(
"!<\?xml\b!si", $str ) ) {
1495 wfDebug( __METHOD__ .
": Unmatched XML declaration start" );
1510 $this->mSVGNSError =
false;
1513 [ $this,
'checkSvgScriptCallback' ],
1516 'processing_instruction_handler' => [ __CLASS__,
'checkSvgPICallback' ],
1517 'external_dtd_handler' => [ __CLASS__,
'checkSvgExternalDTD' ],
1520 if ( $check->wellFormed !==
true ) {
1523 return $partial ? false : [
'uploadinvalidxml' ];
1524 } elseif ( $check->filterMatch ) {
1525 if ( $this->mSVGNSError ) {
1526 return [
'uploadscriptednamespace', $this->mSVGNSError ];
1529 return $check->filterMatchType;
1543 if ( preg_match(
'/xml-stylesheet/i', $target ) ) {
1544 return [
'upload-scripted-pi-callback' ];
1565 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd',
1566 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd',
1567 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd',
1568 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd',
1570 'http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd',
1572 if (
$type !==
'PUBLIC'
1573 || !in_array( $systemId, $allowedDTDs )
1574 || strpos( $publicId,
"-//W3C//" ) !== 0
1576 return [
'upload-scripted-dtd' ];
1593 static $validNamespaces = [
1596 'http://creativecommons.org/ns#',
1597 'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd',
1598 'http://ns.adobe.com/adobeillustrator/10.0/',
1599 'http://ns.adobe.com/adobesvgviewerextensions/3.0/',
1600 'http://ns.adobe.com/extensibility/1.0/',
1601 'http://ns.adobe.com/flows/1.0/',
1602 'http://ns.adobe.com/illustrator/1.0/',
1603 'http://ns.adobe.com/imagereplacement/1.0/',
1604 'http://ns.adobe.com/pdf/1.3/',
1605 'http://ns.adobe.com/photoshop/1.0/',
1606 'http://ns.adobe.com/saveforweb/1.0/',
1607 'http://ns.adobe.com/variables/1.0/',
1608 'http://ns.adobe.com/xap/1.0/',
1609 'http://ns.adobe.com/xap/1.0/g/',
1610 'http://ns.adobe.com/xap/1.0/g/img/',
1611 'http://ns.adobe.com/xap/1.0/mm/',
1612 'http://ns.adobe.com/xap/1.0/rights/',
1613 'http://ns.adobe.com/xap/1.0/stype/dimensions#',
1614 'http://ns.adobe.com/xap/1.0/stype/font#',
1615 'http://ns.adobe.com/xap/1.0/stype/manifestitem#',
1616 'http://ns.adobe.com/xap/1.0/stype/resourceevent#',
1617 'http://ns.adobe.com/xap/1.0/stype/resourceref#',
1618 'http://ns.adobe.com/xap/1.0/t/pg/',
1619 'http://purl.org/dc/elements/1.1/',
1620 'http://purl.org/dc/elements/1.1',
1621 'http://schemas.microsoft.com/visio/2003/svgextensions/',
1622 'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd',
1623 'http://taptrix.com/inkpad/svg_extensions',
1624 'http://web.resource.org/cc/',
1625 'http://www.freesoftware.fsf.org/bkchem/cdml',
1626 'http://www.inkscape.org/namespaces/inkscape',
1627 'http://www.opengis.net/gml',
1628 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
1629 'http://www.w3.org/2000/svg',
1630 'http://www.w3.org/tr/rec-rdf-syntax/',
1631 'http://www.w3.org/2000/01/rdf-schema#',
1636 $isBuggyInkscape = preg_match(
'/^&(#38;)*ns_[a-z_]+;$/', $namespace );
1638 if ( !( $isBuggyInkscape || in_array( $namespace, $validNamespaces ) ) ) {
1639 wfDebug( __METHOD__ .
": Non-svg namespace '$namespace' in uploaded file." );
1641 $this->mSVGNSError = $namespace;
1649 if ( $strippedElement ==
'script' ) {
1650 wfDebug( __METHOD__ .
": Found script element '$element' in uploaded file." );
1652 return [
'uploaded-script-svg', $strippedElement ];
1655 # e.g., <svg xmlns="http://www.w3.org/2000/svg">
1656 # <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
1657 if ( $strippedElement ==
'handler' ) {
1658 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1660 return [
'uploaded-script-svg', $strippedElement ];
1663 # SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
1664 if ( $strippedElement ==
'stylesheet' ) {
1665 wfDebug( __METHOD__ .
": Found scriptable element '$element' in uploaded file." );
1667 return [
'uploaded-script-svg', $strippedElement ];
1670 # Block iframes, in case they pass the namespace check
1671 if ( $strippedElement ==
'iframe' ) {
1672 wfDebug( __METHOD__ .
": iframe in uploaded file." );
1674 return [
'uploaded-script-svg', $strippedElement ];
1678 if ( $strippedElement ==
'style'
1679 && self::checkCssFragment( Sanitizer::normalizeCss( $data ) )
1681 wfDebug( __METHOD__ .
": hostile css in style element." );
1682 return [
'uploaded-hostile-svg' ];
1685 foreach ( $attribs as $attrib => $value ) {
1687 $value = strtolower( $value );
1689 if ( substr( $stripped, 0, 2 ) ==
'on' ) {
1691 .
": Found event-handler attribute '$attrib'='$value' in uploaded file." );
1693 return [
'uploaded-event-handler-on-svg', $attrib, $value ];
1696 # Do not allow relative links, or unsafe url schemas.
1697 # For <a> tags, only data:, http: and https: and same-document
1698 # fragment links are allowed. For all other tags, only data:
1699 # and fragment are allowed.
1700 if ( $stripped ==
'href'
1702 && strpos( $value,
'data:' ) !== 0
1703 && strpos( $value,
'#' ) !== 0
1705 if ( !( $strippedElement ===
'a'
1706 && preg_match(
'!^https?://!i', $value ) )
1708 wfDebug( __METHOD__ .
": Found href attribute <$strippedElement "
1709 .
"'$attrib'='$value' in uploaded file." );
1711 return [
'uploaded-href-attribute-svg', $strippedElement, $attrib, $value ];
1715 # only allow data: targets that should be safe. This prevents vectors like,
1716 # image/svg, text/xml, application/xml, and text/html, which can contain scripts
1717 if ( $stripped ==
'href' && strncasecmp(
'data:', $value, 5 ) === 0 ) {
1720 $parameters =
'(?>;[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+=(?>[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+|"(?>[\0-\x0c\x0e-\x21\x23-\x5b\x5d-\x7f]+|\\\\[\0-\x7f])*"))*(?:;base64)?';
1722 if ( !preg_match(
"!^data:\s*image/(gif|jpeg|jpg|png)$parameters,!i", $value ) ) {
1723 wfDebug( __METHOD__ .
": Found href to unwhitelisted data: uri "
1724 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1725 return [
'uploaded-href-unsafe-target-svg', $strippedElement, $attrib, $value ];
1729 # Change href with animate from (http:
1730 if ( $stripped ===
'attributename'
1731 && $strippedElement ===
'animate'
1734 wfDebug( __METHOD__ .
": Found animate that might be changing href using from "
1735 .
"\"<$strippedElement '$attrib'='$value'...\" in uploaded file." );
1737 return [
'uploaded-animate-svg', $strippedElement, $attrib, $value ];
1740 # use set/animate to add event-handler attribute to parent
1741 if ( ( $strippedElement ==
'set' || $strippedElement ==
'animate' )
1742 && $stripped ==
'attributename'
1743 && substr( $value, 0, 2 ) ==
'on'
1745 wfDebug( __METHOD__ .
": Found svg setting event-handler attribute with "
1746 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1748 return [
'uploaded-setting-event-handler-svg', $strippedElement, $stripped, $value ];
1751 # use set to add href attribute to parent element
1752 if ( $strippedElement ==
'set'
1753 && $stripped ==
'attributename'
1754 && strpos( $value,
'href' ) !==
false
1756 wfDebug( __METHOD__ .
": Found svg setting href attribute '$value' in uploaded file." );
1758 return [
'uploaded-setting-href-svg' ];
1761 # use set to add a remote / data / script target to an element
1762 if ( $strippedElement ==
'set'
1763 && $stripped ==
'to'
1764 && preg_match(
'!(http|https|data|script):!sim', $value )
1766 wfDebug( __METHOD__ .
": Found svg setting attribute to '$value' in uploaded file." );
1768 return [
'uploaded-wrong-setting-svg', $value ];
1771 # use handler attribute with remote / data / script
1772 if ( $stripped ==
'handler' && preg_match(
'!(http|https|data|script):!sim', $value ) ) {
1773 wfDebug( __METHOD__ .
": Found svg setting handler with remote/data/script "
1774 .
"'$attrib'='$value' in uploaded file." );
1776 return [
'uploaded-setting-handler-svg', $attrib, $value ];
1779 # use CSS styles to bring in remote code
1780 if ( $stripped ==
'style'
1781 && self::checkCssFragment( Sanitizer::normalizeCss( $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 # Several attributes can include css, css character escaping isn't allowed
1789 $cssAttrs = [
'font',
'clip-path',
'fill',
'filter',
'marker',
1790 'marker-end',
'marker-mid',
'marker-start',
'mask',
'stroke' ];
1791 if ( in_array( $stripped, $cssAttrs )
1792 && self::checkCssFragment( $value )
1794 wfDebug( __METHOD__ .
": Found svg setting a style with "
1795 .
"remote url '$attrib'='$value' in uploaded file." );
1796 return [
'uploaded-remote-url-svg', $attrib, $value ];
1799 # image filters can pull in url, which could be svg that executes scripts
1800 # Only allow url( "#foo" ). Do not allow url( http:
1801 if ( $strippedElement ==
'image'
1802 && $stripped ==
'filter'
1803 && preg_match(
'!url\s*\(\s*["\']?[^#]!sim', $value )
1805 wfDebug( __METHOD__ .
": Found image filter with url: "
1806 .
"\"<$strippedElement $stripped='$value'...\" in uploaded file." );
1808 return [
'uploaded-image-filter-svg', $strippedElement, $stripped, $value ];
1822 # Forbid external stylesheets, for both reliability and to protect viewer's privacy
1823 if ( stripos( $value,
'@import' ) !==
false ) {
1827 # We allow @font-face to embed fonts with data: urls, so we snip the string
1828 # 'url' out so this case won't match when we check for urls below
1829 $pattern =
'!(@font-face\s*{[^}]*src:)url(\("data:;base64,)!im';
1830 $value = preg_replace( $pattern,
'$1$2', $value );
1832 # Check for remote and executable CSS. Unlike in Sanitizer::checkCss, the CSS
1833 # properties filter and accelerator don't seem to be useful for xss in SVG files.
1834 # Expression and -o-link don't seem to work either, but filtering them here in case.
1835 # Additionally, we catch remote urls like url("http:..., url('http:..., url(http:...,
1836 # but not local ones such as url("#..., url('#..., url(#....
1837 if ( preg_match(
'!expression
1839 | -o-link-source\s*:
1840 | -o-replace\s*:!imx', $value ) ) {
1844 if ( preg_match_all(
1845 "!(\s*(url|image|image-set)\s*\(\s*[\"']?\s*[^#]+.*?\))!sim",
1850 # TODO: redo this in one regex. Until then, url("#whatever") matches the first
1851 foreach (
$matches[1] as $match ) {
1852 if ( !preg_match(
"!\s*(url|image|image-set)\s*\(\s*(#|'#|\"#)!im", $match ) ) {
1858 if ( preg_match(
'/[\000-\010\013\016-\037\177]/', $value ) ) {
1872 $parts = explode(
':', strtolower( $element ) );
1873 $name = array_pop( $parts );
1874 $ns = implode(
':', $parts );
1876 return [ $ns, $name ];
1885 $parts = explode(
':', strtolower( $name ) );
1887 return array_pop( $parts );
1904 wfDebug( __METHOD__ .
": virus scanner disabled" );
1910 wfDebug( __METHOD__ .
": unknown virus scanner: $wgAntivirus" );
1911 $wgOut->wrapWikiMsg(
"<div class=\"error\">\n$1\n</div>",
1914 return wfMessage(
'virus-unknownscanner' )->text() .
" $wgAntivirus";
1917 # look up scanner configuration
1922 if ( strpos(
$command,
"%f" ) ===
false ) {
1923 # simple pattern: append file to scan
1926 # complex pattern: replace "%f" with file to scan
1930 wfDebug( __METHOD__ .
": running virus scan: $command " );
1932 # execute virus scanner
1935 # NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
1936 # that does not seem to be worth the pain.
1937 # Ask me (Duesentrieb) about it if it's ever needed.
1940 # map exit code to AV_xxx constants.
1941 $mappedCode = $exitCode;
1942 if ( $exitCodeMap ) {
1943 if ( isset( $exitCodeMap[$exitCode] ) ) {
1944 $mappedCode = $exitCodeMap[$exitCode];
1945 } elseif ( isset( $exitCodeMap[
"*"] ) ) {
1946 $mappedCode = $exitCodeMap[
"*"];
1954 # scan failed (code was mapped to false by $exitCodeMap)
1955 wfDebug( __METHOD__ .
": failed to scan $file (code $exitCode)." );
1958 ?
wfMessage(
'virus-scanfailed', [ $exitCode ] )->text()
1961 # scan failed because filetype is unknown (probably imune)
1962 wfDebug( __METHOD__ .
": unsupported file type $file (code $exitCode)." );
1966 wfDebug( __METHOD__ .
": file passed virus scan." );
1969 $output = trim( $output );
1972 $output =
true; #
if there
's no output, return true
1973 } elseif ( $msgPattern ) {
1975 if ( preg_match( $msgPattern, $output, $groups ) && $groups[1] ) {
1976 $output = $groups[1];
1980 wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output" );
1994 private function checkOverwrite( $user ) {
1995 // First check whether the local file can be overwritten
1996 $file = $this->getLocalFile();
1997 $file->load( File::READ_LATEST );
1998 if ( $file->exists() ) {
1999 if ( !self::userCanReUpload( $user, $file ) ) {
2000 return [ 'fileexists-forbidden
', $file->getName() ];
2006 $services = MediaWikiServices::getInstance();
2008 /* Check shared conflicts: if the local file does not exist, but
2009 * RepoGroup::findFile finds a file, it exists in a shared repository.
2011 $file = $services->getRepoGroup()->findFile( $this->getTitle(), [ 'latest
' => true ] );
2012 if ( $file && !$services->getPermissionManager()
2013 ->userHasRight( $user, 'reupload-shared
' )
2015 return [ 'fileexists-shared-forbidden
', $file->getName() ];
2028 public static function userCanReUpload( User $user, File $img ) {
2029 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
2030 if ( $permissionManager->userHasRight( $user, 'reupload
' ) ) {
2031 return true; // non-conditional
2032 } elseif ( !$permissionManager->userHasRight( $user, 'reupload-own
' ) ) {
2036 if ( !( $img instanceof LocalFile ) ) {
2042 return $user->getId() == $img->getUser( 'id' );
2056 public static function getExistsWarning( $file ) {
2057 if ( $file->exists() ) {
2058 return [ 'warning
' => 'exists
', 'file
' => $file ];
2061 if ( $file->getTitle()->getArticleID() ) {
2062 return [ 'warning
' => 'page-exists
', 'file
' => $file ];
2065 if ( strpos( $file->getName(), '.
' ) == false ) {
2066 $partname = $file->getName();
2069 $n = strrpos( $file->getName(), '.
' );
2070 $extension = substr( $file->getName(), $n + 1 );
2071 $partname = substr( $file->getName(), 0, $n );
2073 $normalizedExtension = File::normalizeExtension( $extension );
2074 $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
2076 if ( $normalizedExtension != $extension ) {
2077 // We're not
using the normalized form of the extension.
2082 $nt_lc = Title::makeTitle(
NS_FILE,
"{$partname}.{$normalizedExtension}" );
2083 $file_lc = $localRepo->newFile( $nt_lc );
2085 if ( $file_lc->exists() ) {
2087 'warning' =>
'exists-normalized',
2089 'normalizedFile' => $file_lc
2095 $similarFiles = $localRepo->findFilesByPrefix(
"{$partname}.", 1 );
2096 if ( count( $similarFiles ) ) {
2098 'warning' =>
'exists-normalized',
2100 'normalizedFile' => $similarFiles[0],
2104 if ( self::isThumbName(
$file->getName() ) ) {
2105 # Check for filenames like 50px- or 180px-, these are mostly thumbnails
2107 substr( $partname, strpos( $partname,
'-' ) + 1 ) .
'.' . $extension,
2110 $file_thb = $localRepo->newFile( $nt_thb );
2111 if ( $file_thb->exists() ) {
2113 'warning' =>
'thumb',
2115 'thumbFile' => $file_thb
2120 'warning' =>
'thumb-name',
2122 'thumbFile' => $file_thb
2127 foreach ( self::getFilenamePrefixBlacklist() as $prefix ) {
2128 if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
2130 'warning' =>
'bad-prefix',
2146 $n = strrpos( $filename,
'.' );
2147 $partname = $n ? substr( $filename, 0, $n ) : $filename;
2150 substr( $partname, 3, 3 ) ==
'px-' ||
2151 substr( $partname, 2, 3 ) ==
'px-'
2153 preg_match(
"/[0-9]{2}/", substr( $partname, 0, 2 ) );
2163 $message =
wfMessage(
'filename-prefix-blacklist' )->inContentLanguage();
2164 if ( !$message->isDisabled() ) {
2165 $lines = explode(
"\n", $message->plain() );
2168 $comment = substr( trim(
$line ), 0, 1 );
2169 if ( $comment ==
'#' || $comment ==
'' ) {
2173 $comment = strpos(
$line,
'#' );
2174 if ( $comment > 0 ) {
2177 $blacklist[] = trim(
$line );
2216 $code = $error[
'status'];
2217 unset( $code[
'status'] );
2252 ini_get(
'upload_max_filesize' ),
2256 ini_get(
'post_max_size' ),
2259 return min( $phpMaxFileSize, $phpMaxPostSize );
2272 $store = self::getUploadSessionStore();
2273 $key = self::getUploadSessionKey( $store, $user, $statusKey );
2275 return $store->get( $key );
2291 $store = self::getUploadSessionStore();
2292 $key = self::getUploadSessionKey( $store, $user, $statusKey );
2294 if ( $value ===
false ) {
2295 $store->delete( $key );
2297 $store->set( $key, $value, $store::TTL_DAY );
2319 return ObjectCache::getInstance(
'db-replicated' );
$wgAntivirus
Internal name of virus scanner.
$wgFileExtensions
This is the list of preferred extensions for uploading files.
$wgCheckFileExtensions
This is a flag to determine whether or not to check file extensions on upload.
$wgAntivirusRequired
Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected.
$wgUploadSizeWarning
Warn if uploaded files are larger than this (in bytes), or false to disable.
$wgDisableUploadScriptChecks
Setting this to true will disable the upload system's checks for HTML/JavaScript.
$wgVerifyMimeType
Determines if the MIME type of uploaded files should be checked.
$wgAntivirusSetup
Configuration for different virus scanners.
$wgVerifyMimeTypeIE
Determines whether extra checks for IE type detection should be applied.
$wgFileBlacklist
Files with these extensions will never be allowed as uploads.
$wgEnableUploads
Allow users to upload files.
$wgAllowJavaUploads
Allow Java archive uploads.
$wgStrictFileExtensions
If this is turned off, users may override the warning for files not covered by $wgFileExtensions.
$wgMimeTypeBlacklist
Files with these MIME types will never be allowed as uploads if $wgVerifyMimeType is enabled.
$wgMaxUploadSize
Max size for uploads, in bytes.
$wgSVGMetadataCutoff
Don't read SVG metadata beyond this point.
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.
wfArrayDiff2( $a, $b)
Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
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 $function is deprecated.
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( $class,... $components)
Make a cache key, scoped to this instance's 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...
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
getSha1()
Stable to override.
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.
__construct()
Stable to call.
getSourceType()
Returns the upload type.
checkOverwrite( $user)
Check if there's an overwrite conflict and, if so, if restrictions forbid this user from performing t...
static makeWarningsSerializable( $warnings)
Convert the warnings array returned by checkWarnings() to something that can be serialized.
string null $mRemoveTempFile
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.
verifyTitlePermissions( $user)
Check whether the user can edit, upload and create the image.
checkSvgScriptCallback( $element, $attribs, $data=null)
checkLocalFileExists(LocalFile $localFile, $hash)
getLocalFile()
Return the local file and initializes if necessary.
stripXmlNamespace( $name)
string $mTempPath
Local file system path to the file to upload (or a local copy)
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.
verifyPermissions( $user)
Alias for verifyTitlePermissions.
runUploadStashFileHook(User $user)
static getSessionStatus(User $user, $statusKey)
Get the current status of a chunked upload (used for polling)
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.
static isAllowed(UserIdentity $user)
Returns true if the user can use this upload module or else a string identifying the missing permissi...
static getFilenamePrefixBlacklist()
Get a list of blacklisted filename prefixes from [[MediaWiki:Filename-prefix-blacklist]].
const OVERWRITE_EXISTING_FILE
setTempFile( $tempPath, $fileSize=null)
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
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.
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.
static checkSvgExternalDTD( $type, $publicId, $systemId)
Verify that DTD urls referenced are only the standard dtds.
getTempFileSha1Base36()
Get the base 36 SHA1 of the file Stable to override.
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.
static getUploadSessionKey(BagOStuff $store, User $user, $statusKey)
fetchFile()
Fetch the file.
checkAgainstArchiveDupes( $hash, User $user)
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.
checkLocalFileWasDeleted(LocalFile $localFile)
getFileSize()
Return the file size.
verifyUpload()
Verify whether the upload is sane.
stashFile(User $user=null)
If the user does not supply all necessary information in the first upload form submission (either by ...
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)
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.
static setSessionStatus(User $user, $statusKey, $value)
Set the current status of a chunked upload (used for polling)
initializeFromRequest(&$request)
Initialize from a WebRequest.
checkAgainstExistingDupes( $hash, $ignoreLocalDupes)
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
getName()
Get the user name, or the IP of an anonymous user.
getId()
Get the user's ID.
static doWatch(Title $title, User $user, $checkRights=User::CHECK_USER_RIGHTS, ?string $expiry=null)
Watch a page.
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