r40550 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r40549‎ | r40550 | r40551 >
Date:21:26, 6 September 2008
Author:btongminh
Status:old
Tags:
Comment:
Committing a work on progress on improvements to the new upload code. Still needs some work to allow extensions to interact with it and also needs documentation. Modified SpecialUpload.php not committed.
Modified paths:
  • /trunk/phase3/includes/UploadBase.php (modified) (history)
  • /trunk/phase3/includes/UploadFromStash.php (modified) (history)
  • /trunk/phase3/includes/UploadFromUpload.php (modified) (history)
  • /trunk/phase3/includes/UploadFromUrl.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/UploadFromUrl.php
@@ -2,7 +2,7 @@
33
44
55 class UploadFromUrl extends UploadBase {
6 - static function isAllowed( User $user ) {
 6+ static function isAllowed( $user ) {
77 if( !$user->isAllowed( 'upload_by_url' ) )
88 return 'upload_by_url';
99 return parent::isAllowed( $user );
@@ -12,75 +12,62 @@
1313 return $wgAllowCopyUploads && parent::isEnabled();
1414 }
1515
16 - function initialize( $url ) {
 16+ function initialize( $name, $url ) {
1717 global $wgTmpDirectory;
1818 $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' );
 19+ $this-initialize( $name, $local_file, 0, true );
1920
20 - $this->mTempPath = $local_file;
21 - $this->mFileSize = 0; # Will be set by curlCopy
22 - $this->mCurlError = $this->curlCopy( $url, $local_file );
23 - $pathParts = explode( '/', $url );
24 - $this->mSrcName = array_pop( $pathParts );
25 - $this->mSessionKey = false;
26 - $this->mStashed = false;
27 -
28 - // PHP won't auto-cleanup the file
29 - $this->mRemoveTempFile = file_exists( $local_file );
 21+ $this->mUrl = trim( $url );
3022 }
3123
 24+ function verifyUpload() {
 25+ if( stripos($this->mUrl, 'http://') !== 0 && stripos($this->mUrl, 'ftp://') !== 0 ) {
 26+ return array(
 27+ 'status' => self::BEFORE_PROCESSING,
 28+ 'error' => 'upload-proto-error',
 29+ );
 30+ }
 31+ $res = $this->curlCopy();
 32+ if( $res !== true ) {
 33+ return array(
 34+ 'status' => self::BEFORE_PROCESSING,
 35+ 'error' => $res,
 36+ );
 37+ }
 38+ return parent::verifyUpload();
 39+ }
3240
3341 /**
3442 * Safe copy from URL
3543 * Returns true if there was an error, false otherwise
3644 */
37 - private function curlCopy( $url, $dest ) {
 45+ private function curlCopy() {
3846 global $wgUser, $wgOut;
3947
40 - // Bad bad bad!
41 - if( !$wgUser->isAllowed( 'upload_by_url' ) ) {
42 - $wgOut->permissionRequired( 'upload_by_url' );
43 - return true;
44 - }
45 -
46 - # Maybe remove some pasting blanks :-)
47 - $url = trim( $url );
48 - if( stripos($url, 'http://') !== 0 && stripos($url, 'ftp://') !== 0 ) {
49 - # Only HTTP or FTP URLs
50 - $wgOut->showErrorPage( 'upload-proto-error', 'upload-proto-error-text' );
51 - return true;
52 - }
53 -
5448 # Open temporary file
5549 $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" );
5650 if( $this->mCurlDestHandle === false ) {
5751 # Could not open temporary file to write in
58 - $wgOut->showErrorPage( 'upload-file-error', 'upload-file-error-text');
59 - return true;
 52+ return 'upload-file-error';
6053 }
6154
6255 $ch = curl_init();
6356 curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0); # Probably not needed, but apparently can work around some bug
6457 curl_setopt( $ch, CURLOPT_TIMEOUT, 10); # 10 seconds timeout
6558 curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512); # 0.5KB per second minimum transfer speed
66 - curl_setopt( $ch, CURLOPT_URL, $url);
 59+ curl_setopt( $ch, CURLOPT_URL, $this->mUrl);
6760 curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) );
6861 curl_exec( $ch );
69 - $error = curl_errno( $ch ) ? true : false;
70 - $errornum = curl_errno( $ch );
71 - // if ( $error ) print curl_error ( $ch ) ; # Debugging output
 62+ $error = curl_errno( $ch );
7263 curl_close( $ch );
7364
7465 fclose( $this->mCurlDestHandle );
7566 unset( $this->mCurlDestHandle );
76 - if( $error ) {
77 - unlink( $dest );
78 - if( wfEmptyMsg( "upload-curl-error$errornum", wfMsg("upload-curl-error$errornum") ) )
79 - $wgOut->showErrorPage( 'upload-misc-error', 'upload-misc-error-text' );
80 - else
81 - $wgOut->showErrorPage( "upload-curl-error$errornum", "upload-curl-error$errornum-text" );
82 - }
 67+
 68+ if( $error )
 69+ return "upload-curl-error$errornum";
8370
84 - return $error;
 71+ return true;
8572 }
8673
8774 /**
@@ -99,12 +86,4 @@
10087 fwrite( $this->mCurlDestHandle, $data );
10188 return $length;
10289 }
103 -
104 - function execute( &$resultDetails ) {
105 - /* Check for curl error */
106 - if( $this->mCurlError ) {
107 - return self::BEFORE_PROCESSING;
108 - }
109 - return parent::execute( $resultDetails );
110 - }
11190 }
Index: trunk/phase3/includes/UploadFromStash.php
@@ -1,21 +1,47 @@
22 <?php
33
44 class UploadFromStash extends UploadBase {
5 - function initialize( &$sessionData ) {
 5+ static function isValidSessionKey( $key, $sessionData ) {
 6+ return !empty( $key ) &&
 7+ is_array( $sessionData ) &&
 8+ isset( $sessionData[$key] ) &&
 9+ isset( $sessionData[$key]['version'] ) &&
 10+ $sessionData[$key]['version'] == self::SESSION_VERSION
 11+ ;
 12+ }
 13+ static function isValidRequest( $request ) {
 14+ $sessionData = $request->getSessionData('wsUploadData');
 15+ return self::isValidSessionKey(
 16+ $request->getInt( 'wpSessionKey' ),
 17+ $sessionData
 18+ );
 19+ }
 20+
 21+ function initialize( $name, $sessionData ) {
622 /**
723 * Confirming a temporarily stashed upload.
824 * We don't want path names to be forged, so we keep
925 * them in the session on the server and just give
1026 * an opaque key to the user agent.
1127 */
 28+ $this->initialize( $name,
 29+ $sessionData['mTempPath'],
 30+ $sessionData['mFileSize'],
 31+ false
 32+ );
1233
13 - $this->mTempPath = $sessionData['mTempPath'];
14 - $this->mFileSize = $sessionData['mFileSize'];
15 - $this->mSrcName = $sessionData['mSrcName'];
1634 $this->mFileProps = $sessionData['mFileProps'];
17 - $this->mStashed = true;
18 - $this->mRemoveTempFile = false;
1935 }
 36+ function initializeFromRequest( &$request ) {
 37+ $sessionKey = $request->getInt( 'wpSessionKey' );
 38+ $sessionData = $request->getSessionData('wsUploadData');
 39+
 40+ $desiredDestName = $request->getText( 'wpDestFile' );
 41+ if( !$desiredDestName )
 42+ $desiredDestName = $request->getText( 'wpUploadFile' );
 43+
 44+ return $this->initialize( $desiredDestName, $sessionData[$sessionKey] );
 45+ }
2046
2147 /*
2248 * File has been previously verified so no need to do so again.
Index: trunk/phase3/includes/UploadFromUpload.php
@@ -1,12 +1,20 @@
22 <?php
33
44 class UploadFromUpload extends UploadBase {
5 - function initialize( $tempPath, $fileSize, $fileName ) {
6 - $this->mTempPath = $tempPath;
7 - $this->mFileSize = $fileSize;
8 - $this->mSrcName = $fileName;
9 - $this->mSessionKey = false;
10 - $this->mStashed = false;
11 - $this->mRemoveTempFile = false; // PHP will handle this
 5+
 6+ function initializeFromRequest( &$request ) {
 7+ $desiredDestName = $request->getText( 'wpDestFile' );
 8+ if( !$desiredDestName )
 9+ $desiredDestName = $request->getText( 'wpUploadFile' );
 10+
 11+ return $this->initialize(
 12+ $desiredDestName,
 13+ $request->getFileTempName( 'wpUploadFile' ),
 14+ $request->getFileSize( 'wpUploadFile' )
 15+ );
1216 }
 17+
 18+ static function isValidRequest( $request ) {
 19+ return (bool)$request->getFileTempName( 'wpUploadFile' );
 20+ }
1321 }
Index: trunk/phase3/includes/UploadBase.php
@@ -24,38 +24,74 @@
2525
2626 const SESSION_VERSION = 2;
2727
 28+ /*
 29+ * Returns true if uploads are enabled.
 30+ * Can be overriden by subclasses.
 31+ */
2832 static function isEnabled() {
2933 global $wgEnableUploads;
3034 return $wgEnableUploads;
3135 }
32 - static function isAllowed( User $user ) {
 36+ /*
 37+ * Returns true if the user can use this upload module or else a string
 38+ * identifying the missing permission.
 39+ * Can be overriden by subclasses.
 40+ */
 41+ static function isAllowed( $user ) {
3342 if( !$user->isAllowed( 'upload' ) )
3443 return 'upload';
3544 return true;
3645 }
3746
38 - function __construct( $name ) {
39 - $this->mDesiredDestName = $name;
 47+ static $uploadHandlers = array( 'Stash', 'Upload', 'Url' );
 48+ static function createFromRequest( &$request, $type = null ) {
 49+ $type = $type ? $type : $request->getVal( 'wpSourceType' );
 50+ if( !$type )
 51+ return null;
 52+ $type = ucfirst($type);
 53+ $className = 'UploadFrom'.$type;
 54+ if( !in_array( $type, self::$uploadHandlers ) )
 55+ return null;
 56+ if( !call_user_func( array( $className, 'isEnabled' ) ) )
 57+ return null;
 58+ if( !call_user_func( array( $className, 'isValidRequest' ), $request ) )
 59+ return null;
 60+
 61+ $handler = new $className;
 62+ $handler->initializeFromRequest( $request );
 63+ return $handler;
4064 }
4165
 66+ static function isValidRequest( $request ) {
 67+ return false;
 68+ }
4269
43 - function verifyUpload( &$resultDetails ) {
 70+ function __construct() {}
 71+
 72+ function initialize( $name, $tempPath, $fileSize, $removeTempFile = false ) {
 73+ $this->mDesiredDestName = $name;
 74+ $this->mTempPath = $tempPath;
 75+ $this->mFileSize = $fileSize;
 76+ $this->mRemoveTempFile = $removeTempFile;
 77+ }
 78+
 79+ function verifyUpload() {
4480 global $wgUser;
4581
4682 /**
4783 * If there was no filename or a zero size given, give up quick.
4884 */
49 - if( empty( $this->mFileSize ) ) {
50 - return self::EMPTY_FILE;
51 - }
 85+ if( empty( $this->mFileSize ) )
 86+ return array( 'status' => self::EMPTY_FILE );
5287
5388 $nt = $this->getTitle();
5489 if( is_null( $nt ) ) {
 90+ $result = array( 'status' => $this->mTitleError );
5591 if( $this->mTitleError == self::ILLEGAL_FILENAME )
56 - $resultDetails = array( 'filtered' => $this->mFilteredName );
 92+ $resul['filtered'] = $this->mFilteredName;
5793 if ( $this->mTitleError == self::FILETYPE_BADTYPE )
58 - $resultDetails = array( 'finalExt' => $this->mFinalExtension );
59 - return $this->mTitleError;
 94+ $result['finalExt'] = $this->mFinalExtension;
 95+ return $result;
6096 }
6197 $this->mLocalFile = wfLocalFile( $nt );
6298 $this->mDestName = $this->mLocalFile->getName();
@@ -64,30 +100,27 @@
65101 * In some cases we may forbid overwriting of existing files.
66102 */
67103 $overwrite = $this->checkOverwrite( $this->mDestName );
68 - if( $overwrite !== true ) {
69 - $resultDetails = array( 'overwrite' => $overwrite );
70 - return self::OVERWRITE_EXISTING_FILE;
71 - }
 104+ if( $overwrite !== true )
 105+ return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite );
72106
73107 /**
74108 * Look at the contents of the file; if we can recognize the
75109 * type but it's corrupt or data of the wrong type, we should
76110 * probably not accept it.
77111 */
78 - $veri = $this->verifyFile( $this->mTempPath );
 112+ $verification = $this->verifyFile( $this->mTempPath );
79113
80 - if( $veri !== true ) {
81 - if( !is_array( $veri ) )
82 - $veri = array( $veri );
83 - $resultDetails = array( 'veri' => $veri );
84 - return self::VERIFICATION_ERROR;
 114+ if( $verification !== true ) {
 115+ if( !is_array( $verification ) )
 116+ $verification = array( $verification );
 117+ $verification['status'] = self::VERIFICATION_ERROR;
 118+ return $verification;
85119 }
86120
87121 $error = '';
88122 if( !wfRunHooks( 'UploadVerification',
89123 array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
90 - $resultDetails = array( 'error' => $error );
91 - return self::UPLOAD_VERIFICATION_ERROR;
 124+ return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error );
92125 }
93126
94127 return self::OK;
@@ -97,8 +130,7 @@
98131 * Verifies that it's ok to include the uploaded file
99132 *
100133 * @param string $tmpfile the full path of the temporary file to verify
101 - * @param string $extension The filename extension that the file is to be served with
102 - * @return mixed true of the file is verified, a WikiError object otherwise.
 134+ * @return mixed true of the file is verified, a string or array otherwise.
103135 */
104136 protected function verifyFile( $tmpfile ) {
105137 $this->mFileProps = File::getPropsFromPath( $this->mTempPath,
@@ -172,7 +204,6 @@
173205
174206 global $wgCapitalLinks;
175207 if( $this->mDesiredDestName != $filename )
176 - // Use mFilteredName so that we don't have to bother about spaces
177208 $warning['badfilename'] = $filename;
178209
179210 global $wgCheckFileExtensions, $wgFileExtensions;
@@ -319,12 +350,7 @@
320351 global $wgOut;
321352 $repo = RepoGroup::singleton()->getLocalRepo();
322353 $status = $repo->storeTemp( $saveName, $tempName );
323 - if ( !$status->isGood() ) {
324 - $this->showError( $status->getWikiText() );
325 - return false;
326 - } else {
327 - return $status->value;
328 - }
 354+ return $status;
329355 }
330356
331357 /**
@@ -337,18 +363,17 @@
338364 * @access private
339365 */
340366 function stashSession() {
341 - $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
 367+ $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
342368
343 - if( !$stash ) {
 369+ if( !$status->isGood() ) {
344370 # Couldn't save the file.
345371 return false;
346372 }
347373
348374 $key = mt_rand( 0, 0x7fffffff );
349375 $_SESSION['wsUploadData'][$key] = array(
350 - 'mTempPath' => $stash,
 376+ 'mTempPath' => $status->value,
351377 'mFileSize' => $this->mFileSize,
352 - 'mSrcName' => $this->mSrcName,
353378 'mFileProps' => $this->mFileProps,
354379 'version' => self::SESSION_VERSION,
355380 );