MediaWiki
REL1_24
|
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 00053 return parent::isAllowed( $user ); 00054 } 00055 00060 public static function isEnabled() { 00061 global $wgAllowCopyUploads; 00062 00063 return $wgAllowCopyUploads && parent::isEnabled(); 00064 } 00065 00074 public static function isAllowedHost( $url ) { 00075 global $wgCopyUploadsDomains; 00076 if ( !count( $wgCopyUploadsDomains ) ) { 00077 return true; 00078 } 00079 $parsedUrl = wfParseUrl( $url ); 00080 if ( !$parsedUrl ) { 00081 return false; 00082 } 00083 $valid = false; 00084 foreach ( $wgCopyUploadsDomains as $domain ) { 00085 // See if the domain for the upload matches this whitelisted domain 00086 $whitelistedDomainPieces = explode( '.', $domain ); 00087 $uploadDomainPieces = explode( '.', $parsedUrl['host'] ); 00088 if ( count( $whitelistedDomainPieces ) === count( $uploadDomainPieces ) ) { 00089 $valid = true; 00090 // See if all the pieces match or not (excluding wildcards) 00091 foreach ( $whitelistedDomainPieces as $index => $piece ) { 00092 if ( $piece !== '*' && $piece !== $uploadDomainPieces[$index] ) { 00093 $valid = false; 00094 } 00095 } 00096 if ( $valid ) { 00097 // We found a match, so quit comparing against the list 00098 break; 00099 } 00100 } 00101 /* Non-wildcard test 00102 if ( $parsedUrl['host'] === $domain ) { 00103 $valid = true; 00104 break; 00105 } 00106 */ 00107 } 00108 00109 return $valid; 00110 } 00111 00118 public static function isAllowedUrl( $url ) { 00119 if ( !isset( self::$allowedUrls[$url] ) ) { 00120 $allowed = true; 00121 wfRunHooks( 'IsUploadAllowedFromUrl', array( $url, &$allowed ) ); 00122 self::$allowedUrls[$url] = $allowed; 00123 } 00124 00125 return self::$allowedUrls[$url]; 00126 } 00127 00138 public function initialize( $name, $url, $async = false ) { 00139 global $wgAllowAsyncCopyUploads; 00140 00141 $this->mUrl = $url; 00142 $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false; 00143 if ( $async ) { 00144 throw new MWException( 'Asynchronous copy uploads are no longer possible as of r81612.' ); 00145 } 00146 00147 $tempPath = $this->mAsync ? null : $this->makeTemporaryFile(); 00148 # File size and removeTempFile will be filled in later 00149 $this->initializePathInfo( $name, $tempPath, 0, false ); 00150 } 00151 00156 public function initializeFromRequest( &$request ) { 00157 $desiredDestName = $request->getText( 'wpDestFile' ); 00158 if ( !$desiredDestName ) { 00159 $desiredDestName = $request->getText( 'wpUploadFileURL' ); 00160 } 00161 $this->initialize( 00162 $desiredDestName, 00163 trim( $request->getVal( 'wpUploadFileURL' ) ), 00164 false 00165 ); 00166 } 00167 00172 public static function isValidRequest( $request ) { 00173 global $wgUser; 00174 00175 $url = $request->getVal( 'wpUploadFileURL' ); 00176 00177 return !empty( $url ) 00178 && Http::isValidURI( $url ) 00179 && $wgUser->isAllowed( 'upload_by_url' ); 00180 } 00181 00185 public function getSourceType() { 00186 return 'url'; 00187 } 00188 00196 public function fetchFile( $httpOptions = array() ) { 00197 if ( !Http::isValidURI( $this->mUrl ) ) { 00198 return Status::newFatal( 'http-invalid-url' ); 00199 } 00200 00201 if ( !self::isAllowedHost( $this->mUrl ) ) { 00202 return Status::newFatal( 'upload-copy-upload-invalid-domain' ); 00203 } 00204 if ( !self::isAllowedUrl( $this->mUrl ) ) { 00205 return Status::newFatal( 'upload-copy-upload-invalid-url' ); 00206 } 00207 if ( !$this->mAsync ) { 00208 return $this->reallyFetchFile( $httpOptions ); 00209 } 00210 00211 return Status::newGood(); 00212 } 00213 00219 protected function makeTemporaryFile() { 00220 $tmpFile = TempFSFile::factory( 'URL' ); 00221 $tmpFile->bind( $this ); 00222 00223 return $tmpFile->getPath(); 00224 } 00225 00233 public function saveTempFileChunk( $req, $buffer ) { 00234 $nbytes = fwrite( $this->mTmpHandle, $buffer ); 00235 00236 if ( $nbytes == strlen( $buffer ) ) { 00237 $this->mFileSize += $nbytes; 00238 } else { 00239 // Well... that's not good! 00240 fclose( $this->mTmpHandle ); 00241 $this->mTmpHandle = false; 00242 } 00243 00244 return $nbytes; 00245 } 00246 00254 protected function reallyFetchFile( $httpOptions = array() ) { 00255 global $wgCopyUploadProxy, $wgCopyUploadTimeout; 00256 if ( $this->mTempPath === false ) { 00257 return Status::newFatal( 'tmp-create-error' ); 00258 } 00259 00260 // Note the temporary file should already be created by makeTemporaryFile() 00261 $this->mTmpHandle = fopen( $this->mTempPath, 'wb' ); 00262 if ( !$this->mTmpHandle ) { 00263 return Status::newFatal( 'tmp-create-error' ); 00264 } 00265 00266 $this->mRemoveTempFile = true; 00267 $this->mFileSize = 0; 00268 00269 $options = $httpOptions + array( 'followRedirects' => true ); 00270 00271 if ( $wgCopyUploadProxy !== false ) { 00272 $options['proxy'] = $wgCopyUploadProxy; 00273 } 00274 00275 if ( $wgCopyUploadTimeout && !isset( $options['timeout'] ) ) { 00276 $options['timeout'] = $wgCopyUploadTimeout; 00277 } 00278 $req = MWHttpRequest::factory( $this->mUrl, $options ); 00279 $req->setCallback( array( $this, 'saveTempFileChunk' ) ); 00280 $status = $req->execute(); 00281 00282 if ( $this->mTmpHandle ) { 00283 // File got written ok... 00284 fclose( $this->mTmpHandle ); 00285 $this->mTmpHandle = null; 00286 } else { 00287 // We encountered a write error during the download... 00288 return Status::newFatal( 'tmp-write-error' ); 00289 } 00290 00291 if ( !$status->isOk() ) { 00292 return $status; 00293 } 00294 00295 return $status; 00296 } 00297 00303 public function verifyUpload() { 00304 if ( $this->mAsync ) { 00305 return array( 'status' => UploadBase::OK ); 00306 } 00307 00308 return parent::verifyUpload(); 00309 } 00310 00316 public function checkWarnings() { 00317 if ( $this->mAsync ) { 00318 $this->mIgnoreWarnings = false; 00319 00320 return array(); 00321 } 00322 00323 return parent::checkWarnings(); 00324 } 00325 00332 public function verifyTitlePermissions( $user ) { 00333 if ( $this->mAsync ) { 00334 return true; 00335 } 00336 00337 return parent::verifyTitlePermissions( $user ); 00338 } 00339 00349 public function performUpload( $comment, $pageText, $watch, $user ) { 00350 if ( $this->mAsync ) { 00351 $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user ); 00352 00353 return Status::newFatal( 'async', $sessionKey ); 00354 } 00355 00356 return parent::performUpload( $comment, $pageText, $watch, $user ); 00357 } 00358 00366 protected function insertJob( $comment, $pageText, $watch, $user ) { 00367 $sessionKey = $this->stashSession(); 00368 $job = new UploadFromUrlJob( $this->getTitle(), array( 00369 'url' => $this->mUrl, 00370 'comment' => $comment, 00371 'pageText' => $pageText, 00372 'watch' => $watch, 00373 'userName' => $user->getName(), 00374 'leaveMessage' => $this->mAsync == 'async-leavemessage', 00375 'ignoreWarnings' => $this->mIgnoreWarnings, 00376 'sessionId' => session_id(), 00377 'sessionKey' => $sessionKey, 00378 ) ); 00379 $job->initializeSessionData(); 00380 JobQueueGroup::singleton()->push( $job ); 00381 00382 return $sessionKey; 00383 } 00384 }