MediaWiki
REL1_22
|
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 }