MediaWiki
REL1_23
|
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 } 00207 00213 protected function makeTemporaryFile() { 00214 $tmpFile = TempFSFile::factory( 'URL' ); 00215 $tmpFile->bind( $this ); 00216 return $tmpFile->getPath(); 00217 } 00218 00226 public function saveTempFileChunk( $req, $buffer ) { 00227 $nbytes = fwrite( $this->mTmpHandle, $buffer ); 00228 00229 if ( $nbytes == strlen( $buffer ) ) { 00230 $this->mFileSize += $nbytes; 00231 } else { 00232 // Well... that's not good! 00233 fclose( $this->mTmpHandle ); 00234 $this->mTmpHandle = false; 00235 } 00236 00237 return $nbytes; 00238 } 00239 00247 protected function reallyFetchFile( $httpOptions = array() ) { 00248 global $wgCopyUploadProxy, $wgCopyUploadTimeout; 00249 if ( $this->mTempPath === false ) { 00250 return Status::newFatal( 'tmp-create-error' ); 00251 } 00252 00253 // Note the temporary file should already be created by makeTemporaryFile() 00254 $this->mTmpHandle = fopen( $this->mTempPath, 'wb' ); 00255 if ( !$this->mTmpHandle ) { 00256 return Status::newFatal( 'tmp-create-error' ); 00257 } 00258 00259 $this->mRemoveTempFile = true; 00260 $this->mFileSize = 0; 00261 00262 $options = $httpOptions + array( 00263 'followRedirects' => true, 00264 ); 00265 if ( $wgCopyUploadProxy !== false ) { 00266 $options['proxy'] = $wgCopyUploadProxy; 00267 } 00268 if ( $wgCopyUploadTimeout && !isset( $options['timeout'] ) ) { 00269 $options['timeout'] = $wgCopyUploadTimeout; 00270 } 00271 $req = MWHttpRequest::factory( $this->mUrl, $options ); 00272 $req->setCallback( array( $this, 'saveTempFileChunk' ) ); 00273 $status = $req->execute(); 00274 00275 if ( $this->mTmpHandle ) { 00276 // File got written ok... 00277 fclose( $this->mTmpHandle ); 00278 $this->mTmpHandle = null; 00279 } else { 00280 // We encountered a write error during the download... 00281 return Status::newFatal( 'tmp-write-error' ); 00282 } 00283 00284 if ( !$status->isOk() ) { 00285 return $status; 00286 } 00287 00288 return $status; 00289 } 00290 00296 public function verifyUpload() { 00297 if ( $this->mAsync ) { 00298 return array( 'status' => UploadBase::OK ); 00299 } 00300 return parent::verifyUpload(); 00301 } 00302 00308 public function checkWarnings() { 00309 if ( $this->mAsync ) { 00310 $this->mIgnoreWarnings = false; 00311 return array(); 00312 } 00313 return parent::checkWarnings(); 00314 } 00315 00322 public function verifyTitlePermissions( $user ) { 00323 if ( $this->mAsync ) { 00324 return true; 00325 } 00326 return parent::verifyTitlePermissions( $user ); 00327 } 00328 00338 public function performUpload( $comment, $pageText, $watch, $user ) { 00339 if ( $this->mAsync ) { 00340 $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user ); 00341 00342 return Status::newFatal( 'async', $sessionKey ); 00343 } 00344 00345 return parent::performUpload( $comment, $pageText, $watch, $user ); 00346 } 00347 00355 protected function insertJob( $comment, $pageText, $watch, $user ) { 00356 $sessionKey = $this->stashSession(); 00357 $job = new UploadFromUrlJob( $this->getTitle(), array( 00358 'url' => $this->mUrl, 00359 'comment' => $comment, 00360 'pageText' => $pageText, 00361 'watch' => $watch, 00362 'userName' => $user->getName(), 00363 'leaveMessage' => $this->mAsync == 'async-leavemessage', 00364 'ignoreWarnings' => $this->mIgnoreWarnings, 00365 'sessionId' => session_id(), 00366 'sessionKey' => $sessionKey, 00367 ) ); 00368 $job->initializeSessionData(); 00369 JobQueueGroup::singleton()->push( $job ); 00370 return $sessionKey; 00371 } 00372 00373 }