diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2011-06-22 11:28:20 +0200 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2011-06-22 11:28:20 +0200 |
commit | 9db190c7e736ec8d063187d4241b59feaf7dc2d1 (patch) | |
tree | 46d1a0dee7febef5c2d57a9f7b972be16a163b3d /includes/upload/UploadFromUrl.php | |
parent | 78677c7bbdcc9739f6c10c75935898a20e1acd9e (diff) |
update to MediaWiki 1.17.0
Diffstat (limited to 'includes/upload/UploadFromUrl.php')
-rw-r--r-- | includes/upload/UploadFromUrl.php | 221 |
1 files changed, 157 insertions, 64 deletions
diff --git a/includes/upload/UploadFromUrl.php b/includes/upload/UploadFromUrl.php index 763dae38..c28fd7da 100644 --- a/includes/upload/UploadFromUrl.php +++ b/includes/upload/UploadFromUrl.php @@ -1,28 +1,30 @@ <?php /** - * @file - * @ingroup upload - * * Implements uploading from a HTTP resource. * + * @file + * @ingroup upload * @author Bryan Tong Minh * @author Michael Dale */ + class UploadFromUrl extends UploadBase { - protected $mTempDownloadPath; + protected $mAsync, $mUrl; + protected $mIgnoreWarnings = true; /** * Checks if the user is allowed to use the upload-by-URL feature. If the * user is allowed, pass on permissions checking to the parent. */ public static function isAllowed( $user ) { - if( !$user->isAllowed( 'upload_by_url' ) ) + if ( !$user->isAllowed( 'upload_by_url' ) ) return 'upload_by_url'; return parent::isAllowed( $user ); } /** * Checks if the upload from URL feature is enabled + * @return bool */ public static function isEnabled() { global $wgAllowCopyUploads; @@ -31,14 +33,22 @@ class UploadFromUrl extends UploadBase { /** * Entry point for API upload + * + * @param $name string + * @param $url string + * @param $async mixed Whether the download should be performed + * asynchronous. False for synchronous, async or async-leavemessage for + * asynchronous download. */ - public function initialize( $name, $url, $na, $nb = false ) { - global $wgTmpDirectory; + public function initialize( $name, $url, $async = false ) { + global $wgAllowAsyncCopyUploads; - $localFile = tempnam( $wgTmpDirectory, 'WEBUPLOAD' ); - $this->initializePathInfo( $name, $localFile, 0, true ); + $this->mUrl = $url; + $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false; - $this->mUrl = trim( $url ); + $tempPath = $this->mAsync ? null : $this->makeTemporaryFile(); + # File size and removeTempFile will be filled in later + $this->initializePathInfo( $name, $tempPath, 0, false ); } /** @@ -47,7 +57,7 @@ class UploadFromUrl extends UploadBase { */ public function initializeFromRequest( &$request ) { $desiredDestName = $request->getText( 'wpDestFile' ); - if( !$desiredDestName ) + if ( !$desiredDestName ) $desiredDestName = $request->getText( 'wpUploadFileURL' ); return $this->initialize( $desiredDestName, @@ -59,79 +69,162 @@ class UploadFromUrl extends UploadBase { /** * @param $request Object: WebRequest object */ - public static function isValidRequest( $request ){ - if( !$request->getVal( 'wpUploadFileURL' ) ) - return false; - // check that is a valid url: - return self::isValidUrl( $request->getVal( 'wpUploadFileURL' ) ); + public static function isValidRequest( $request ) { + global $wgUser; + + $url = $request->getVal( 'wpUploadFileURL' ); + return !empty( $url ) + && Http::isValidURI( $url ) + && $wgUser->isAllowed( 'upload_by_url' ); } - public static function isValidUrl( $url ) { - // Only allow HTTP or FTP for now - return (bool)preg_match( '!^(http://|ftp://)!', $url ); + + public function fetchFile() { + if ( !Http::isValidURI( $this->mUrl ) ) { + return Status::newFatal( 'http-invalid-url' ); + } + + if ( !$this->mAsync ) { + return $this->reallyFetchFile(); + } + return Status::newGood(); + } + /** + * Create a new temporary file in the URL subdirectory of wfTempDir(). + * + * @return string Path to the file + */ + protected function makeTemporaryFile() { + return tempnam( wfTempDir(), 'URL' ); } /** - * Do the real fetching stuff + * Callback: save a chunk of the result of a HTTP request to the temporary file + * + * @param $req mixed + * @param $buffer string + * @return int number of bytes handled */ - function fetchFile() { - if( !self::isValidUrl( $this->mUrl ) ) { - return Status::newFatal( 'upload-proto-error' ); + public function saveTempFileChunk( $req, $buffer ) { + $nbytes = fwrite( $this->mTmpHandle, $buffer ); + + if ( $nbytes == strlen( $buffer ) ) { + $this->mFileSize += $nbytes; + } else { + // Well... that's not good! + fclose( $this->mTmpHandle ); + $this->mTmpHandle = false; } - $res = $this->curlCopy(); - if( $res !== true ) { - return Status::newFatal( $res ); - } - return Status::newGood(); + + return $nbytes; } /** - * Safe copy from URL - * Returns true if there was an error, false otherwise + * Download the file, save it to the temporary file and update the file + * size and set $mRemoveTempFile to true. */ - private function curlCopy() { - global $wgOut; - - # Open temporary file - $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" ); - if( $this->mCurlDestHandle === false ) { - # Could not open temporary file to write in - return 'upload-file-error'; + protected function reallyFetchFile() { + if ( $this->mTempPath === false ) { + return Status::newFatal( 'tmp-create-error' ); } - $ch = curl_init(); - curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0); # Probably not needed, but apparently can work around some bug - curl_setopt( $ch, CURLOPT_TIMEOUT, 10); # 10 seconds timeout - curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512); # 0.5KB per second minimum transfer speed - curl_setopt( $ch, CURLOPT_URL, $this->mUrl); - curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) ); - curl_exec( $ch ); - $error = curl_errno( $ch ); - curl_close( $ch ); + // Note the temporary file should already be created by makeTemporaryFile() + $this->mTmpHandle = fopen( $this->mTempPath, 'wb' ); + if ( !$this->mTmpHandle ) { + return Status::newFatal( 'tmp-create-error' ); + } + + $this->mRemoveTempFile = true; + $this->mFileSize = 0; - fclose( $this->mCurlDestHandle ); - unset( $this->mCurlDestHandle ); + $req = MWHttpRequest::factory( $this->mUrl ); + $req->setCallback( array( $this, 'saveTempFileChunk' ) ); + $status = $req->execute(); - if( $error ) - return "upload-curl-error$errornum"; + if ( $this->mTmpHandle ) { + // File got written ok... + fclose( $this->mTmpHandle ); + $this->mTmpHandle = null; + } else { + // We encountered a write error during the download... + return Status::newFatal( 'tmp-write-error' ); + } + + if ( !$status->isOk() ) { + return $status; + } - return true; + return $status; } /** - * Callback function for CURL-based web transfer - * Write data to file unless we've passed the length limit; - * if so, abort immediately. - * @access private + * Wrapper around the parent function in order to defer verifying the + * upload until the file really has been fetched. */ - function uploadCurlCallback( $ch, $data ) { - global $wgMaxUploadSize; - $length = strlen( $data ); - $this->mFileSize += $length; - if( $this->mFileSize > $wgMaxUploadSize ) { - return 0; + public function verifyUpload() { + if ( $this->mAsync ) { + return array( 'status' => UploadBase::OK ); } - fwrite( $this->mCurlDestHandle, $data ); - return $length; + return parent::verifyUpload(); } + + /** + * Wrapper around the parent function in order to defer checking warnings + * until the file really has been fetched. + */ + public function checkWarnings() { + if ( $this->mAsync ) { + $this->mIgnoreWarnings = false; + return array(); + } + return parent::checkWarnings(); + } + + /** + * Wrapper around the parent function in order to defer checking protection + * until we are sure that the file can actually be uploaded + */ + public function verifyPermissions( $user ) { + if ( $this->mAsync ) { + return true; + } + return parent::verifyPermissions( $user ); + } + + /** + * Wrapper around the parent function in order to defer uploading to the + * job queue for asynchronous uploads + */ + public function performUpload( $comment, $pageText, $watch, $user ) { + if ( $this->mAsync ) { + $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user ); + + $status = new Status; + $status->error( 'async', $sessionKey ); + return $status; + } + + return parent::performUpload( $comment, $pageText, $watch, $user ); + } + + + protected function insertJob( $comment, $pageText, $watch, $user ) { + $sessionKey = $this->stashSession(); + $job = new UploadFromUrlJob( $this->getTitle(), array( + 'url' => $this->mUrl, + 'comment' => $comment, + 'pageText' => $pageText, + 'watch' => $watch, + 'userName' => $user->getName(), + 'leaveMessage' => $this->mAsync == 'async-leavemessage', + 'ignoreWarnings' => $this->mIgnoreWarnings, + 'sessionId' => session_id(), + 'sessionKey' => $sessionKey, + ) ); + $job->initializeSessionData(); + $job->insert(); + return $sessionKey; + } + + } |