MediaWiki  REL1_22
UploadFromUrl.php
Go to the documentation of this file.
00001 <?php
00031 class UploadFromUrl extends UploadBase {
00032     protected $mAsync, $mUrl;
00033     protected $mIgnoreWarnings = true;
00034 
00035     protected $mTempPath, $mTmpHandle;
00036 
00037     protected static $allowedUrls = array();
00038 
00048     public static function isAllowed( $user ) {
00049         if ( !$user->isAllowed( 'upload_by_url' ) ) {
00050             return 'upload_by_url';
00051         }
00052         return parent::isAllowed( $user );
00053     }
00054 
00059     public static function isEnabled() {
00060         global $wgAllowCopyUploads;
00061         return $wgAllowCopyUploads && parent::isEnabled();
00062     }
00063 
00072     public static function isAllowedHost( $url ) {
00073         global $wgCopyUploadsDomains;
00074         if ( !count( $wgCopyUploadsDomains ) ) {
00075             return true;
00076         }
00077         $parsedUrl = wfParseUrl( $url );
00078         if ( !$parsedUrl ) {
00079             return false;
00080         }
00081         $valid = false;
00082         foreach ( $wgCopyUploadsDomains as $domain ) {
00083             // See if the domain for the upload matches this whitelisted domain
00084             $whitelistedDomainPieces = explode( '.', $domain );
00085             $uploadDomainPieces = explode( '.', $parsedUrl['host'] );
00086             if ( count( $whitelistedDomainPieces ) === count( $uploadDomainPieces ) ) {
00087                 $valid = true;
00088                 // See if all the pieces match or not (excluding wildcards)
00089                 foreach ( $whitelistedDomainPieces as $index => $piece ) {
00090                     if ( $piece !== '*' && $piece !== $uploadDomainPieces[$index] ) {
00091                         $valid = false;
00092                     }
00093                 }
00094                 if ( $valid ) {
00095                     // We found a match, so quit comparing against the list
00096                     break;
00097                 }
00098             }
00099             /* Non-wildcard test
00100             if ( $parsedUrl['host'] === $domain ) {
00101                 $valid = true;
00102                 break;
00103             }
00104             */
00105         }
00106         return $valid;
00107     }
00108 
00115     public static function isAllowedUrl( $url ) {
00116         if ( !isset( self::$allowedUrls[$url] ) ) {
00117             $allowed = true;
00118             wfRunHooks( 'IsUploadAllowedFromUrl', array( $url, &$allowed ) );
00119             self::$allowedUrls[$url] = $allowed;
00120         }
00121         return self::$allowedUrls[$url];
00122     }
00123 
00134     public function initialize( $name, $url, $async = false ) {
00135         global $wgAllowAsyncCopyUploads;
00136 
00137         $this->mUrl = $url;
00138         $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false;
00139         if ( $async ) {
00140             throw new MWException( 'Asynchronous copy uploads are no longer possible as of r81612.' );
00141         }
00142 
00143         $tempPath = $this->mAsync ? null : $this->makeTemporaryFile();
00144         # File size and removeTempFile will be filled in later
00145         $this->initializePathInfo( $name, $tempPath, 0, false );
00146     }
00147 
00152     public function initializeFromRequest( &$request ) {
00153         $desiredDestName = $request->getText( 'wpDestFile' );
00154         if ( !$desiredDestName ) {
00155             $desiredDestName = $request->getText( 'wpUploadFileURL' );
00156         }
00157         $this->initialize(
00158             $desiredDestName,
00159             trim( $request->getVal( 'wpUploadFileURL' ) ),
00160             false
00161         );
00162     }
00163 
00168     public static function isValidRequest( $request ) {
00169         global $wgUser;
00170 
00171         $url = $request->getVal( 'wpUploadFileURL' );
00172         return !empty( $url )
00173             && Http::isValidURI( $url )
00174             && $wgUser->isAllowed( 'upload_by_url' );
00175     }
00176 
00180     public function getSourceType() {
00181         return 'url';
00182     }
00183 
00191     public function fetchFile( $httpOptions = array() ) {
00192         if ( !Http::isValidURI( $this->mUrl ) ) {
00193             return Status::newFatal( 'http-invalid-url' );
00194         }
00195 
00196         if ( !self::isAllowedHost( $this->mUrl ) ) {
00197             return Status::newFatal( 'upload-copy-upload-invalid-domain' );
00198         }
00199         if ( !self::isAllowedUrl( $this->mUrl ) ) {
00200             return Status::newFatal( 'upload-copy-upload-invalid-url' );
00201         }
00202         if ( !$this->mAsync ) {
00203             return $this->reallyFetchFile( $httpOptions );
00204         }
00205         return Status::newGood();
00206     }
00212     protected function makeTemporaryFile() {
00213         return tempnam( wfTempDir(), 'URL' );
00214     }
00215 
00223     public function saveTempFileChunk( $req, $buffer ) {
00224         $nbytes = fwrite( $this->mTmpHandle, $buffer );
00225 
00226         if ( $nbytes == strlen( $buffer ) ) {
00227             $this->mFileSize += $nbytes;
00228         } else {
00229             // Well... that's not good!
00230             fclose( $this->mTmpHandle );
00231             $this->mTmpHandle = false;
00232         }
00233 
00234         return $nbytes;
00235     }
00236 
00244     protected function reallyFetchFile( $httpOptions = array() ) {
00245         global $wgCopyUploadProxy, $wgCopyUploadTimeout;
00246         if ( $this->mTempPath === false ) {
00247             return Status::newFatal( 'tmp-create-error' );
00248         }
00249 
00250         // Note the temporary file should already be created by makeTemporaryFile()
00251         $this->mTmpHandle = fopen( $this->mTempPath, 'wb' );
00252         if ( !$this->mTmpHandle ) {
00253             return Status::newFatal( 'tmp-create-error' );
00254         }
00255 
00256         $this->mRemoveTempFile = true;
00257         $this->mFileSize = 0;
00258 
00259         $options = $httpOptions + array(
00260             'followRedirects' => true,
00261         );
00262         if ( $wgCopyUploadProxy !== false ) {
00263             $options['proxy'] = $wgCopyUploadProxy;
00264         }
00265         if ( $wgCopyUploadTimeout && !isset( $options['timeout'] ) ) {
00266             $options['timeout'] = $wgCopyUploadTimeout;
00267         }
00268         $req = MWHttpRequest::factory( $this->mUrl, $options );
00269         $req->setCallback( array( $this, 'saveTempFileChunk' ) );
00270         $status = $req->execute();
00271 
00272         if ( $this->mTmpHandle ) {
00273             // File got written ok...
00274             fclose( $this->mTmpHandle );
00275             $this->mTmpHandle = null;
00276         } else {
00277             // We encountered a write error during the download...
00278             return Status::newFatal( 'tmp-write-error' );
00279         }
00280 
00281         if ( !$status->isOk() ) {
00282             return $status;
00283         }
00284 
00285         return $status;
00286     }
00287 
00293     public function verifyUpload() {
00294         if ( $this->mAsync ) {
00295             return array( 'status' => UploadBase::OK );
00296         }
00297         return parent::verifyUpload();
00298     }
00299 
00305     public function checkWarnings() {
00306         if ( $this->mAsync ) {
00307             $this->mIgnoreWarnings = false;
00308             return array();
00309         }
00310         return parent::checkWarnings();
00311     }
00312 
00319     public function verifyTitlePermissions( $user ) {
00320         if ( $this->mAsync ) {
00321             return true;
00322         }
00323         return parent::verifyTitlePermissions( $user );
00324     }
00325 
00335     public function performUpload( $comment, $pageText, $watch, $user ) {
00336         if ( $this->mAsync ) {
00337             $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user );
00338 
00339             return Status::newFatal( 'async', $sessionKey );
00340         }
00341 
00342         return parent::performUpload( $comment, $pageText, $watch, $user );
00343     }
00344 
00352     protected function insertJob( $comment, $pageText, $watch, $user ) {
00353         $sessionKey = $this->stashSession();
00354         $job = new UploadFromUrlJob( $this->getTitle(), array(
00355             'url' => $this->mUrl,
00356             'comment' => $comment,
00357             'pageText' => $pageText,
00358             'watch' => $watch,
00359             'userName' => $user->getName(),
00360             'leaveMessage' => $this->mAsync == 'async-leavemessage',
00361             'ignoreWarnings' => $this->mIgnoreWarnings,
00362             'sessionId' => session_id(),
00363             'sessionKey' => $sessionKey,
00364         ) );
00365         $job->initializeSessionData();
00366         JobQueueGroup::singleton()->push( $job );
00367         return $sessionKey;
00368     }
00369 
00370 }