X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/74c929b24b048c9f1e31e17db757ae4195cd7673..dc9cc5d707f5a612938cc9371614cc41c328fda2:/includes/UploadBase.php diff --git a/includes/UploadBase.php b/includes/UploadBase.php deleted file mode 100644 index 91155a1b..00000000 --- a/includes/UploadBase.php +++ /dev/null @@ -1,867 +0,0 @@ -isAllowed( 'upload' ) ) - return 'upload'; - return true; - } - - // Upload handlers. Should probably just be a global - static $uploadHandlers = array( 'Stash', 'Upload', 'Url' ); - /** - * Create a form of UploadBase depending on wpSourceType and initializes it - */ - static function createFromRequest( &$request, $type = null ) { - $type = $type ? $type : $request->getVal( 'wpSourceType' ); - if( !$type ) - return null; - $type = ucfirst($type); - $className = 'UploadFrom'.$type; - if( !in_array( $type, self::$uploadHandlers ) ) - return null; - if( !call_user_func( array( $className, 'isEnabled' ) ) ) - return null; - if( !call_user_func( array( $className, 'isValidRequest' ), $request ) ) - return null; - - $handler = new $className; - $handler->initializeFromRequest( $request ); - return $handler; - } - - /** - * Check whether a request if valid for this handler - */ - static function isValidRequest( $request ) { - return false; - } - - function __construct() {} - - /** - * Do the real variable initialization - */ - function initialize( $name, $tempPath, $fileSize, $removeTempFile = false ) { - $this->mDesiredDestName = $name; - $this->mTempPath = $tempPath; - $this->mFileSize = $fileSize; - $this->mRemoveTempFile = $removeTempFile; - } - - /** - * Fetch the file. Usually a no-op - */ - function fetchFile() { - return self::OK; - } - - /** - * Verify whether the upload is sane. - * Returns self::OK or else an array with error information - */ - function verifyUpload() { - global $wgUser; - - /** - * If there was no filename or a zero size given, give up quick. - */ - if( empty( $this->mFileSize ) ) - return array( 'status' => self::EMPTY_FILE ); - - $nt = $this->getTitle(); - if( is_null( $nt ) ) { - $result = array( 'status' => $this->mTitleError ); - if( $this->mTitleError == self::ILLEGAL_FILENAME ) - $resul['filtered'] = $this->mFilteredName; - if ( $this->mTitleError == self::FILETYPE_BADTYPE ) - $result['finalExt'] = $this->mFinalExtension; - return $result; - } - $this->mLocalFile = wfLocalFile( $nt ); - $this->mDestName = $this->mLocalFile->getName(); - - /** - * In some cases we may forbid overwriting of existing files. - */ - $overwrite = $this->checkOverwrite( $this->mDestName ); - if( $overwrite !== true ) - return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite ); - - /** - * Look at the contents of the file; if we can recognize the - * type but it's corrupt or data of the wrong type, we should - * probably not accept it. - */ - $verification = $this->verifyFile( $this->mTempPath ); - - if( $verification !== true ) { - if( !is_array( $verification ) ) - $verification = array( $verification ); - $verification['status'] = self::VERIFICATION_ERROR; - return $verification; - } - - $error = ''; - if( !wfRunHooks( 'UploadVerification', - array( $this->mDestName, $this->mTempPath, &$error ) ) ) { - return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error ); - } - - return self::OK; - } - - /** - * Verifies that it's ok to include the uploaded file - * - * @param string $tmpfile the full path of the temporary file to verify - * @return mixed true of the file is verified, a string or array otherwise. - */ - protected function verifyFile( $tmpfile ) { - $this->mFileProps = File::getPropsFromPath( $this->mTempPath, - $this->mFinalExtension ); - $this->checkMacBinary(); - - #magically determine mime type - $magic = MimeMagic::singleton(); - $mime = $magic->guessMimeType( $tmpfile, false ); - - #check mime type, if desired - global $wgVerifyMimeType; - if ( $wgVerifyMimeType ) { - - wfDebug ( "\n\nmime: <$mime> extension: <{$this->mFinalExtension}>\n\n"); - #check mime type against file extension - if( !self::verifyExtension( $mime, $this->mFinalExtension ) ) { - return 'uploadcorrupt'; - } - - #check mime type blacklist - global $wgMimeTypeBlacklist; - if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist) - && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) { - return array( 'filetype-badmime', $mime ); - } - } - - #check for htmlish code and javascript - if( $this->detectScript ( $tmpfile, $mime, $this->mFinalExtension ) ) { - return 'uploadscripted'; - } - - /** - * Scan the uploaded file for viruses - */ - $virus = $this->detectVirus($tmpfile); - if ( $virus ) { - return array( 'uploadvirus', $virus ); - } - - wfDebug( __METHOD__.": all clear; passing.\n" ); - return true; - } - - /** - * Check whether the user can edit, upload and create the image - */ - function verifyPermissions( $user ) { - /** - * If the image is protected, non-sysop users won't be able - * to modify it by uploading a new revision. - */ - $nt = $this->getTitle(); - if( is_null( $nt ) ) - return true; - $permErrors = $nt->getUserPermissionsErrors( 'edit', $user ); - $permErrorsUpload = $nt->getUserPermissionsErrors( 'upload', $user ); - $permErrorsCreate = ( $nt->exists() ? array() : $nt->getUserPermissionsErrors( 'create', $user ) ); - if( $permErrors || $permErrorsUpload || $permErrorsCreate ) { - $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) ); - $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsCreate, $permErrors ) ); - return $permErrors; - } - return true; - } - - /** - * Check for non fatal problems with the file - */ - function checkWarnings() { - $warning = array(); - - $filename = $this->mLocalFile->getName(); - $n = strrpos( $filename, '.' ); - $partname = $n ? substr( $filename, 0, $n ) : $filename; - - // Check whether the resulting filename is different from the desired one - if( $this->mDesiredDestName != $filename ) - $warning['badfilename'] = $filename; - - // Check whether the file extension is on the unwanted list - global $wgCheckFileExtensions, $wgFileExtensions; - if ( $wgCheckFileExtensions ) { - if ( !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) ) - $warning['filetype-unwanted-type'] = $this->mFinalExtension; - } - - global $wgUploadSizeWarning; - if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) - $warning['large-file'] = $wgUploadSizeWarning; - - if ( $this->mFileSize == 0 ) - $warning['emptyfile'] = true; - - $exists = self::getExistsWarning( $this->mLocalFile ); - if( $exists !== false ) - $warning['exists'] = $exists; - - // Check whether this may be a thumbnail - if( $exists !== false && $exists[0] != 'thumb' - && self::isThumbName( $this->mLocalFile->getName() ) ) - $warning['file-thumbnail-no'] = substr( $filename , 0, - strpos( $nt->getText() , '-' ) +1 ); - - $hash = File::sha1Base36( $this->mTempPath ); - $dupes = RepoGroup::singleton()->findBySha1( $hash ); - if( $dupes ) - $warning['duplicate'] = $dupes; - - $filenamePrefixBlacklist = self::getFilenamePrefixBlacklist(); - foreach( $filenamePrefixBlacklist as $prefix ) { - if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) { - $warning['filename-bad-prefix'] = $prefix; - break; - } - } - - # If the file existed before and was deleted, warn the user of this - # Don't bother doing so if the file exists now, however - if( $this->mLocalFile->wasDeleted() && !$this->mLocalFile->exists() ) - $warning['filewasdeleted'] = $this->mLocalFile->getTitle(); - - return $warning; - } - - /** - * Really perform the upload. - */ - function performUpload( $comment, $pageText, $watch, $user ) { - $status = $this->mLocalFile->upload( $this->mTempPath, $comment, $pageText, - File::DELETE_SOURCE, $this->mFileProps, false, $user ); - - if( $status->isGood() && $watch ) { - $user->addWatch( $this->mLocalFile->getTitle() ); - } - - if( $status->isGood() ) - wfRunHooks( 'UploadComplete', array( &$this ) ); - - return $status; - } - - /** - * Returns a title or null - */ - function getTitle() { - if ( $this->mTitle !== false ) - return $this->mTitle; - - /** - * Chop off any directories in the given filename. Then - * filter out illegal characters, and try to make a legible name - * out of it. We'll strip some silently that Title would die on. - */ - - $basename = $this->mDesiredDestName; - - $this->mFilteredName = wfStripIllegalFilenameChars( $basename ); - - /** - * We'll want to blacklist against *any* 'extension', and use - * only the final one for the whitelist. - */ - list( $partname, $ext ) = $this->splitExtensions( $this->mFilteredName ); - - if( count( $ext ) ) { - $this->mFinalExtension = $ext[count( $ext ) - 1]; - } else { - $this->mFinalExtension = ''; - } - - /* Don't allow users to override the blacklist (check file extension) */ - global $wgCheckFileExtensions, $wgStrictFileExtensions; - global $wgFileExtensions, $wgFileBlacklist; - if ( $this->mFinalExtension == '' ) { - $this->mTitleError = self::FILETYPE_MISSING; - return $this->mTitle = null; - } elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) || - ( $wgCheckFileExtensions && $wgStrictFileExtensions && - !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) ) ) { - $this->mTitleError = self::FILETYPE_BADTYPE; - return $this->mTitle = null; - } - - # If there was more than one "extension", reassemble the base - # filename to prevent bogus complaints about length - if( count( $ext ) > 1 ) { - for( $i = 0; $i < count( $ext ) - 1; $i++ ) - $partname .= '.' . $ext[$i]; - } - - if( strlen( $partname ) < 1 ) { - $this->mTitleError = self::MIN_LENGTH_PARTNAME; - return $this->mTitle = null; - } - - $nt = Title::makeTitleSafe( NS_FILE, $this->mFilteredName ); - if( is_null( $nt ) ) { - $this->mTitleError = self::ILLEGAL_FILENAME; - return $this->mTitle = null; - } - return $this->mTitle = $nt; - } - - function getLocalFile() { - if( is_null( $this->mLocalFile ) ) { - $nt = $this->getTitle(); - $this->mLocalFile = is_null( $nt ) ? null : wfLocalFile( $nt ); - } - return $this->mLocalFile; - } - - /** - * Stash a file in a temporary directory for later processing - * after the user has confirmed it. - * - * If the user doesn't explicitly cancel or accept, these files - * can accumulate in the temp directory. - * - * @param string $saveName - the destination filename - * @param string $tempName - the source temporary file to save - * @return string - full path the stashed file, or false on failure - * @access private - */ - function saveTempUploadedFile( $saveName, $tempName ) { - global $wgOut; - $repo = RepoGroup::singleton()->getLocalRepo(); - $status = $repo->storeTemp( $saveName, $tempName ); - return $status; - } - - /** - * Stash a file in a temporary directory for later processing, - * and save the necessary descriptive info into the session. - * Returns a key value which will be passed through a form - * to pick up the path info on a later invocation. - * - * @return int - * @access private - */ - function stashSession() { - $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); - - if( !$status->isGood() ) { - # Couldn't save the file. - return false; - } - - return array( - 'mTempPath' => $status->value, - 'mFileSize' => $this->mFileSize, - 'mFileProps' => $this->mFileProps, - 'version' => self::SESSION_VERSION, - ); - } - - /** - * Remove a temporarily kept file stashed by saveTempUploadedFile(). - * @return success - */ - function unsaveUploadedFile() { - $repo = RepoGroup::singleton()->getLocalRepo(); - $success = $repo->freeTemp( $this->mTempPath ); - return $success; - } - - /** - * If we've modified the upload file we need to manually remove it - * on exit to clean up. - * @access private - */ - function cleanupTempFile() { - if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) { - wfDebug( __METHOD__.": Removing temporary file {$this->mTempPath}\n" ); - unlink( $this->mTempPath ); - } - } - - function getTempPath() { - return $this->mTempPath; - } - - - /** - * Split a file into a base name and all dot-delimited 'extensions' - * on the end. Some web server configurations will fall back to - * earlier pseudo-'extensions' to determine type and execute - * scripts, so the blacklist needs to check them all. - * - * @return array - */ - function splitExtensions( $filename ) { - $bits = explode( '.', $filename ); - $basename = array_shift( $bits ); - return array( $basename, $bits ); - } - - /** - * Perform case-insensitive match against a list of file extensions. - * Returns true if the extension is in the list. - * - * @param string $ext - * @param array $list - * @return bool - */ - function checkFileExtension( $ext, $list ) { - return in_array( strtolower( $ext ), $list ); - } - - /** - * Perform case-insensitive match against a list of file extensions. - * Returns true if any of the extensions are in the list. - * - * @param array $ext - * @param array $list - * @return bool - */ - function checkFileExtensionList( $ext, $list ) { - foreach( $ext as $e ) { - if( in_array( strtolower( $e ), $list ) ) { - return true; - } - } - return false; - } - - - /** - * Checks if the mime type of the uploaded file matches the file extension. - * - * @param string $mime the mime type of the uploaded file - * @param string $extension The filename extension that the file is to be served with - * @return bool - */ - public static function verifyExtension( $mime, $extension ) { - $magic = MimeMagic::singleton(); - - if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' ) - if ( ! $magic->isRecognizableExtension( $extension ) ) { - wfDebug( __METHOD__.": passing file with unknown detected mime type; " . - "unrecognized extension '$extension', can't verify\n" ); - return true; - } else { - wfDebug( __METHOD__.": rejecting file with unknown detected mime type; ". - "recognized extension '$extension', so probably invalid file\n" ); - return false; - } - - $match= $magic->isMatchingExtension($extension,$mime); - - if ($match===NULL) { - wfDebug( __METHOD__.": no file extension known for mime type $mime, passing file\n" ); - return true; - } elseif ($match===true) { - wfDebug( __METHOD__.": mime type $mime matches extension $extension, passing file\n" ); - - #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it! - return true; - - } else { - wfDebug( __METHOD__.": mime type $mime mismatches file extension $extension, rejecting file\n" ); - return false; - } - } - - /** - * Heuristic for detecting files that *could* contain JavaScript instructions or - * things that may look like HTML to a browser and are thus - * potentially harmful. The present implementation will produce false positives in some situations. - * - * @param string $file Pathname to the temporary upload file - * @param string $mime The mime type of the file - * @param string $extension The extension of the file - * @return bool true if the file contains something looking like embedded scripts - */ - function detectScript($file, $mime, $extension) { - global $wgAllowTitlesInSVG; - - #ugly hack: for text files, always look at the entire file. - #For binary field, just check the first K. - - if (strpos($mime,'text/')===0) $chunk = file_get_contents( $file ); - else { - $fp = fopen( $file, 'rb' ); - $chunk = fread( $fp, 1024 ); - fclose( $fp ); - } - - $chunk= strtolower( $chunk ); - - if (!$chunk) return false; - - #decode from UTF-16 if needed (could be used for obfuscation). - if (substr($chunk,0,2)=="\xfe\xff") $enc= "UTF-16BE"; - elseif (substr($chunk,0,2)=="\xff\xfe") $enc= "UTF-16LE"; - else $enc= NULL; - - if ($enc) $chunk= iconv($enc,"ASCII//IGNORE",$chunk); - - $chunk= trim($chunk); - - #FIXME: convert from UTF-16 if necessarry! - - wfDebug("SpecialUpload::detectScript: checking for embedded scripts and HTML stuff\n"); - - #check for HTML doctype - if (eregi("wrapWikiMsg( '