MediaWiki
REL1_21
|
00001 <?php 00031 class UploadFromUrl extends UploadBase { 00032 protected $mAsync, $mUrl; 00033 protected $mIgnoreWarnings = true; 00034 00035 protected $mTempPath, $mTmpHandle; 00036 00046 public static function isAllowed( $user ) { 00047 if ( !$user->isAllowed( 'upload_by_url' ) ) { 00048 return 'upload_by_url'; 00049 } 00050 return parent::isAllowed( $user ); 00051 } 00052 00057 public static function isEnabled() { 00058 global $wgAllowCopyUploads; 00059 return $wgAllowCopyUploads && parent::isEnabled(); 00060 } 00061 00070 public static function isAllowedHost( $url ) { 00071 global $wgCopyUploadsDomains; 00072 if ( !count( $wgCopyUploadsDomains ) ) { 00073 return true; 00074 } 00075 $parsedUrl = wfParseUrl( $url ); 00076 if ( !$parsedUrl ) { 00077 return false; 00078 } 00079 $valid = false; 00080 foreach( $wgCopyUploadsDomains as $domain ) { 00081 // See if the domain for the upload matches this whitelisted domain 00082 $whitelistedDomainPieces = explode( '.', $domain ); 00083 $uploadDomainPieces = explode( '.', $parsedUrl['host'] ); 00084 if ( count( $whitelistedDomainPieces ) === count( $uploadDomainPieces ) ) { 00085 $valid = true; 00086 // See if all the pieces match or not (excluding wildcards) 00087 foreach ( $whitelistedDomainPieces as $index => $piece ) { 00088 if ( $piece !== '*' && $piece !== $uploadDomainPieces[$index] ) { 00089 $valid = false; 00090 } 00091 } 00092 if ( $valid ) { 00093 // We found a match, so quit comparing against the list 00094 break; 00095 } 00096 } 00097 /* Non-wildcard test 00098 if ( $parsedUrl['host'] === $domain ) { 00099 $valid = true; 00100 break; 00101 } 00102 */ 00103 } 00104 return $valid; 00105 } 00106 00117 public function initialize( $name, $url, $async = false ) { 00118 global $wgAllowAsyncCopyUploads; 00119 00120 $this->mUrl = $url; 00121 $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false; 00122 if ( $async ) { 00123 throw new MWException( 'Asynchronous copy uploads are no longer possible as of r81612.' ); 00124 } 00125 00126 $tempPath = $this->mAsync ? null : $this->makeTemporaryFile(); 00127 # File size and removeTempFile will be filled in later 00128 $this->initializePathInfo( $name, $tempPath, 0, false ); 00129 } 00130 00135 public function initializeFromRequest( &$request ) { 00136 $desiredDestName = $request->getText( 'wpDestFile' ); 00137 if ( !$desiredDestName ) { 00138 $desiredDestName = $request->getText( 'wpUploadFileURL' ); 00139 } 00140 $this->initialize( 00141 $desiredDestName, 00142 trim( $request->getVal( 'wpUploadFileURL' ) ), 00143 false 00144 ); 00145 } 00146 00151 public static function isValidRequest( $request ) { 00152 global $wgUser; 00153 00154 $url = $request->getVal( 'wpUploadFileURL' ); 00155 return !empty( $url ) 00156 && Http::isValidURI( $url ) 00157 && $wgUser->isAllowed( 'upload_by_url' ); 00158 } 00159 00163 public function getSourceType() { return 'url'; } 00164 00168 public function fetchFile() { 00169 if ( !Http::isValidURI( $this->mUrl ) ) { 00170 return Status::newFatal( 'http-invalid-url' ); 00171 } 00172 00173 if( !self::isAllowedHost( $this->mUrl ) ) { 00174 return Status::newFatal( 'upload-copy-upload-invalid-domain' ); 00175 } 00176 if ( !$this->mAsync ) { 00177 return $this->reallyFetchFile(); 00178 } 00179 return Status::newGood(); 00180 } 00186 protected function makeTemporaryFile() { 00187 return tempnam( wfTempDir(), 'URL' ); 00188 } 00189 00197 public function saveTempFileChunk( $req, $buffer ) { 00198 $nbytes = fwrite( $this->mTmpHandle, $buffer ); 00199 00200 if ( $nbytes == strlen( $buffer ) ) { 00201 $this->mFileSize += $nbytes; 00202 } else { 00203 // Well... that's not good! 00204 fclose( $this->mTmpHandle ); 00205 $this->mTmpHandle = false; 00206 } 00207 00208 return $nbytes; 00209 } 00210 00216 protected function reallyFetchFile() { 00217 if ( $this->mTempPath === false ) { 00218 return Status::newFatal( 'tmp-create-error' ); 00219 } 00220 00221 // Note the temporary file should already be created by makeTemporaryFile() 00222 $this->mTmpHandle = fopen( $this->mTempPath, 'wb' ); 00223 if ( !$this->mTmpHandle ) { 00224 return Status::newFatal( 'tmp-create-error' ); 00225 } 00226 00227 $this->mRemoveTempFile = true; 00228 $this->mFileSize = 0; 00229 00230 $options = array( 00231 'followRedirects' => true 00232 ); 00233 global $wgCopyUploadProxy; 00234 if ( $wgCopyUploadProxy !== false ) { 00235 $options['proxy'] = $wgCopyUploadProxy; 00236 } 00237 $req = MWHttpRequest::factory( $this->mUrl, $options ); 00238 $req->setCallback( array( $this, 'saveTempFileChunk' ) ); 00239 $status = $req->execute(); 00240 00241 if ( $this->mTmpHandle ) { 00242 // File got written ok... 00243 fclose( $this->mTmpHandle ); 00244 $this->mTmpHandle = null; 00245 } else { 00246 // We encountered a write error during the download... 00247 return Status::newFatal( 'tmp-write-error' ); 00248 } 00249 00250 if ( !$status->isOk() ) { 00251 return $status; 00252 } 00253 00254 return $status; 00255 } 00256 00262 public function verifyUpload() { 00263 if ( $this->mAsync ) { 00264 return array( 'status' => UploadBase::OK ); 00265 } 00266 return parent::verifyUpload(); 00267 } 00268 00274 public function checkWarnings() { 00275 if ( $this->mAsync ) { 00276 $this->mIgnoreWarnings = false; 00277 return array(); 00278 } 00279 return parent::checkWarnings(); 00280 } 00281 00288 public function verifyTitlePermissions( $user ) { 00289 if ( $this->mAsync ) { 00290 return true; 00291 } 00292 return parent::verifyTitlePermissions( $user ); 00293 } 00294 00304 public function performUpload( $comment, $pageText, $watch, $user ) { 00305 if ( $this->mAsync ) { 00306 $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user ); 00307 00308 return Status::newFatal( 'async', $sessionKey ); 00309 } 00310 00311 return parent::performUpload( $comment, $pageText, $watch, $user ); 00312 } 00313 00321 protected function insertJob( $comment, $pageText, $watch, $user ) { 00322 $sessionKey = $this->stashSession(); 00323 $job = new UploadFromUrlJob( $this->getTitle(), array( 00324 'url' => $this->mUrl, 00325 'comment' => $comment, 00326 'pageText' => $pageText, 00327 'watch' => $watch, 00328 'userName' => $user->getName(), 00329 'leaveMessage' => $this->mAsync == 'async-leavemessage', 00330 'ignoreWarnings' => $this->mIgnoreWarnings, 00331 'sessionId' => session_id(), 00332 'sessionKey' => $sessionKey, 00333 ) ); 00334 $job->initializeSessionData(); 00335 JobQueueGroup::singleton()->push( $job ); 00336 return $sessionKey; 00337 } 00338 00339 }