MediaWiki  master
UploadBase.php
Go to the documentation of this file.
1 <?php
38 abstract class UploadBase {
39  protected $mTempPath;
41  protected $mTitle = false, $mTitleError = 0;
46 
47  protected static $safeXmlEncodings = array(
48  'UTF-8',
49  'ISO-8859-1',
50  'ISO-8859-2',
51  'UTF-16',
52  'UTF-32'
53  );
54 
55  const SUCCESS = 0;
56  const OK = 0;
57  const EMPTY_FILE = 3;
59  const ILLEGAL_FILENAME = 5;
60  const OVERWRITE_EXISTING_FILE = 7; # Not used anymore; handled by verifyTitlePermissions()
61  const FILETYPE_MISSING = 8;
62  const FILETYPE_BADTYPE = 9;
63  const VERIFICATION_ERROR = 10;
64 
65  # HOOK_ABORTED is the new name of UPLOAD_VERIFICATION_ERROR
67  const HOOK_ABORTED = 11;
68  const FILE_TOO_LARGE = 12;
70  const FILENAME_TOO_LONG = 14;
71 
76  public function getVerificationErrorCode( $error ) {
77  $code_to_status = array(
78  self::EMPTY_FILE => 'empty-file',
79  self::FILE_TOO_LARGE => 'file-too-large',
80  self::FILETYPE_MISSING => 'filetype-missing',
81  self::FILETYPE_BADTYPE => 'filetype-banned',
82  self::MIN_LENGTH_PARTNAME => 'filename-tooshort',
83  self::ILLEGAL_FILENAME => 'illegal-filename',
84  self::OVERWRITE_EXISTING_FILE => 'overwrite',
85  self::VERIFICATION_ERROR => 'verification-error',
86  self::HOOK_ABORTED => 'hookaborted',
87  self::WINDOWS_NONASCII_FILENAME => 'windows-nonascii-filename',
88  self::FILENAME_TOO_LONG => 'filename-toolong',
89  );
90  if ( isset( $code_to_status[$error] ) ) {
91  return $code_to_status[$error];
92  }
93 
94  return 'unknown-error';
95  }
96 
102  public static function isEnabled() {
104 
105  if ( !$wgEnableUploads ) {
106  return false;
107  }
108 
109  # Check php's file_uploads setting
110  return wfIsHHVM() || wfIniGetBool( 'file_uploads' );
111  }
112 
121  public static function isAllowed( $user ) {
122  foreach ( array( 'upload', 'edit' ) as $permission ) {
123  if ( !$user->isAllowed( $permission ) ) {
124  return $permission;
125  }
126  }
127 
128  return true;
129  }
130 
131  // Upload handlers. Should probably just be a global.
132  private static $uploadHandlers = array( 'Stash', 'File', 'Url' );
133 
141  public static function createFromRequest( &$request, $type = null ) {
142  $type = $type ? $type : $request->getVal( 'wpSourceType', 'File' );
143 
144  if ( !$type ) {
145  return null;
146  }
147 
148  // Get the upload class
149  $type = ucfirst( $type );
150 
151  // Give hooks the chance to handle this request
152  $className = null;
153  Hooks::run( 'UploadCreateFromRequest', array( $type, &$className ) );
154  if ( is_null( $className ) ) {
155  $className = 'UploadFrom' . $type;
156  wfDebug( __METHOD__ . ": class name: $className\n" );
157  if ( !in_array( $type, self::$uploadHandlers ) ) {
158  return null;
159  }
160  }
161 
162  // Check whether this upload class is enabled
163  if ( !call_user_func( array( $className, 'isEnabled' ) ) ) {
164  return null;
165  }
166 
167  // Check whether the request is valid
168  if ( !call_user_func( array( $className, 'isValidRequest' ), $request ) ) {
169  return null;
170  }
171 
173  $handler = new $className;
174 
175  $handler->initializeFromRequest( $request );
176 
177  return $handler;
178  }
179 
185  public static function isValidRequest( $request ) {
186  return false;
187  }
188 
189  public function __construct() {
190  }
191 
198  public function getSourceType() {
199  return null;
200  }
201 
210  public function initializePathInfo( $name, $tempPath, $fileSize, $removeTempFile = false ) {
211  $this->mDesiredDestName = $name;
212  if ( FileBackend::isStoragePath( $tempPath ) ) {
213  throw new MWException( __METHOD__ . " given storage path `$tempPath`." );
214  }
215  $this->mTempPath = $tempPath;
216  $this->mFileSize = $fileSize;
217  $this->mRemoveTempFile = $removeTempFile;
218  }
219 
225  abstract public function initializeFromRequest( &$request );
226 
231  public function fetchFile() {
232  return Status::newGood();
233  }
234 
239  public function isEmptyFile() {
240  return empty( $this->mFileSize );
241  }
242 
247  public function getFileSize() {
248  return $this->mFileSize;
249  }
250 
255  public function getTempFileSha1Base36() {
256  return FSFile::getSha1Base36FromPath( $this->mTempPath );
257  }
258 
263  function getRealPath( $srcPath ) {
264  $repo = RepoGroup::singleton()->getLocalRepo();
265  if ( $repo->isVirtualUrl( $srcPath ) ) {
269  $tmpFile = $repo->getLocalCopy( $srcPath );
270  if ( $tmpFile ) {
271  $tmpFile->bind( $this ); // keep alive with $this
272  }
273  $path = $tmpFile ? $tmpFile->getPath() : false;
274  } else {
275  $path = $srcPath;
276  }
277 
278  return $path;
279  }
280 
285  public function verifyUpload() {
286 
290  if ( $this->isEmptyFile() ) {
291 
292  return array( 'status' => self::EMPTY_FILE );
293  }
294 
298  $maxSize = self::getMaxUploadSize( $this->getSourceType() );
299  if ( $this->mFileSize > $maxSize ) {
300 
301  return array(
302  'status' => self::FILE_TOO_LARGE,
303  'max' => $maxSize,
304  );
305  }
306 
312  $verification = $this->verifyFile();
313  if ( $verification !== true ) {
314 
315  return array(
316  'status' => self::VERIFICATION_ERROR,
317  'details' => $verification
318  );
319  }
320 
324  $result = $this->validateName();
325  if ( $result !== true ) {
326 
327  return $result;
328  }
329 
330  $error = '';
331  if ( !Hooks::run( 'UploadVerification',
332  array( $this->mDestName, $this->mTempPath, &$error ) )
333  ) {
334 
335  return array( 'status' => self::HOOK_ABORTED, 'error' => $error );
336  }
337 
338  return array( 'status' => self::OK );
339  }
340 
347  public function validateName() {
348  $nt = $this->getTitle();
349  if ( is_null( $nt ) ) {
350  $result = array( 'status' => $this->mTitleError );
351  if ( $this->mTitleError == self::ILLEGAL_FILENAME ) {
352  $result['filtered'] = $this->mFilteredName;
353  }
354  if ( $this->mTitleError == self::FILETYPE_BADTYPE ) {
355  $result['finalExt'] = $this->mFinalExtension;
356  if ( count( $this->mBlackListedExtensions ) ) {
357  $result['blacklistedExt'] = $this->mBlackListedExtensions;
358  }
359  }
360 
361  return $result;
362  }
363  $this->mDestName = $this->getLocalFile()->getName();
364 
365  return true;
366  }
367 
377  protected function verifyMimeType( $mime ) {
379  if ( $wgVerifyMimeType ) {
380  wfDebug( "mime: <$mime> extension: <{$this->mFinalExtension}>\n" );
382  if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
383 
384  return array( 'filetype-badmime', $mime );
385  }
386 
387  # Check what Internet Explorer would detect
388  $fp = fopen( $this->mTempPath, 'rb' );
389  $chunk = fread( $fp, 256 );
390  fclose( $fp );
391 
392  $magic = MimeMagic::singleton();
393  $extMime = $magic->guessTypesForExtension( $this->mFinalExtension );
394  $ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
395  foreach ( $ieTypes as $ieType ) {
396  if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
397 
398  return array( 'filetype-bad-ie-mime', $ieType );
399  }
400  }
401  }
402 
403  return true;
404  }
405 
411  protected function verifyFile() {
413 
414  $status = $this->verifyPartialFile();
415  if ( $status !== true ) {
416 
417  return $status;
418  }
419 
420  $this->mFileProps = FSFile::getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
421  $mime = $this->mFileProps['mime'];
422 
423  if ( $wgVerifyMimeType ) {
424  # XXX: Missing extension will be caught by validateName() via getTitle()
425  if ( $this->mFinalExtension != '' && !$this->verifyExtension( $mime, $this->mFinalExtension ) ) {
426 
427  return array( 'filetype-mime-mismatch', $this->mFinalExtension, $mime );
428  }
429  }
430 
431  # check for htmlish code and javascript
432  if ( !$wgDisableUploadScriptChecks ) {
433  if ( $this->mFinalExtension == 'svg' || $mime == 'image/svg+xml' ) {
434  $svgStatus = $this->detectScriptInSvg( $this->mTempPath, false );
435  if ( $svgStatus !== false ) {
436 
437  return $svgStatus;
438  }
439  }
440  }
441 
442  $handler = MediaHandler::getHandler( $mime );
443  if ( $handler ) {
444  $handlerStatus = $handler->verifyUpload( $this->mTempPath );
445  if ( !$handlerStatus->isOK() ) {
446  $errors = $handlerStatus->getErrorsArray();
447 
448  return reset( $errors );
449  }
450  }
451 
452  Hooks::run( 'UploadVerifyFile', array( $this, $mime, &$status ) );
453  if ( $status !== true ) {
454 
455  return $status;
456  }
457 
458  wfDebug( __METHOD__ . ": all clear; passing.\n" );
459 
460  return true;
461  }
462 
471  protected function verifyPartialFile() {
473 
474  # getTitle() sets some internal parameters like $this->mFinalExtension
475  $this->getTitle();
476 
477  $this->mFileProps = FSFile::getPropsFromPath( $this->mTempPath, $this->mFinalExtension );
478 
479  # check MIME type, if desired
480  $mime = $this->mFileProps['file-mime'];
481  $status = $this->verifyMimeType( $mime );
482  if ( $status !== true ) {
483 
484  return $status;
485  }
486 
487  # check for htmlish code and javascript
488  if ( !$wgDisableUploadScriptChecks ) {
489  if ( self::detectScript( $this->mTempPath, $mime, $this->mFinalExtension ) ) {
490 
491  return array( 'uploadscripted' );
492  }
493  if ( $this->mFinalExtension == 'svg' || $mime == 'image/svg+xml' ) {
494  $svgStatus = $this->detectScriptInSvg( $this->mTempPath, true );
495  if ( $svgStatus !== false ) {
496 
497  return $svgStatus;
498  }
499  }
500  }
501 
502  # Check for Java applets, which if uploaded can bypass cross-site
503  # restrictions.
504  if ( !$wgAllowJavaUploads ) {
505  $this->mJavaDetected = false;
506  $zipStatus = ZipDirectoryReader::read( $this->mTempPath,
507  array( $this, 'zipEntryCallback' ) );
508  if ( !$zipStatus->isOK() ) {
509  $errors = $zipStatus->getErrorsArray();
510  $error = reset( $errors );
511  if ( $error[0] !== 'zip-wrong-format' ) {
512 
513  return $error;
514  }
515  }
516  if ( $this->mJavaDetected ) {
517 
518  return array( 'uploadjava' );
519  }
520  }
521 
522  # Scan the uploaded file for viruses
523  $virus = $this->detectVirus( $this->mTempPath );
524  if ( $virus ) {
525 
526  return array( 'uploadvirus', $virus );
527  }
528 
529  return true;
530  }
531 
537  function zipEntryCallback( $entry ) {
538  $names = array( $entry['name'] );
539 
540  // If there is a null character, cut off the name at it, because JDK's
541  // ZIP_GetEntry() uses strcmp() if the name hashes match. If a file name
542  // were constructed which had ".class\0" followed by a string chosen to
543  // make the hash collide with the truncated name, that file could be
544  // returned in response to a request for the .class file.
545  $nullPos = strpos( $entry['name'], "\000" );
546  if ( $nullPos !== false ) {
547  $names[] = substr( $entry['name'], 0, $nullPos );
548  }
549 
550  // If there is a trailing slash in the file name, we have to strip it,
551  // because that's what ZIP_GetEntry() does.
552  if ( preg_grep( '!\.class/?$!', $names ) ) {
553  $this->mJavaDetected = true;
554  }
555  }
556 
566  public function verifyPermissions( $user ) {
567  return $this->verifyTitlePermissions( $user );
568  }
569 
581  public function verifyTitlePermissions( $user ) {
586  $nt = $this->getTitle();
587  if ( is_null( $nt ) ) {
588  return true;
589  }
590  $permErrors = $nt->getUserPermissionsErrors( 'edit', $user );
591  $permErrorsUpload = $nt->getUserPermissionsErrors( 'upload', $user );
592  if ( !$nt->exists() ) {
593  $permErrorsCreate = $nt->getUserPermissionsErrors( 'create', $user );
594  } else {
595  $permErrorsCreate = array();
596  }
597  if ( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
598  $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) );
599  $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsCreate, $permErrors ) );
600 
601  return $permErrors;
602  }
603 
604  $overwriteError = $this->checkOverwrite( $user );
605  if ( $overwriteError !== true ) {
606  return array( $overwriteError );
607  }
608 
609  return true;
610  }
611 
619  public function checkWarnings() {
620  global $wgLang;
621 
622  $warnings = array();
623 
624  $localFile = $this->getLocalFile();
625  $localFile->load( File::READ_LATEST );
626  $filename = $localFile->getName();
627 
632  $comparableName = str_replace( ' ', '_', $this->mDesiredDestName );
633  $comparableName = Title::capitalize( $comparableName, NS_FILE );
634 
635  if ( $this->mDesiredDestName != $filename && $comparableName != $filename ) {
636  $warnings['badfilename'] = $filename;
637  // Debugging for bug 62241
638  wfDebugLog( 'upload', "Filename: '$filename', mDesiredDestName: "
639  . "'$this->mDesiredDestName', comparableName: '$comparableName'" );
640  }
641 
642  // Check whether the file extension is on the unwanted list
644  if ( $wgCheckFileExtensions ) {
645  $extensions = array_unique( $wgFileExtensions );
646  if ( !$this->checkFileExtension( $this->mFinalExtension, $extensions ) ) {
647  $warnings['filetype-unwanted-type'] = array( $this->mFinalExtension,
648  $wgLang->commaList( $extensions ), count( $extensions ) );
649  }
650  }
651 
653  if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) {
654  $warnings['large-file'] = array( $wgUploadSizeWarning, $this->mFileSize );
655  }
656 
657  if ( $this->mFileSize == 0 ) {
658  $warnings['emptyfile'] = true;
659  }
660 
661  $exists = self::getExistsWarning( $localFile );
662  if ( $exists !== false ) {
663  $warnings['exists'] = $exists;
664  }
665 
666  // Check dupes against existing files
667  $hash = $this->getTempFileSha1Base36();
668  $dupes = RepoGroup::singleton()->findBySha1( $hash );
669  $title = $this->getTitle();
670  // Remove all matches against self
671  foreach ( $dupes as $key => $dupe ) {
672  if ( $title->equals( $dupe->getTitle() ) ) {
673  unset( $dupes[$key] );
674  }
675  }
676  if ( $dupes ) {
677  $warnings['duplicate'] = $dupes;
678  }
679 
680  // Check dupes against archives
681  $archivedFile = new ArchivedFile( null, 0, '', $hash );
682  if ( $archivedFile->getID() > 0 ) {
683  if ( $archivedFile->userCan( File::DELETED_FILE ) ) {
684  $warnings['duplicate-archive'] = $archivedFile->getName();
685  } else {
686  $warnings['duplicate-archive'] = '';
687  }
688  }
689 
690  return $warnings;
691  }
692 
704  public function performUpload( $comment, $pageText, $watch, $user ) {
705  $this->getLocalFile()->load( File::READ_LATEST );
706 
707  $status = $this->getLocalFile()->upload(
708  $this->mTempPath,
709  $comment,
710  $pageText,
712  $this->mFileProps,
713  false,
714  $user
715  );
716 
717  if ( $status->isGood() ) {
718  if ( $watch ) {
720  $this->getLocalFile()->getTitle(),
721  $user,
723  );
724  }
725  Hooks::run( 'UploadComplete', array( &$this ) );
726 
727  $this->postProcessUpload();
728  }
729 
730  return $status;
731  }
732 
738  public function postProcessUpload() {
740 
741  $jobs = array();
742 
744  rsort( $sizes );
745 
746  $file = $this->getLocalFile();
747 
748  foreach ( $sizes as $size ) {
749  if ( $file->isVectorized()
750  || $file->getWidth() > $size ) {
751  $jobs[] = new ThumbnailRenderJob( $file->getTitle(), array(
752  'transformParams' => array( 'width' => $size ),
753  ) );
754  }
755  }
756 
757  if ( $jobs ) {
758  JobQueueGroup::singleton()->push( $jobs );
759  }
760  }
761 
768  public function getTitle() {
769  if ( $this->mTitle !== false ) {
770  return $this->mTitle;
771  }
772  /* Assume that if a user specified File:Something.jpg, this is an error
773  * and that the namespace prefix needs to be stripped of.
774  */
775  $title = Title::newFromText( $this->mDesiredDestName );
776  if ( $title && $title->getNamespace() == NS_FILE ) {
777  $this->mFilteredName = $title->getDBkey();
778  } else {
779  $this->mFilteredName = $this->mDesiredDestName;
780  }
781 
782  # oi_archive_name is max 255 bytes, which include a timestamp and an
783  # exclamation mark, so restrict file name to 240 bytes.
784  if ( strlen( $this->mFilteredName ) > 240 ) {
785  $this->mTitleError = self::FILENAME_TOO_LONG;
786  $this->mTitle = null;
787 
788  return $this->mTitle;
789  }
790 
796  $this->mFilteredName = wfStripIllegalFilenameChars( $this->mFilteredName );
797  /* Normalize to title form before we do any further processing */
798  $nt = Title::makeTitleSafe( NS_FILE, $this->mFilteredName );
799  if ( is_null( $nt ) ) {
800  $this->mTitleError = self::ILLEGAL_FILENAME;
801  $this->mTitle = null;
802 
803  return $this->mTitle;
804  }
805  $this->mFilteredName = $nt->getDBkey();
806 
811  list( $partname, $ext ) = $this->splitExtensions( $this->mFilteredName );
812 
813  if ( count( $ext ) ) {
814  $this->mFinalExtension = trim( $ext[count( $ext ) - 1] );
815  } else {
816  $this->mFinalExtension = '';
817 
818  # No extension, try guessing one
819  $magic = MimeMagic::singleton();
820  $mime = $magic->guessMimeType( $this->mTempPath );
821  if ( $mime !== 'unknown/unknown' ) {
822  # Get a space separated list of extensions
823  $extList = $magic->getExtensionsForType( $mime );
824  if ( $extList ) {
825  # Set the extension to the canonical extension
826  $this->mFinalExtension = strtok( $extList, ' ' );
827 
828  # Fix up the other variables
829  $this->mFilteredName .= ".{$this->mFinalExtension}";
830  $nt = Title::makeTitleSafe( NS_FILE, $this->mFilteredName );
831  $ext = array( $this->mFinalExtension );
832  }
833  }
834  }
835 
836  /* Don't allow users to override the blacklist (check file extension) */
839 
840  $blackListedExtensions = $this->checkFileExtensionList( $ext, $wgFileBlacklist );
841 
842  if ( $this->mFinalExtension == '' ) {
843  $this->mTitleError = self::FILETYPE_MISSING;
844  $this->mTitle = null;
845 
846  return $this->mTitle;
847  } elseif ( $blackListedExtensions ||
848  ( $wgCheckFileExtensions && $wgStrictFileExtensions &&
849  !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) )
850  ) {
851  $this->mBlackListedExtensions = $blackListedExtensions;
852  $this->mTitleError = self::FILETYPE_BADTYPE;
853  $this->mTitle = null;
854 
855  return $this->mTitle;
856  }
857 
858  // Windows may be broken with special characters, see bug 1780
859  if ( !preg_match( '/^[\x0-\x7f]*$/', $nt->getText() )
860  && !RepoGroup::singleton()->getLocalRepo()->backendSupportsUnicodePaths()
861  ) {
862  $this->mTitleError = self::WINDOWS_NONASCII_FILENAME;
863  $this->mTitle = null;
864 
865  return $this->mTitle;
866  }
867 
868  # If there was more than one "extension", reassemble the base
869  # filename to prevent bogus complaints about length
870  if ( count( $ext ) > 1 ) {
871  $iterations = count( $ext ) - 1;
872  for ( $i = 0; $i < $iterations; $i++ ) {
873  $partname .= '.' . $ext[$i];
874  }
875  }
876 
877  if ( strlen( $partname ) < 1 ) {
878  $this->mTitleError = self::MIN_LENGTH_PARTNAME;
879  $this->mTitle = null;
880 
881  return $this->mTitle;
882  }
883 
884  $this->mTitle = $nt;
885 
886  return $this->mTitle;
887  }
888 
894  public function getLocalFile() {
895  if ( is_null( $this->mLocalFile ) ) {
896  $nt = $this->getTitle();
897  $this->mLocalFile = is_null( $nt ) ? null : wfLocalFile( $nt );
898  }
899 
900  return $this->mLocalFile;
901  }
902 
918  public function stashFile( User $user = null ) {
919  // was stashSessionFile
920 
921  $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $user );
922  $file = $stash->stashFile( $this->mTempPath, $this->getSourceType() );
923  $this->mLocalFile = $file;
924 
925  return $file;
926  }
927 
934  public function stashFileGetKey() {
935  return $this->stashFile()->getFileKey();
936  }
937 
943  public function stashSession() {
944  return $this->stashFileGetKey();
945  }
946 
951  public function cleanupTempFile() {
952  if ( $this->mRemoveTempFile && $this->mTempPath && file_exists( $this->mTempPath ) ) {
953  wfDebug( __METHOD__ . ": Removing temporary file {$this->mTempPath}\n" );
954  unlink( $this->mTempPath );
955  }
956  }
957 
958  public function getTempPath() {
959  return $this->mTempPath;
960  }
961 
971  public static function splitExtensions( $filename ) {
972  $bits = explode( '.', $filename );
973  $basename = array_shift( $bits );
974 
975  return array( $basename, $bits );
976  }
977 
986  public static function checkFileExtension( $ext, $list ) {
987  return in_array( strtolower( $ext ), $list );
988  }
989 
998  public static function checkFileExtensionList( $ext, $list ) {
999  return array_intersect( array_map( 'strtolower', $ext ), $list );
1000  }
1001 
1009  public static function verifyExtension( $mime, $extension ) {
1010  $magic = MimeMagic::singleton();
1011 
1012  if ( !$mime || $mime == 'unknown' || $mime == 'unknown/unknown' ) {
1013  if ( !$magic->isRecognizableExtension( $extension ) ) {
1014  wfDebug( __METHOD__ . ": passing file with unknown detected mime type; " .
1015  "unrecognized extension '$extension', can't verify\n" );
1016 
1017  return true;
1018  } else {
1019  wfDebug( __METHOD__ . ": rejecting file with unknown detected mime type; " .
1020  "recognized extension '$extension', so probably invalid file\n" );
1021 
1022  return false;
1023  }
1024  }
1025 
1026  $match = $magic->isMatchingExtension( $extension, $mime );
1027 
1028  if ( $match === null ) {
1029  if ( $magic->getTypesForExtension( $extension ) !== null ) {
1030  wfDebug( __METHOD__ . ": No extension known for $mime, but we know a mime for $extension\n" );
1031 
1032  return false;
1033  } else {
1034  wfDebug( __METHOD__ . ": no file extension known for mime type $mime, passing file\n" );
1035 
1036  return true;
1037  }
1038  } elseif ( $match === true ) {
1039  wfDebug( __METHOD__ . ": mime type $mime matches extension $extension, passing file\n" );
1040 
1042  return true;
1043  } else {
1044  wfDebug( __METHOD__
1045  . ": mime type $mime mismatches file extension $extension, rejecting file\n" );
1046 
1047  return false;
1048  }
1049  }
1050 
1062  public static function detectScript( $file, $mime, $extension ) {
1064 
1065  # ugly hack: for text files, always look at the entire file.
1066  # For binary field, just check the first K.
1067 
1068  if ( strpos( $mime, 'text/' ) === 0 ) {
1069  $chunk = file_get_contents( $file );
1070  } else {
1071  $fp = fopen( $file, 'rb' );
1072  $chunk = fread( $fp, 1024 );
1073  fclose( $fp );
1074  }
1075 
1076  $chunk = strtolower( $chunk );
1077 
1078  if ( !$chunk ) {
1079 
1080  return false;
1081  }
1082 
1083  # decode from UTF-16 if needed (could be used for obfuscation).
1084  if ( substr( $chunk, 0, 2 ) == "\xfe\xff" ) {
1085  $enc = 'UTF-16BE';
1086  } elseif ( substr( $chunk, 0, 2 ) == "\xff\xfe" ) {
1087  $enc = 'UTF-16LE';
1088  } else {
1089  $enc = null;
1090  }
1091 
1092  if ( $enc ) {
1093  $chunk = iconv( $enc, "ASCII//IGNORE", $chunk );
1094  }
1095 
1096  $chunk = trim( $chunk );
1097 
1099  wfDebug( __METHOD__ . ": checking for embedded scripts and HTML stuff\n" );
1100 
1101  # check for HTML doctype
1102  if ( preg_match( "/<!DOCTYPE *X?HTML/i", $chunk ) ) {
1103 
1104  return true;
1105  }
1106 
1107  // Some browsers will interpret obscure xml encodings as UTF-8, while
1108  // PHP/expat will interpret the given encoding in the xml declaration (bug 47304)
1109  if ( $extension == 'svg' || strpos( $mime, 'image/svg' ) === 0 ) {
1110  if ( self::checkXMLEncodingMissmatch( $file ) ) {
1111 
1112  return true;
1113  }
1114  }
1115 
1131  $tags = array(
1132  '<a href',
1133  '<body',
1134  '<head',
1135  '<html', #also in safari
1136  '<img',
1137  '<pre',
1138  '<script', #also in safari
1139  '<table'
1140  );
1141 
1142  if ( !$wgAllowTitlesInSVG && $extension !== 'svg' && $mime !== 'image/svg' ) {
1143  $tags[] = '<title';
1144  }
1145 
1146  foreach ( $tags as $tag ) {
1147  if ( false !== strpos( $chunk, $tag ) ) {
1148  wfDebug( __METHOD__ . ": found something that may make it be mistaken for html: $tag\n" );
1149 
1150  return true;
1151  }
1152  }
1153 
1154  /*
1155  * look for JavaScript
1156  */
1157 
1158  # resolve entity-refs to look at attributes. may be harsh on big files... cache result?
1159  $chunk = Sanitizer::decodeCharReferences( $chunk );
1160 
1161  # look for script-types
1162  if ( preg_match( '!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim', $chunk ) ) {
1163  wfDebug( __METHOD__ . ": found script types\n" );
1164 
1165  return true;
1166  }
1167 
1168  # look for html-style script-urls
1169  if ( preg_match( '!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
1170  wfDebug( __METHOD__ . ": found html-style script urls\n" );
1171 
1172  return true;
1173  }
1174 
1175  # look for css-style script-urls
1176  if ( preg_match( '!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
1177  wfDebug( __METHOD__ . ": found css-style script urls\n" );
1178 
1179  return true;
1180  }
1181 
1182  wfDebug( __METHOD__ . ": no scripts found\n" );
1183 
1184  return false;
1185  }
1186 
1194  public static function checkXMLEncodingMissmatch( $file ) {
1196  $contents = file_get_contents( $file, false, null, -1, $wgSVGMetadataCutoff );
1197  $encodingRegex = '!encoding[ \t\n\r]*=[ \t\n\r]*[\'"](.*?)[\'"]!si';
1198 
1199  if ( preg_match( "!<\?xml\b(.*?)\?>!si", $contents, $matches ) ) {
1200  if ( preg_match( $encodingRegex, $matches[1], $encMatch )
1201  && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1202  ) {
1203  wfDebug( __METHOD__ . ": Found unsafe XML encoding '{$encMatch[1]}'\n" );
1204 
1205  return true;
1206  }
1207  } elseif ( preg_match( "!<\?xml\b!si", $contents ) ) {
1208  // Start of XML declaration without an end in the first $wgSVGMetadataCutoff
1209  // bytes. There shouldn't be a legitimate reason for this to happen.
1210  wfDebug( __METHOD__ . ": Unmatched XML declaration start\n" );
1211 
1212  return true;
1213  } elseif ( substr( $contents, 0, 4 ) == "\x4C\x6F\xA7\x94" ) {
1214  // EBCDIC encoded XML
1215  wfDebug( __METHOD__ . ": EBCDIC Encoded XML\n" );
1216 
1217  return true;
1218  }
1219 
1220  // It's possible the file is encoded with multi-byte encoding, so re-encode attempt to
1221  // detect the encoding in case is specifies an encoding not whitelisted in self::$safeXmlEncodings
1222  $attemptEncodings = array( 'UTF-16', 'UTF-16BE', 'UTF-32', 'UTF-32BE' );
1223  foreach ( $attemptEncodings as $encoding ) {
1225  $str = iconv( $encoding, 'UTF-8', $contents );
1227  if ( $str != '' && preg_match( "!<\?xml\b(.*?)\?>!si", $str, $matches ) ) {
1228  if ( preg_match( $encodingRegex, $matches[1], $encMatch )
1229  && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
1230  ) {
1231  wfDebug( __METHOD__ . ": Found unsafe XML encoding '{$encMatch[1]}'\n" );
1232 
1233  return true;
1234  }
1235  } elseif ( $str != '' && preg_match( "!<\?xml\b!si", $str ) ) {
1236  // Start of XML declaration without an end in the first $wgSVGMetadataCutoff
1237  // bytes. There shouldn't be a legitimate reason for this to happen.
1238  wfDebug( __METHOD__ . ": Unmatched XML declaration start\n" );
1239 
1240  return true;
1241  }
1242  }
1243 
1244  return false;
1245  }
1246 
1252  protected function detectScriptInSvg( $filename, $partial ) {
1253  $this->mSVGNSError = false;
1254  $check = new XmlTypeCheck(
1255  $filename,
1256  array( $this, 'checkSvgScriptCallback' ),
1257  true,
1258  array( 'processing_instruction_handler' => 'UploadBase::checkSvgPICallback' )
1259  );
1260  if ( $check->wellFormed !== true ) {
1261  // Invalid xml (bug 58553)
1262  // But only when non-partial (bug 65724)
1263  return $partial ? false : array( 'uploadinvalidxml' );
1264  } elseif ( $check->filterMatch ) {
1265  if ( $this->mSVGNSError ) {
1266  return array( 'uploadscriptednamespace', $this->mSVGNSError );
1267  }
1268 
1269  return array( 'uploadscripted' );
1270  }
1271 
1272  return false;
1273  }
1274 
1281  public static function checkSvgPICallback( $target, $data ) {
1282  // Don't allow external stylesheets (bug 57550)
1283  if ( preg_match( '/xml-stylesheet/i', $target ) ) {
1284  return true;
1285  }
1286 
1287  return false;
1288  }
1289 
1296  public function checkSvgScriptCallback( $element, $attribs, $data = null ) {
1297 
1298  list( $namespace, $strippedElement ) = $this->splitXmlNamespace( $element );
1299 
1300  // We specifically don't include:
1301  // http://www.w3.org/1999/xhtml (bug 60771)
1302  static $validNamespaces = array(
1303  '',
1304  'adobe:ns:meta/',
1305  'http://creativecommons.org/ns#',
1306  'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd',
1307  'http://ns.adobe.com/adobeillustrator/10.0/',
1308  'http://ns.adobe.com/adobesvgviewerextensions/3.0/',
1309  'http://ns.adobe.com/extensibility/1.0/',
1310  'http://ns.adobe.com/flows/1.0/',
1311  'http://ns.adobe.com/illustrator/1.0/',
1312  'http://ns.adobe.com/imagereplacement/1.0/',
1313  'http://ns.adobe.com/pdf/1.3/',
1314  'http://ns.adobe.com/photoshop/1.0/',
1315  'http://ns.adobe.com/saveforweb/1.0/',
1316  'http://ns.adobe.com/variables/1.0/',
1317  'http://ns.adobe.com/xap/1.0/',
1318  'http://ns.adobe.com/xap/1.0/g/',
1319  'http://ns.adobe.com/xap/1.0/g/img/',
1320  'http://ns.adobe.com/xap/1.0/mm/',
1321  'http://ns.adobe.com/xap/1.0/rights/',
1322  'http://ns.adobe.com/xap/1.0/stype/dimensions#',
1323  'http://ns.adobe.com/xap/1.0/stype/font#',
1324  'http://ns.adobe.com/xap/1.0/stype/manifestitem#',
1325  'http://ns.adobe.com/xap/1.0/stype/resourceevent#',
1326  'http://ns.adobe.com/xap/1.0/stype/resourceref#',
1327  'http://ns.adobe.com/xap/1.0/t/pg/',
1328  'http://purl.org/dc/elements/1.1/',
1329  'http://purl.org/dc/elements/1.1',
1330  'http://schemas.microsoft.com/visio/2003/svgextensions/',
1331  'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd',
1332  'http://taptrix.com/inkpad/svg_extensions',
1333  'http://web.resource.org/cc/',
1334  'http://www.freesoftware.fsf.org/bkchem/cdml',
1335  'http://www.inkscape.org/namespaces/inkscape',
1336  'http://www.opengis.net/gml',
1337  'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
1338  'http://www.w3.org/2000/svg',
1339  'http://www.w3.org/tr/rec-rdf-syntax/',
1340  );
1341 
1342  if ( !in_array( $namespace, $validNamespaces ) ) {
1343  wfDebug( __METHOD__ . ": Non-svg namespace '$namespace' in uploaded file.\n" );
1345  $this->mSVGNSError = $namespace;
1346 
1347  return true;
1348  }
1349 
1350  /*
1351  * check for elements that can contain javascript
1352  */
1353  if ( $strippedElement == 'script' ) {
1354  wfDebug( __METHOD__ . ": Found script element '$element' in uploaded file.\n" );
1355 
1356  return true;
1357  }
1358 
1359  # e.g., <svg xmlns="http://www.w3.org/2000/svg">
1360  # <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
1361  if ( $strippedElement == 'handler' ) {
1362  wfDebug( __METHOD__ . ": Found scriptable element '$element' in uploaded file.\n" );
1363 
1364  return true;
1365  }
1366 
1367  # SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
1368  if ( $strippedElement == 'stylesheet' ) {
1369  wfDebug( __METHOD__ . ": Found scriptable element '$element' in uploaded file.\n" );
1370 
1371  return true;
1372  }
1373 
1374  # Block iframes, in case they pass the namespace check
1375  if ( $strippedElement == 'iframe' ) {
1376  wfDebug( __METHOD__ . ": iframe in uploaded file.\n" );
1377 
1378  return true;
1379  }
1380 
1381  # Check <style> css
1382  if ( $strippedElement == 'style'
1383  && self::checkCssFragment( Sanitizer::normalizeCss( $data ) )
1384  ) {
1385  wfDebug( __METHOD__ . ": hostile css in style element.\n" );
1386  return true;
1387  }
1388 
1389  foreach ( $attribs as $attrib => $value ) {
1390  $stripped = $this->stripXmlNamespace( $attrib );
1391  $value = strtolower( $value );
1392 
1393  if ( substr( $stripped, 0, 2 ) == 'on' ) {
1394  wfDebug( __METHOD__
1395  . ": Found event-handler attribute '$attrib'='$value' in uploaded file.\n" );
1396 
1397  return true;
1398  }
1399 
1400  # href with non-local target (don't allow http://, javascript:, etc)
1401  if ( $stripped == 'href'
1402  && strpos( $value, 'data:' ) !== 0
1403  && strpos( $value, '#' ) !== 0
1404  ) {
1405  if ( !( $strippedElement === 'a'
1406  && preg_match( '!^https?://!im', $value ) )
1407  ) {
1408  wfDebug( __METHOD__ . ": Found href attribute <$strippedElement "
1409  . "'$attrib'='$value' in uploaded file.\n" );
1410 
1411  return true;
1412  }
1413  }
1414 
1415  # only allow data: targets that should be safe. This prevents vectors like,
1416  # image/svg, text/xml, application/xml, and text/html, which can contain scripts
1417  if ( $stripped == 'href' && strncasecmp( 'data:', $value, 5 ) === 0 ) {
1418  // rfc2397 parameters. This is only slightly slower than (;[\w;]+)*.
1419  $parameters = '(?>;[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+=(?>[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+|"(?>[\0-\x0c\x0e-\x21\x23-\x5b\x5d-\x7f]+|\\\\[\0-\x7f])*"))*(?:;base64)?';
1420  if ( !preg_match( "!^data:\s*image/(gif|jpeg|jpg|png)$parameters,!i", $value ) ) {
1421  wfDebug( __METHOD__ . ": Found href to unwhitelisted data: uri "
1422  . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
1423  return true;
1424  }
1425  }
1426 
1427  # Change href with animate from (http://html5sec.org/#137).
1428  if ( $stripped === 'attributename'
1429  && $strippedElement === 'animate'
1430  && $this->stripXmlNamespace( $value ) == 'href'
1431  ) {
1432  wfDebug( __METHOD__ . ": Found animate that might be changing href using from "
1433  . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
1434 
1435  return true;
1436  }
1437 
1438  # use set/animate to add event-handler attribute to parent
1439  if ( ( $strippedElement == 'set' || $strippedElement == 'animate' )
1440  && $stripped == 'attributename'
1441  && substr( $value, 0, 2 ) == 'on'
1442  ) {
1443  wfDebug( __METHOD__ . ": Found svg setting event-handler attribute with "
1444  . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
1445 
1446  return true;
1447  }
1448 
1449  # use set to add href attribute to parent element
1450  if ( $strippedElement == 'set'
1451  && $stripped == 'attributename'
1452  && strpos( $value, 'href' ) !== false
1453  ) {
1454  wfDebug( __METHOD__ . ": Found svg setting href attribute '$value' in uploaded file.\n" );
1455 
1456  return true;
1457  }
1458 
1459  # use set to add a remote / data / script target to an element
1460  if ( $strippedElement == 'set'
1461  && $stripped == 'to'
1462  && preg_match( '!(http|https|data|script):!sim', $value )
1463  ) {
1464  wfDebug( __METHOD__ . ": Found svg setting attribute to '$value' in uploaded file.\n" );
1465 
1466  return true;
1467  }
1468 
1469  # use handler attribute with remote / data / script
1470  if ( $stripped == 'handler' && preg_match( '!(http|https|data|script):!sim', $value ) ) {
1471  wfDebug( __METHOD__ . ": Found svg setting handler with remote/data/script "
1472  . "'$attrib'='$value' in uploaded file.\n" );
1473 
1474  return true;
1475  }
1476 
1477  # use CSS styles to bring in remote code
1478  if ( $stripped == 'style'
1479  && self::checkCssFragment( Sanitizer::normalizeCss( $value ) )
1480  ) {
1481  wfDebug( __METHOD__ . ": Found svg setting a style with "
1482  . "remote url '$attrib'='$value' in uploaded file.\n" );
1483  return true;
1484  }
1485 
1486  # Several attributes can include css, css character escaping isn't allowed
1487  $cssAttrs = array( 'font', 'clip-path', 'fill', 'filter', 'marker',
1488  'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke' );
1489  if ( in_array( $stripped, $cssAttrs )
1490  && self::checkCssFragment( $value )
1491  ) {
1492  wfDebug( __METHOD__ . ": Found svg setting a style with "
1493  . "remote url '$attrib'='$value' in uploaded file.\n" );
1494  return true;
1495  }
1496 
1497  # image filters can pull in url, which could be svg that executes scripts
1498  if ( $strippedElement == 'image'
1499  && $stripped == 'filter'
1500  && preg_match( '!url\s*\(!sim', $value )
1501  ) {
1502  wfDebug( __METHOD__ . ": Found image filter with url: "
1503  . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
1504 
1505  return true;
1506  }
1507  }
1508 
1509  return false; //No scripts detected
1510  }
1511 
1519  private static function checkCssFragment( $value ) {
1520 
1521  # Forbid external stylesheets, for both reliability and to protect viewer's privacy
1522  if ( stripos( $value, '@import' ) !== false ) {
1523  return true;
1524  }
1525 
1526  # We allow @font-face to embed fonts with data: urls, so we snip the string
1527  # 'url' out so this case won't match when we check for urls below
1528  $pattern = '!(@font-face\s*{[^}]*src:)url(\("data:;base64,)!im';
1529  $value = preg_replace( $pattern, '$1$2', $value );
1530 
1531  # Check for remote and executable CSS. Unlike in Sanitizer::checkCss, the CSS
1532  # properties filter and accelerator don't seem to be useful for xss in SVG files.
1533  # Expression and -o-link don't seem to work either, but filtering them here in case.
1534  # Additionally, we catch remote urls like url("http:..., url('http:..., url(http:...,
1535  # but not local ones such as url("#..., url('#..., url(#....
1536  if ( preg_match( '!expression
1537  | -o-link\s*:
1538  | -o-link-source\s*:
1539  | -o-replace\s*:!imx', $value ) ) {
1540  return true;
1541  }
1542 
1543  if ( preg_match_all(
1544  "!(\s*(url|image|image-set)\s*\(\s*[\"']?\s*[^#]+.*?\))!sim",
1545  $value,
1546  $matches
1547  ) !== 0
1548  ) {
1549  # TODO: redo this in one regex. Until then, url("#whatever") matches the first
1550  foreach ( $matches[1] as $match ) {
1551  if ( !preg_match( "!\s*(url|image|image-set)\s*\(\s*(#|'#|\"#)!im", $match ) ) {
1552  return true;
1553  }
1554  }
1555  }
1556 
1557  if ( preg_match( '/[\000-\010\013\016-\037\177]/', $value ) ) {
1558  return true;
1559  }
1560 
1561  return false;
1562  }
1563 
1569  private static function splitXmlNamespace( $element ) {
1570  // 'http://www.w3.org/2000/svg:script' -> array( 'http://www.w3.org/2000/svg', 'script' )
1571  $parts = explode( ':', strtolower( $element ) );
1572  $name = array_pop( $parts );
1573  $ns = implode( ':', $parts );
1574 
1575  return array( $ns, $name );
1576  }
1577 
1582  private function stripXmlNamespace( $name ) {
1583  // 'http://www.w3.org/2000/svg:script' -> 'script'
1584  $parts = explode( ':', strtolower( $name ) );
1585 
1586  return array_pop( $parts );
1587  }
1588 
1599  public static function detectVirus( $file ) {
1601 
1602  if ( !$wgAntivirus ) {
1603  wfDebug( __METHOD__ . ": virus scanner disabled\n" );
1604 
1605  return null;
1606  }
1607 
1608  if ( !$wgAntivirusSetup[$wgAntivirus] ) {
1609  wfDebug( __METHOD__ . ": unknown virus scanner: $wgAntivirus\n" );
1610  $wgOut->wrapWikiMsg( "<div class=\"error\">\n$1\n</div>",
1611  array( 'virus-badscanner', $wgAntivirus ) );
1612 
1613  return wfMessage( 'virus-unknownscanner' )->text() . " $wgAntivirus";
1614  }
1615 
1616  # look up scanner configuration
1617  $command = $wgAntivirusSetup[$wgAntivirus]['command'];
1618  $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]['codemap'];
1619  $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]['messagepattern'] ) ?
1620  $wgAntivirusSetup[$wgAntivirus]['messagepattern'] : null;
1621 
1622  if ( strpos( $command, "%f" ) === false ) {
1623  # simple pattern: append file to scan
1624  $command .= " " . wfEscapeShellArg( $file );
1625  } else {
1626  # complex pattern: replace "%f" with file to scan
1627  $command = str_replace( "%f", wfEscapeShellArg( $file ), $command );
1628  }
1629 
1630  wfDebug( __METHOD__ . ": running virus scan: $command \n" );
1631 
1632  # execute virus scanner
1633  $exitCode = false;
1634 
1635  # NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
1636  # that does not seem to be worth the pain.
1637  # Ask me (Duesentrieb) about it if it's ever needed.
1638  $output = wfShellExecWithStderr( $command, $exitCode );
1639 
1640  # map exit code to AV_xxx constants.
1641  $mappedCode = $exitCode;
1642  if ( $exitCodeMap ) {
1643  if ( isset( $exitCodeMap[$exitCode] ) ) {
1644  $mappedCode = $exitCodeMap[$exitCode];
1645  } elseif ( isset( $exitCodeMap["*"] ) ) {
1646  $mappedCode = $exitCodeMap["*"];
1647  }
1648  }
1649 
1650  /* NB: AV_NO_VIRUS is 0 but AV_SCAN_FAILED is false,
1651  * so we need the strict equalities === and thus can't use a switch here
1652  */
1653  if ( $mappedCode === AV_SCAN_FAILED ) {
1654  # scan failed (code was mapped to false by $exitCodeMap)
1655  wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" );
1656 
1657  $output = $wgAntivirusRequired
1658  ? wfMessage( 'virus-scanfailed', array( $exitCode ) )->text()
1659  : null;
1660  } elseif ( $mappedCode === AV_SCAN_ABORTED ) {
1661  # scan failed because filetype is unknown (probably imune)
1662  wfDebug( __METHOD__ . ": unsupported file type $file (code $exitCode).\n" );
1663  $output = null;
1664  } elseif ( $mappedCode === AV_NO_VIRUS ) {
1665  # no virus found
1666  wfDebug( __METHOD__ . ": file passed virus scan.\n" );
1667  $output = false;
1668  } else {
1669  $output = trim( $output );
1670 
1671  if ( !$output ) {
1672  $output = true; #if there's no output, return true
1673  } elseif ( $msgPattern ) {
1674  $groups = array();
1675  if ( preg_match( $msgPattern, $output, $groups ) ) {
1676  if ( $groups[1] ) {
1677  $output = $groups[1];
1678  }
1679  }
1680  }
1681 
1682  wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output \n" );
1683  }
1684 
1685  return $output;
1686  }
1687 
1696  private function checkOverwrite( $user ) {
1697  // First check whether the local file can be overwritten
1698  $file = $this->getLocalFile();
1699  $file->load( File::READ_LATEST );
1700  if ( $file->exists() ) {
1701  if ( !self::userCanReUpload( $user, $file ) ) {
1702  return array( 'fileexists-forbidden', $file->getName() );
1703  } else {
1704  return true;
1705  }
1706  }
1707 
1708  /* Check shared conflicts: if the local file does not exist, but
1709  * wfFindFile finds a file, it exists in a shared repository.
1710  */
1711  $file = wfFindFile( $this->getTitle(), array( 'latest' => true ) );
1712  if ( $file && !$user->isAllowed( 'reupload-shared' ) ) {
1713  return array( 'fileexists-shared-forbidden', $file->getName() );
1714  }
1715 
1716  return true;
1717  }
1718 
1726  public static function userCanReUpload( User $user, $img ) {
1727  if ( $user->isAllowed( 'reupload' ) ) {
1728  return true; // non-conditional
1729  }
1730  if ( !$user->isAllowed( 'reupload-own' ) ) {
1731  return false;
1732  }
1733  if ( is_string( $img ) ) {
1734  $img = wfLocalFile( $img );
1735  }
1736  if ( !( $img instanceof LocalFile ) ) {
1737  return false;
1738  }
1739 
1740  $img->load( File::READ_LATEST );
1741 
1742  return $user->getId() == $img->getUser( 'id' );
1743  }
1744 
1756  public static function getExistsWarning( $file ) {
1757  if ( $file->exists() ) {
1758  return array( 'warning' => 'exists', 'file' => $file );
1759  }
1760 
1761  if ( $file->getTitle()->getArticleID() ) {
1762  return array( 'warning' => 'page-exists', 'file' => $file );
1763  }
1764 
1765  if ( $file->wasDeleted() && !$file->exists() ) {
1766  return array( 'warning' => 'was-deleted', 'file' => $file );
1767  }
1768 
1769  if ( strpos( $file->getName(), '.' ) == false ) {
1770  $partname = $file->getName();
1771  $extension = '';
1772  } else {
1773  $n = strrpos( $file->getName(), '.' );
1774  $extension = substr( $file->getName(), $n + 1 );
1775  $partname = substr( $file->getName(), 0, $n );
1776  }
1777  $normalizedExtension = File::normalizeExtension( $extension );
1778 
1779  if ( $normalizedExtension != $extension ) {
1780  // We're not using the normalized form of the extension.
1781  // Normal form is lowercase, using most common of alternate
1782  // extensions (eg 'jpg' rather than 'JPEG').
1783  //
1784  // Check for another file using the normalized form...
1785  $nt_lc = Title::makeTitle( NS_FILE, "{$partname}.{$normalizedExtension}" );
1786  $file_lc = wfLocalFile( $nt_lc );
1787 
1788  if ( $file_lc->exists() ) {
1789  return array(
1790  'warning' => 'exists-normalized',
1791  'file' => $file,
1792  'normalizedFile' => $file_lc
1793  );
1794  }
1795  }
1796 
1797  // Check for files with the same name but a different extension
1798  $similarFiles = RepoGroup::singleton()->getLocalRepo()->findFilesByPrefix(
1799  "{$partname}.", 1 );
1800  if ( count( $similarFiles ) ) {
1801  return array(
1802  'warning' => 'exists-normalized',
1803  'file' => $file,
1804  'normalizedFile' => $similarFiles[0],
1805  );
1806  }
1807 
1808  if ( self::isThumbName( $file->getName() ) ) {
1809  # Check for filenames like 50px- or 180px-, these are mostly thumbnails
1810  $nt_thb = Title::newFromText(
1811  substr( $partname, strpos( $partname, '-' ) + 1 ) . '.' . $extension,
1812  NS_FILE
1813  );
1814  $file_thb = wfLocalFile( $nt_thb );
1815  if ( $file_thb->exists() ) {
1816  return array(
1817  'warning' => 'thumb',
1818  'file' => $file,
1819  'thumbFile' => $file_thb
1820  );
1821  } else {
1822  // File does not exist, but we just don't like the name
1823  return array(
1824  'warning' => 'thumb-name',
1825  'file' => $file,
1826  'thumbFile' => $file_thb
1827  );
1828  }
1829  }
1830 
1831  foreach ( self::getFilenamePrefixBlacklist() as $prefix ) {
1832  if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
1833  return array(
1834  'warning' => 'bad-prefix',
1835  'file' => $file,
1836  'prefix' => $prefix
1837  );
1838  }
1839  }
1840 
1841  return false;
1842  }
1843 
1849  public static function isThumbName( $filename ) {
1850  $n = strrpos( $filename, '.' );
1851  $partname = $n ? substr( $filename, 0, $n ) : $filename;
1852 
1853  return (
1854  substr( $partname, 3, 3 ) == 'px-' ||
1855  substr( $partname, 2, 3 ) == 'px-'
1856  ) &&
1857  preg_match( "/[0-9]{2}/", substr( $partname, 0, 2 ) );
1858  }
1859 
1865  public static function getFilenamePrefixBlacklist() {
1866  $blacklist = array();
1867  $message = wfMessage( 'filename-prefix-blacklist' )->inContentLanguage();
1868  if ( !$message->isDisabled() ) {
1869  $lines = explode( "\n", $message->plain() );
1870  foreach ( $lines as $line ) {
1871  // Remove comment lines
1872  $comment = substr( trim( $line ), 0, 1 );
1873  if ( $comment == '#' || $comment == '' ) {
1874  continue;
1875  }
1876  // Remove additional comments after a prefix
1877  $comment = strpos( $line, '#' );
1878  if ( $comment > 0 ) {
1879  $line = substr( $line, 0, $comment - 1 );
1880  }
1881  $blacklist[] = trim( $line );
1882  }
1883  }
1884 
1885  return $blacklist;
1886  }
1887 
1899  public function getImageInfo( $result ) {
1900  $file = $this->getLocalFile();
1906  if ( $file instanceof UploadStashFile ) {
1908  $info = ApiQueryStashImageInfo::getInfo( $file, array_flip( $imParam ), $result );
1909  } else {
1911  $info = ApiQueryImageInfo::getInfo( $file, array_flip( $imParam ), $result );
1912  }
1913 
1914  return $info;
1915  }
1916 
1921  public function convertVerifyErrorToStatus( $error ) {
1922  $code = $error['status'];
1923  unset( $code['status'] );
1924 
1925  return Status::newFatal( $this->getVerificationErrorCode( $code ), $error );
1926  }
1927 
1932  public static function getMaxUploadSize( $forType = null ) {
1934 
1935  if ( is_array( $wgMaxUploadSize ) ) {
1936  if ( !is_null( $forType ) && isset( $wgMaxUploadSize[$forType] ) ) {
1937  return $wgMaxUploadSize[$forType];
1938  } else {
1939  return $wgMaxUploadSize['*'];
1940  }
1941  } else {
1942  return intval( $wgMaxUploadSize );
1943  }
1944  }
1945 
1955  public static function getSessionStatus( User $user, $statusKey ) {
1956  $key = wfMemcKey( 'uploadstatus', $user->getId() ?: md5( $user->getName() ), $statusKey );
1957 
1958  return wfGetCache( CACHE_ANYTHING )->get( $key );
1959  }
1960 
1971  public static function setSessionStatus( User $user, $statusKey, $value ) {
1972  $key = wfMemcKey( 'uploadstatus', $user->getId() ?: md5( $user->getName() ), $statusKey );
1973 
1975  if ( $value === false ) {
1976  $cache->delete( $key );
1977  } else {
1978  $cache->set( $key, $value, 86400 );
1979  }
1980  }
1981 }
$wgStrictFileExtensions
If this is turned off, users may override the warning for files not covered by $wgFileExtensions.
checkSvgScriptCallback($element, $attribs, $data=null)
static checkFileExtensionList($ext, $list)
Perform case-insensitive match against a list of file extensions.
Definition: UploadBase.php:998
getImageInfo($result)
Gets image info about the file just uploaded.
getVerificationErrorCode($error)
Definition: UploadBase.php:76
null means default in associative array form
Definition: hooks.txt:1712
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
const FILENAME_TOO_LONG
Definition: UploadBase.php:70
$wgSVGMetadataCutoff
Don't read SVG metadata beyond this point.
$wgDisableUploadScriptChecks
Setting this to true will disable the upload system's checks for HTML/JavaScript. ...
wfIsHHVM()
Check if we are running under HHVM.
const SUCCESS
Definition: UploadBase.php:55
static createFromRequest(&$request, $type=null)
Create a form of UploadBase depending on wpSourceType and initializes it.
Definition: UploadBase.php:141
static isAllowed($user)
Returns true if the user can use this upload module or else a string identifying the missing permissi...
Definition: UploadBase.php:121
performUpload($comment, $pageText, $watch, $user)
Really perform the upload.
Definition: UploadBase.php:704
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:325
wfGetCache($inputType)
Get a cache object.
const UPLOAD_VERIFICATION_ERROR
Definition: UploadBase.php:66
static singleton()
Get an instance of this class.
Definition: MimeMagic.php:357
$command
Definition: cdb.php:65
namespace and then decline to actually register it file or subcat img or subcat RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition: hooks.txt:952
static checkFileExtension($ext, $list)
Perform case-insensitive match against a list of file extensions.
Definition: UploadBase.php:986
const DELETE_SOURCE
Definition: File.php:65
static getSessionStatus(User $user, $statusKey)
Get the current status of a chunked upload (used for polling)
const OVERWRITE_EXISTING_FILE
Definition: UploadBase.php:60
static $safeXmlEncodings
Definition: UploadBase.php:47
static isValidRequest($request)
Check whether a request if valid for this handler.
Definition: UploadBase.php:185
$comment
wfDebug($text, $dest= 'all', array $context=array())
Sends a line to the debug log if enabled or, optionally, to a comment in output.
has been added to your &Future changes to this page and its associated Talk page will be listed there
$value
const AV_NO_VIRUS
Definition: Defines.php:138
verifyPermissions($user)
Alias for verifyTitlePermissions.
Definition: UploadBase.php:566
if($ext== 'php'||$ext== 'php5') $mime
Definition: router.php:65
stripXmlNamespace($name)
static splitXmlNamespace($element)
Divide the element name passed by the xml parser to the callback into URI and prifix.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:233
const ILLEGAL_FILENAME
Definition: UploadBase.php:59
isEmptyFile()
Return true if the file is empty.
Definition: UploadBase.php:239
const AV_SCAN_FAILED
Definition: Defines.php:141
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
static newFatal($message)
Factory function for fatal errors.
Definition: Status.php:83
wfLocalFile($title)
Get an object referring to a locally registered file.
$wgAllowJavaUploads
Allow Java archive uploads.
wfStripIllegalFilenameChars($name)
Replace all invalid characters with - Additional characters can be defined in $wgIllegalFileChars (se...
static checkSvgPICallback($target, $data)
Callback to filter SVG Processing Instructions.
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2054
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
Definition: globals.txt:10
verifyMimeType($mime)
Verify the MIME type.
Definition: UploadBase.php:377
wfArrayDiff2($a, $b)
Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
const AV_SCAN_ABORTED
Definition: Defines.php:140
static getMaxUploadSize($forType=null)
const DELETED_FILE
Definition: File.php:52
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
Definition: design.txt:56
verifyPartialFile()
A verification routine suitable for partial files.
Definition: UploadBase.php:471
$wgCheckFileExtensions
This is a flag to determine whether or not to check file extensions on upload.
The User object encapsulates all of the user-specific settings (user_id, name, rights, password, email address, options, last login time).
Definition: User.php:39
static decodeCharReferences($text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string...
Definition: Sanitizer.php:1397
$wgEnableUploads
Uploads have to be specially set up to be secure.
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock()-offset Set to overwrite offset parameter in $wgRequest set to ''to unsetoffset-wrap String Wrap the message in html(usually something like"&lt
static isThumbName($filename)
Helper function that checks whether the filename looks like a thumbnail.
Class representing a row of the 'filearchive' table.
static read($fileName, $callback, $options=array())
Read a ZIP file and call a function for each file discovered in it.
const IGNORE_USER_RIGHTS
Constant to specify that user rights 'editmywatchlist' and 'viewmywatchlist' should not be checked...
Definition: WatchedItem.php:35
zipEntryCallback($entry)
Callback for ZipDirectoryReader to detect Java class files.
Definition: UploadBase.php:537
namespace and then decline to actually register it file or subcat img or subcat RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books $tag
Definition: hooks.txt:870
$wgAntivirusRequired
Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected.
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing we can concentrate it all in an extension file
Definition: hooks.txt:93
magic word use ParserLimitReportPrepare and ParserLimitReportFormat instead Called at the end of the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Definition: hooks.txt:2187
getTempFileSha1Base36()
Get the base 36 SHA1 of the file.
Definition: UploadBase.php:255
stashFileGetKey()
Stash a file in a temporary directory, returning a key which can be used to find the file again...
Definition: UploadBase.php:934
UploadBase and subclasses are the backend of MediaWiki's file uploads.
Definition: UploadBase.php:38
wfIniGetBool($setting)
Safety wrapper around ini_get() for boolean settings.
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message.Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item.$reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page.$reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision.$reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag.$reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload.$reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page.$context:IContextSource object &$pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect.$title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not.Return true without providing an interwiki to continue interwiki search.$prefix:interwiki prefix we are looking for.&$iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings.Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InternalParseBeforeLinks':during Parser's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InvalidateEmailComplete':Called after a user's email has been invalidated successfully.$user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification.Callee may modify $url and $query, URL will be constructed as $url.$query &$url:URL to index.php &$query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from &$allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn't match your organization.$addr:The e-mail address entered by the user &$result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user &$result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we're looking for a messages file for &$file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces.Do not use this hook to add namespaces.Use CanonicalNamespaces for that.&$namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead.Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead.Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names.&$names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page's language links.This is called in various places to allow extensions to define the effective language links for a page.$title:The page's Title.&$links:Associative array mapping language codes to prefixed links of the form"language:title".&$linkFlags:Associative array mapping prefixed links to arrays of flags.Currently unused, but planned to provide support for marking individual language links in the UI, e.g.for featured articles. 'LanguageSelector':Hook to change the language selector available on a page.$out:The output page.$cssClassName:CSS class name of the language selector. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts.Return false to skip default processing and return $ret.See documentation for Linker::link() for details on the expected meanings of parameters.$skin:the Skin object $target:the Title that the link is pointing to &$html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1710
static singleton()
Get a RepoGroup instance.
Definition: RepoGroup.php:53
fetchFile()
Fetch the file.
Definition: UploadBase.php:231
static isStoragePath($path)
Check if a given path is a "mwstore://" path.
$wgUploadThumbnailRenderMap
When defined, is an array of thumbnail widths to be rendered at upload time.
wfShellExecWithStderr($cmd, &$retval=null, $environ=array(), $limits=array())
Execute a shell command, returning both stdout and stderr.
MediaWiki exception.
Definition: MWException.php:26
static run($event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:137
stashSession()
alias for stashFileGetKey, for backwards compatibility
Definition: UploadBase.php:943
postProcessUpload()
Perform extra steps after a successful upload.
Definition: UploadBase.php:738
$cache
Definition: mcc.php:32
getTitle()
Returns the title of the file to be uploaded.
Definition: UploadBase.php:768
static getPropertyNames($filter=array())
Returns all possible parameters to iiprop.
static detectVirus($file)
Generic wrapper function for a virus scanner program.
static splitExtensions($filename)
Split a file into a base name and all dot-delimited 'extensions' on the end.
Definition: UploadBase.php:971
static makeTitleSafe($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:474
cleanupTempFile()
If we've modified the upload file we need to manually remove it on exit to clean up.
Definition: UploadBase.php:951
getSourceType()
Returns the upload type.
Definition: UploadBase.php:198
const FILE_TOO_LARGE
Definition: UploadBase.php:68
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:870
checkWarnings()
Check for non fatal problems with the file.
Definition: UploadBase.php:619
initializeFromRequest(&$request)
Initialize from a WebRequest.
verifyUpload()
Verify whether the upload is sane.
Definition: UploadBase.php:285
const MIN_LENGTH_PARTNAME
Definition: UploadBase.php:58
getFileSize()
Return the file size.
Definition: UploadBase.php:247
const NS_FILE
Definition: Defines.php:75
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable or merely the Work and Derivative Works thereof Contribution shall mean any work of including the original version of the Work and any modifications or additions to that Work or Derivative Works that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner For the purposes of this submitted means any form of or written communication sent to the Licensor or its including but not limited to communication on electronic mailing source code control and issue tracking systems that are managed by
static getSha1Base36FromPath($path)
Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case encoding, zero padded to 31 digits.
Definition: FSFile.php:252
const VERIFICATION_ERROR
Definition: UploadBase.php:63
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
wfSuppressWarnings($end=false)
Reference-counted warning suppression.
static isEnabled()
Returns true if uploads are enabled.
Definition: UploadBase.php:102
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition: hooks.txt:240
#define the
validateName()
Verify that the name is valid and, if necessary, that we can overwrite.
Definition: UploadBase.php:347
const FILETYPE_BADTYPE
Definition: UploadBase.php:62
$wgMaxUploadSize
Max size for uploads, in bytes.
wfDebugLog($logGroup, $text, $dest= 'all', array $context=array())
Send a line to a supplementary debug log file, if configured, or main debug log if not...
getLocalFile()
Return the local file and initializes if necessary.
Definition: UploadBase.php:894
static singleton($wiki=false)
$wgAntivirusSetup
Configuration for different virus scanners.
Prior to maintenance scripts were a hodgepodge of code that had no cohesion or formal method of action Beginning in
Definition: maintenance.txt:1
const FILETYPE_MISSING
Definition: UploadBase.php:61
namespace and then decline to actually register it file or subcat img or subcat RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions instead of letting the login form give the generic error message that the account does not exist For when the account has been renamed or deleted or an array to pass a message key and parameters but no entry for that model exists in $wgContentHandlers if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object & $output
Definition: hooks.txt:952
static normalizeCss($value)
Normalize CSS into a format we can easily search for hostile input.
Definition: Sanitizer.php:832
$wgFileExtensions
This is the list of preferred extensions for uploading files.
static getFilenamePrefixBlacklist()
Get a list of blacklisted filename prefixes from [[MediaWiki:Filename-prefix-blacklist]].
$wgUploadSizeWarning
Warn if uploaded files are larger than this (in bytes), or false to disable.
verifyTitlePermissions($user)
Check whether the user can edit, upload and create the image.
Definition: UploadBase.php:581
$lines
Definition: router.php:66
const HOOK_ABORTED
Definition: UploadBase.php:67
stashFile(User $user=null)
If the user does not supply all necessary information in the first upload form submission (either by ...
Definition: UploadBase.php:918
getId()
Get the user's ID.
Definition: User.php:2030
static verifyExtension($mime, $extension)
Checks if the MIME type of the uploaded file matches the file extension.
detectScriptInSvg($filename, $partial)
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
Job for asynchronous rendering of thumbnails.
static detectScript($file, $mime, $extension)
Heuristic for detecting files that could contain JavaScript instructions or things that may look like...
static doWatch(Title $title, User $user, $checkRights=WatchedItem::CHECK_USER_RIGHTS)
Watch a page.
convertVerifyErrorToStatus($error)
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
Definition: hooks.txt:2212
$line
Definition: cdb.php:59
static $uploadHandlers
Definition: UploadBase.php:132
const CACHE_ANYTHING
Definition: Defines.php:101
wfRestoreWarnings()
Restore error level to previous value.
const WINDOWS_NONASCII_FILENAME
Definition: UploadBase.php:69
static getHandler($type)
Get a MediaHandler for a given MIME type from the instance cache.
$wgAllowTitlesInSVG
Disallow <title> element in SVG files.
static checkXMLEncodingMissmatch($file)
Check a whitelist of xml encodings that are known not to be interpreted differently by the server's x...
wfEscapeShellArg()
Windows-compatible version of escapeshellarg() Windows doesn't recognise single-quotes in the shell...
static checkCssFragment($value)
Check a block of CSS or CSS fragment for anything that looks like it is bringing in remote code...
wfMemcKey()
Get a cache key.
$wgOut
Definition: Setup.php:644
you don t have to do a grep find to see where the $wgReverseTitle variable is used
Definition: hooks.txt:117
static setSessionStatus(User $user, $statusKey, $value)
Set the current status of a chunked upload (used for polling)
$extensions
$mBlackListedExtensions
Definition: UploadBase.php:44
maintenance dev scripts can help quickly setup a local MediaWiki for development purposes Wikis setup in this way are NOT meant to be publicly available They use a development database not acceptible for use in production Place a sqlite database in an unsafe location a real wiki should never place it in And use predictable default logins for the initial administrator user Running maintenance dev install sh will download and install a local copy of php
Definition: README:5
$wgMimeTypeBlacklist
Files with these MIME types will never be allowed as uploads if $wgVerifyMimeType is enabled...
verifyFile()
Verifies that it's ok to include the uploaded file.
Definition: UploadBase.php:411
const OK
Definition: UploadBase.php:56
$wgVerifyMimeType
Determines if the MIME type of uploaded files should be checked.
$wgFileBlacklist
Files with these extensions will never be allowed as uploads.
static getPropsFromPath($path, $ext=true)
Get an associative array containing information about a file in the local filesystem.
Definition: FSFile.php:236
static getInfo($file, $prop, $result, $thumbParams=null, $opts=false)
Get result information for an image revision.
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk page
Definition: hooks.txt:2212
const EMPTY_FILE
Definition: UploadBase.php:57
static capitalize($text, $ns=NS_MAIN)
Capitalize a text string for a title if it belongs to a namespace that capitalizes.
Definition: Title.php:3265
static & makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:450
static newGood($value=null)
Factory function for good results.
Definition: Status.php:95
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing & $attribs
Definition: hooks.txt:1712
getRealPath($srcPath)
Definition: UploadBase.php:263
initializePathInfo($name, $tempPath, $fileSize, $removeTempFile=false)
Initialize the path information.
Definition: UploadBase.php:210
checkOverwrite($user)
Check if there's an overwrite conflict and, if so, if restrictions forbid this user from performing t...
$wgAntivirus
Internal name of virus scanner.
$matches