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() {
103  global $wgEnableUploads;
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 ) {
378  global $wgVerifyMimeType;
379  if ( $wgVerifyMimeType ) {
380  wfDebug( "mime: <$mime> extension: <{$this->mFinalExtension}>\n" );
381  global $wgMimeTypeBlacklist;
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() {
412  global $wgVerifyMimeType, $wgDisableUploadScriptChecks;
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() {
472  global $wgAllowJavaUploads, $wgDisableUploadScriptChecks;
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
643  global $wgCheckFileExtensions, $wgFileExtensions;
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 
652  global $wgUploadSizeWarning;
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() {
739  global $wgUploadThumbnailRenderMap;
740 
741  $jobs = array();
742 
743  $sizes = $wgUploadThumbnailRenderMap;
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) */
837  global $wgCheckFileExtensions, $wgStrictFileExtensions;
838  global $wgFileExtensions, $wgFileBlacklist;
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 ) {
1063  global $wgAllowTitlesInSVG;
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 ) {
1195  global $wgSVGMetadataCutoff;
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  # href with embedded svg as target
1416  if ( $stripped == 'href' && preg_match( '!data:[^,]*image/svg[^,]*,!sim', $value ) ) {
1417  wfDebug( __METHOD__ . ": Found href to embedded svg "
1418  . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
1419 
1420  return true;
1421  }
1422 
1423  # href with embedded (text/xml) svg as target
1424  if ( $stripped == 'href' && preg_match( '!data:[^,]*text/xml[^,]*,!sim', $value ) ) {
1425  wfDebug( __METHOD__ . ": Found href to embedded svg "
1426  . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
1427 
1428  return true;
1429  }
1430 
1431  # Change href with animate from (http://html5sec.org/#137). This doesn't seem
1432  # possible without embedding the svg, but filter here in case.
1433  if ( $stripped == 'from'
1434  && $strippedElement === 'animate'
1435  && !preg_match( '!^https?://!im', $value )
1436  ) {
1437  wfDebug( __METHOD__ . ": Found animate that might be changing href using from "
1438  . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
1439 
1440  return true;
1441  }
1442 
1443  # use set/animate to add event-handler attribute to parent
1444  if ( ( $strippedElement == 'set' || $strippedElement == 'animate' )
1445  && $stripped == 'attributename'
1446  && substr( $value, 0, 2 ) == 'on'
1447  ) {
1448  wfDebug( __METHOD__ . ": Found svg setting event-handler attribute with "
1449  . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
1450 
1451  return true;
1452  }
1453 
1454  # use set to add href attribute to parent element
1455  if ( $strippedElement == 'set'
1456  && $stripped == 'attributename'
1457  && strpos( $value, 'href' ) !== false
1458  ) {
1459  wfDebug( __METHOD__ . ": Found svg setting href attribute '$value' in uploaded file.\n" );
1460 
1461  return true;
1462  }
1463 
1464  # use set to add a remote / data / script target to an element
1465  if ( $strippedElement == 'set'
1466  && $stripped == 'to'
1467  && preg_match( '!(http|https|data|script):!sim', $value )
1468  ) {
1469  wfDebug( __METHOD__ . ": Found svg setting attribute to '$value' in uploaded file.\n" );
1470 
1471  return true;
1472  }
1473 
1474  # use handler attribute with remote / data / script
1475  if ( $stripped == 'handler' && preg_match( '!(http|https|data|script):!sim', $value ) ) {
1476  wfDebug( __METHOD__ . ": Found svg setting handler with remote/data/script "
1477  . "'$attrib'='$value' in uploaded file.\n" );
1478 
1479  return true;
1480  }
1481 
1482  # use CSS styles to bring in remote code
1483  if ( $stripped == 'style'
1484  && self::checkCssFragment( Sanitizer::normalizeCss( $value ) )
1485  ) {
1486  wfDebug( __METHOD__ . ": Found svg setting a style with "
1487  . "remote url '$attrib'='$value' in uploaded file.\n" );
1488  return true;
1489  }
1490 
1491  # Several attributes can include css, css character escaping isn't allowed
1492  $cssAttrs = array( 'font', 'clip-path', 'fill', 'filter', 'marker',
1493  'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke' );
1494  if ( in_array( $stripped, $cssAttrs )
1495  && self::checkCssFragment( $value )
1496  ) {
1497  wfDebug( __METHOD__ . ": Found svg setting a style with "
1498  . "remote url '$attrib'='$value' in uploaded file.\n" );
1499  return true;
1500  }
1501 
1502  # image filters can pull in url, which could be svg that executes scripts
1503  if ( $strippedElement == 'image'
1504  && $stripped == 'filter'
1505  && preg_match( '!url\s*\(!sim', $value )
1506  ) {
1507  wfDebug( __METHOD__ . ": Found image filter with url: "
1508  . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
1509 
1510  return true;
1511  }
1512  }
1513 
1514  return false; //No scripts detected
1515  }
1516 
1524  private static function checkCssFragment( $value ) {
1525 
1526  # Forbid external stylesheets, for both reliability and to protect viewer's privacy
1527  if ( strpos( $value, '@import' ) !== false ) {
1528  return true;
1529  }
1530 
1531  # We allow @font-face to embed fonts with data: urls, so we snip the string
1532  # 'url' out so this case won't match when we check for urls below
1533  $pattern = '!(@font-face\s*{[^}]*src:)url(\("data:;base64,)!im';
1534  $value = preg_replace( $pattern, '$1$2', $value );
1535 
1536  # Check for remote and executable CSS. Unlike in Sanitizer::checkCss, the CSS
1537  # properties filter and accelerator don't seem to be useful for xss in SVG files.
1538  # Expression and -o-link don't seem to work either, but filtering them here in case.
1539  # Additionally, we catch remote urls like url("http:..., url('http:..., url(http:...,
1540  # but not local ones such as url("#..., url('#..., url(#....
1541  if ( preg_match( '!expression
1542  | -o-link\s*:
1543  | -o-link-source\s*:
1544  | -o-replace\s*:!imx', $value ) ) {
1545  return true;
1546  }
1547 
1548  if ( preg_match_all(
1549  "!(\s*(url|image|image-set)\s*\(\s*[\"']?\s*[^#]+.*?\))!sim",
1550  $value,
1551  $matches
1552  ) !== 0
1553  ) {
1554  # TODO: redo this in one regex. Until then, url("#whatever") matches the first
1555  foreach ( $matches[1] as $match ) {
1556  if ( !preg_match( "!\s*(url|image|image-set)\s*\(\s*(#|'#|\"#)!im", $match ) ) {
1557  return true;
1558  }
1559  }
1560  }
1561 
1562  if ( preg_match( '/[\000-\010\013\016-\037\177]/', $value ) ) {
1563  return true;
1564  }
1565 
1566  return false;
1567  }
1568 
1574  private static function splitXmlNamespace( $element ) {
1575  // 'http://www.w3.org/2000/svg:script' -> array( 'http://www.w3.org/2000/svg', 'script' )
1576  $parts = explode( ':', strtolower( $element ) );
1577  $name = array_pop( $parts );
1578  $ns = implode( ':', $parts );
1579 
1580  return array( $ns, $name );
1581  }
1582 
1587  private function stripXmlNamespace( $name ) {
1588  // 'http://www.w3.org/2000/svg:script' -> 'script'
1589  $parts = explode( ':', strtolower( $name ) );
1590 
1591  return array_pop( $parts );
1592  }
1593 
1604  public static function detectVirus( $file ) {
1605  global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut;
1606 
1607  if ( !$wgAntivirus ) {
1608  wfDebug( __METHOD__ . ": virus scanner disabled\n" );
1609 
1610  return null;
1611  }
1612 
1613  if ( !$wgAntivirusSetup[$wgAntivirus] ) {
1614  wfDebug( __METHOD__ . ": unknown virus scanner: $wgAntivirus\n" );
1615  $wgOut->wrapWikiMsg( "<div class=\"error\">\n$1\n</div>",
1616  array( 'virus-badscanner', $wgAntivirus ) );
1617 
1618  return wfMessage( 'virus-unknownscanner' )->text() . " $wgAntivirus";
1619  }
1620 
1621  # look up scanner configuration
1622  $command = $wgAntivirusSetup[$wgAntivirus]['command'];
1623  $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]['codemap'];
1624  $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]['messagepattern'] ) ?
1625  $wgAntivirusSetup[$wgAntivirus]['messagepattern'] : null;
1626 
1627  if ( strpos( $command, "%f" ) === false ) {
1628  # simple pattern: append file to scan
1629  $command .= " " . wfEscapeShellArg( $file );
1630  } else {
1631  # complex pattern: replace "%f" with file to scan
1632  $command = str_replace( "%f", wfEscapeShellArg( $file ), $command );
1633  }
1634 
1635  wfDebug( __METHOD__ . ": running virus scan: $command \n" );
1636 
1637  # execute virus scanner
1638  $exitCode = false;
1639 
1640  # NOTE: there's a 50 line workaround to make stderr redirection work on windows, too.
1641  # that does not seem to be worth the pain.
1642  # Ask me (Duesentrieb) about it if it's ever needed.
1643  $output = wfShellExecWithStderr( $command, $exitCode );
1644 
1645  # map exit code to AV_xxx constants.
1646  $mappedCode = $exitCode;
1647  if ( $exitCodeMap ) {
1648  if ( isset( $exitCodeMap[$exitCode] ) ) {
1649  $mappedCode = $exitCodeMap[$exitCode];
1650  } elseif ( isset( $exitCodeMap["*"] ) ) {
1651  $mappedCode = $exitCodeMap["*"];
1652  }
1653  }
1654 
1655  /* NB: AV_NO_VIRUS is 0 but AV_SCAN_FAILED is false,
1656  * so we need the strict equalities === and thus can't use a switch here
1657  */
1658  if ( $mappedCode === AV_SCAN_FAILED ) {
1659  # scan failed (code was mapped to false by $exitCodeMap)
1660  wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" );
1661 
1662  $output = $wgAntivirusRequired
1663  ? wfMessage( 'virus-scanfailed', array( $exitCode ) )->text()
1664  : null;
1665  } elseif ( $mappedCode === AV_SCAN_ABORTED ) {
1666  # scan failed because filetype is unknown (probably imune)
1667  wfDebug( __METHOD__ . ": unsupported file type $file (code $exitCode).\n" );
1668  $output = null;
1669  } elseif ( $mappedCode === AV_NO_VIRUS ) {
1670  # no virus found
1671  wfDebug( __METHOD__ . ": file passed virus scan.\n" );
1672  $output = false;
1673  } else {
1674  $output = trim( $output );
1675 
1676  if ( !$output ) {
1677  $output = true; #if there's no output, return true
1678  } elseif ( $msgPattern ) {
1679  $groups = array();
1680  if ( preg_match( $msgPattern, $output, $groups ) ) {
1681  if ( $groups[1] ) {
1682  $output = $groups[1];
1683  }
1684  }
1685  }
1686 
1687  wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output \n" );
1688  }
1689 
1690  return $output;
1691  }
1692 
1701  private function checkOverwrite( $user ) {
1702  // First check whether the local file can be overwritten
1703  $file = $this->getLocalFile();
1704  $file->load( File::READ_LATEST );
1705  if ( $file->exists() ) {
1706  if ( !self::userCanReUpload( $user, $file ) ) {
1707  return array( 'fileexists-forbidden', $file->getName() );
1708  } else {
1709  return true;
1710  }
1711  }
1712 
1713  /* Check shared conflicts: if the local file does not exist, but
1714  * wfFindFile finds a file, it exists in a shared repository.
1715  */
1716  $file = wfFindFile( $this->getTitle(), array( 'latest' => true ) );
1717  if ( $file && !$user->isAllowed( 'reupload-shared' ) ) {
1718  return array( 'fileexists-shared-forbidden', $file->getName() );
1719  }
1720 
1721  return true;
1722  }
1723 
1731  public static function userCanReUpload( User $user, $img ) {
1732  if ( $user->isAllowed( 'reupload' ) ) {
1733  return true; // non-conditional
1734  }
1735  if ( !$user->isAllowed( 'reupload-own' ) ) {
1736  return false;
1737  }
1738  if ( is_string( $img ) ) {
1739  $img = wfLocalFile( $img );
1740  }
1741  if ( !( $img instanceof LocalFile ) ) {
1742  return false;
1743  }
1744 
1745  $img->load( File::READ_LATEST );
1746 
1747  return $user->getId() == $img->getUser( 'id' );
1748  }
1749 
1761  public static function getExistsWarning( $file ) {
1762  if ( $file->exists() ) {
1763  return array( 'warning' => 'exists', 'file' => $file );
1764  }
1765 
1766  if ( $file->getTitle()->getArticleID() ) {
1767  return array( 'warning' => 'page-exists', 'file' => $file );
1768  }
1769 
1770  if ( $file->wasDeleted() && !$file->exists() ) {
1771  return array( 'warning' => 'was-deleted', 'file' => $file );
1772  }
1773 
1774  if ( strpos( $file->getName(), '.' ) == false ) {
1775  $partname = $file->getName();
1776  $extension = '';
1777  } else {
1778  $n = strrpos( $file->getName(), '.' );
1779  $extension = substr( $file->getName(), $n + 1 );
1780  $partname = substr( $file->getName(), 0, $n );
1781  }
1782  $normalizedExtension = File::normalizeExtension( $extension );
1783 
1784  if ( $normalizedExtension != $extension ) {
1785  // We're not using the normalized form of the extension.
1786  // Normal form is lowercase, using most common of alternate
1787  // extensions (eg 'jpg' rather than 'JPEG').
1788  //
1789  // Check for another file using the normalized form...
1790  $nt_lc = Title::makeTitle( NS_FILE, "{$partname}.{$normalizedExtension}" );
1791  $file_lc = wfLocalFile( $nt_lc );
1792 
1793  if ( $file_lc->exists() ) {
1794  return array(
1795  'warning' => 'exists-normalized',
1796  'file' => $file,
1797  'normalizedFile' => $file_lc
1798  );
1799  }
1800  }
1801 
1802  // Check for files with the same name but a different extension
1803  $similarFiles = RepoGroup::singleton()->getLocalRepo()->findFilesByPrefix(
1804  "{$partname}.", 1 );
1805  if ( count( $similarFiles ) ) {
1806  return array(
1807  'warning' => 'exists-normalized',
1808  'file' => $file,
1809  'normalizedFile' => $similarFiles[0],
1810  );
1811  }
1812 
1813  if ( self::isThumbName( $file->getName() ) ) {
1814  # Check for filenames like 50px- or 180px-, these are mostly thumbnails
1815  $nt_thb = Title::newFromText(
1816  substr( $partname, strpos( $partname, '-' ) + 1 ) . '.' . $extension,
1817  NS_FILE
1818  );
1819  $file_thb = wfLocalFile( $nt_thb );
1820  if ( $file_thb->exists() ) {
1821  return array(
1822  'warning' => 'thumb',
1823  'file' => $file,
1824  'thumbFile' => $file_thb
1825  );
1826  } else {
1827  // File does not exist, but we just don't like the name
1828  return array(
1829  'warning' => 'thumb-name',
1830  'file' => $file,
1831  'thumbFile' => $file_thb
1832  );
1833  }
1834  }
1835 
1836  foreach ( self::getFilenamePrefixBlacklist() as $prefix ) {
1837  if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
1838  return array(
1839  'warning' => 'bad-prefix',
1840  'file' => $file,
1841  'prefix' => $prefix
1842  );
1843  }
1844  }
1845 
1846  return false;
1847  }
1848 
1854  public static function isThumbName( $filename ) {
1855  $n = strrpos( $filename, '.' );
1856  $partname = $n ? substr( $filename, 0, $n ) : $filename;
1857 
1858  return (
1859  substr( $partname, 3, 3 ) == 'px-' ||
1860  substr( $partname, 2, 3 ) == 'px-'
1861  ) &&
1862  preg_match( "/[0-9]{2}/", substr( $partname, 0, 2 ) );
1863  }
1864 
1870  public static function getFilenamePrefixBlacklist() {
1871  $blacklist = array();
1872  $message = wfMessage( 'filename-prefix-blacklist' )->inContentLanguage();
1873  if ( !$message->isDisabled() ) {
1874  $lines = explode( "\n", $message->plain() );
1875  foreach ( $lines as $line ) {
1876  // Remove comment lines
1877  $comment = substr( trim( $line ), 0, 1 );
1878  if ( $comment == '#' || $comment == '' ) {
1879  continue;
1880  }
1881  // Remove additional comments after a prefix
1882  $comment = strpos( $line, '#' );
1883  if ( $comment > 0 ) {
1884  $line = substr( $line, 0, $comment - 1 );
1885  }
1886  $blacklist[] = trim( $line );
1887  }
1888  }
1889 
1890  return $blacklist;
1891  }
1892 
1904  public function getImageInfo( $result ) {
1905  $file = $this->getLocalFile();
1911  if ( $file instanceof UploadStashFile ) {
1913  $info = ApiQueryStashImageInfo::getInfo( $file, array_flip( $imParam ), $result );
1914  } else {
1916  $info = ApiQueryImageInfo::getInfo( $file, array_flip( $imParam ), $result );
1917  }
1918 
1919  return $info;
1920  }
1921 
1926  public function convertVerifyErrorToStatus( $error ) {
1927  $code = $error['status'];
1928  unset( $code['status'] );
1929 
1930  return Status::newFatal( $this->getVerificationErrorCode( $code ), $error );
1931  }
1932 
1937  public static function getMaxUploadSize( $forType = null ) {
1938  global $wgMaxUploadSize;
1939 
1940  if ( is_array( $wgMaxUploadSize ) ) {
1941  if ( !is_null( $forType ) && isset( $wgMaxUploadSize[$forType] ) ) {
1942  return $wgMaxUploadSize[$forType];
1943  } else {
1944  return $wgMaxUploadSize['*'];
1945  }
1946  } else {
1947  return intval( $wgMaxUploadSize );
1948  }
1949  }
1950 
1960  public static function getSessionStatus( User $user, $statusKey ) {
1961  $key = wfMemcKey( 'uploadstatus', $user->getId() ?: md5( $user->getName() ), $statusKey );
1962 
1963  return wfGetCache( CACHE_ANYTHING )->get( $key );
1964  }
1965 
1976  public static function setSessionStatus( User $user, $statusKey, $value ) {
1977  $key = wfMemcKey( 'uploadstatus', $user->getId() ?: md5( $user->getName() ), $statusKey );
1978 
1980  if ( $value === false ) {
1981  $cache->delete( $key );
1982  } else {
1983  $cache->set( $key, $value, 86400 );
1984  }
1985  }
1986 }
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:1697
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
$size
Definition: RandomTest.php:76
if(!$wgHtml5Version &&$wgAllowRdfaAttributes) $wgFileExtensions
Definition: Setup.php:433
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:143
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:146
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.
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:1967
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:145
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
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:1399
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
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:2154
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:1695
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.
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:469
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:80
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
$n
Definition: RandomTest.php:77
const FILETYPE_BADTYPE
Definition: UploadBase.php:62
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)
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:834
static getFilenamePrefixBlacklist()
Get a list of blacklisted filename prefixes from [[MediaWiki:Filename-prefix-blacklist]].
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:1943
static verifyExtension($mime, $extension)
Checks if the MIME type of the uploaded file matches the file extension.
detectScriptInSvg($filename, $partial)
SQUARED OK
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
if(PHP_SAPI!= 'cli') $file
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:2179
$line
Definition: cdb.php:59
static $uploadHandlers
Definition: UploadBase.php:132
const CACHE_ANYTHING
Definition: Defines.php:106
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.
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:630
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
verifyFile()
Verifies that it's ok to include the uploaded file.
Definition: UploadBase.php:411
const OK
Definition: UploadBase.php:56
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:2179
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:3263
static & makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:445
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:1697
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...
$matches