Committing a work on progress on improvements to the new upload code. Still needs...
authorBryan Tong Minh <btongminh@users.mediawiki.org>
Sat, 6 Sep 2008 21:26:46 +0000 (21:26 +0000)
committerBryan Tong Minh <btongminh@users.mediawiki.org>
Sat, 6 Sep 2008 21:26:46 +0000 (21:26 +0000)
includes/UploadBase.php
includes/UploadFromStash.php
includes/UploadFromUpload.php
includes/UploadFromUrl.php

index 58f78cc..665f413 100644 (file)
@@ -24,38 +24,74 @@ class UploadBase {
        
        const SESSION_VERSION = 2;
        
+       /*
+        * Returns true if uploads are enabled.
+        * Can be overriden by subclasses.
+        */
        static function isEnabled() {
                global $wgEnableUploads;
                return $wgEnableUploads;
        }
-       static function isAllowed( User $user ) {
+       /*
+        * Returns true if the user can use this upload module or else a string 
+        * identifying the missing permission.
+        * Can be overriden by subclasses.
+        */
+       static function isAllowed( $user ) {
                if( !$user->isAllowed( 'upload' ) )
                        return 'upload';
                return true;
        }
        
-       function __construct( $name ) {
-               $this->mDesiredDestName = $name;
+       static $uploadHandlers = array( 'Stash', 'Upload', 'Url' );
+       static function createFromRequest( &$request, $type = null ) {
+               $type = $type ? $type : $request->getVal( 'wpSourceType' );
+               if( !$type ) 
+                       return null;
+               $type = ucfirst($type);
+               $className = 'UploadFrom'.$type;
+               if( !in_array( $type, self::$uploadHandlers ) )
+                       return null;
+               if( !call_user_func( array( $className, 'isEnabled' ) ) )
+                       return null;
+               if( !call_user_func( array( $className, 'isValidRequest' ), $request ) )
+                       return null;
+               
+               $handler = new $className;
+               $handler->initializeFromRequest( $request );
+               return $handler;
        }
        
+       static function isValidRequest( $request ) {
+               return false;
+       }
+       
+       function __construct() {}
        
-       function verifyUpload( &$resultDetails ) {
+       function initialize( $name, $tempPath, $fileSize, $removeTempFile = false ) {
+               $this->mDesiredDestName = $name;
+               $this->mTempPath = $tempPath;
+               $this->mFileSize = $fileSize;
+               $this->mRemoveTempFile = $removeTempFile;
+       }
+
+       function verifyUpload() {
                global $wgUser;
                
                /**
                 * If there was no filename or a zero size given, give up quick.
                 */
-               if( empty( $this->mFileSize ) ) {
-                       return self::EMPTY_FILE;
-               }
+               if( empty( $this->mFileSize ) ) 
+                       return array( 'status' => self::EMPTY_FILE );
 
                $nt = $this->getTitle();
                if( is_null( $nt ) ) {
+                       $result = array( 'status' => $this->mTitleError );
                        if( $this->mTitleError == self::ILLEGAL_FILENAME )
-                               $resultDetails = array( 'filtered' => $this->mFilteredName );
+                               $resul['filtered'] = $this->mFilteredName;
                        if ( $this->mTitleError == self::FILETYPE_BADTYPE )
-                               $resultDetails = array( 'finalExt' => $this->mFinalExtension );
-                       return $this->mTitleError;
+                               $result['finalExt'] = $this->mFinalExtension;
+                       return $result;
                }
                $this->mLocalFile = wfLocalFile( $nt );
                $this->mDestName = $this->mLocalFile->getName();
@@ -64,30 +100,27 @@ class UploadBase {
                 * In some cases we may forbid overwriting of existing files.
                 */
                $overwrite = $this->checkOverwrite( $this->mDestName );
-               if( $overwrite !== true ) {
-                       $resultDetails = array( 'overwrite' => $overwrite );
-                       return self::OVERWRITE_EXISTING_FILE;
-               }
+               if( $overwrite !== true )
+                       return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite );
                
                /**
                 * Look at the contents of the file; if we can recognize the
                 * type but it's corrupt or data of the wrong type, we should
                 * probably not accept it.
                 */
-               $veri = $this->verifyFile( $this->mTempPath );
+               $verification = $this->verifyFile( $this->mTempPath );
 
-               if( $veri !== true ) {
-                       if( !is_array( $veri ) ) 
-                               $veri = array( $veri );
-                       $resultDetails = array( 'veri' => $veri );
-                       return self::VERIFICATION_ERROR;
+               if( $verification !== true ) {
+                       if( !is_array( $verification ) ) 
+                               $verification = array( $verification );
+                       $verification['status'] = self::VERIFICATION_ERROR;
+                       return $verification;
                }
                
                $error = '';
                if( !wfRunHooks( 'UploadVerification',
                                array( $this->mDestName, $this->mTempPath, &$error ) ) ) {
-                       $resultDetails = array( 'error' => $error );
-                       return self::UPLOAD_VERIFICATION_ERROR;
+                       return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error );
                }
                
                return self::OK;
@@ -97,8 +130,7 @@ class UploadBase {
         * Verifies that it's ok to include the uploaded file
         *
         * @param string $tmpfile the full path of the temporary file to verify
-        * @param string $extension The filename extension that the file is to be served with
-        * @return mixed true of the file is verified, a WikiError object otherwise.
+        * @return mixed true of the file is verified, a string or array otherwise.
         */
        protected function verifyFile( $tmpfile ) {
                $this->mFileProps = File::getPropsFromPath( $this->mTempPath, 
@@ -172,7 +204,6 @@ class UploadBase {
 
                global $wgCapitalLinks;
                if( $this->mDesiredDestName != $filename )
-                       // Use mFilteredName so that we don't have to bother about spaces
                        $warning['badfilename'] = $filename;
 
                global $wgCheckFileExtensions, $wgFileExtensions;
@@ -319,12 +350,7 @@ class UploadBase {
                global $wgOut;
                $repo = RepoGroup::singleton()->getLocalRepo();
                $status = $repo->storeTemp( $saveName, $tempName );
-               if ( !$status->isGood() ) {
-                       $this->showError( $status->getWikiText() );
-                       return false;
-               } else {
-                       return $status->value;
-               }
+               return $status;
        }
        
        /**
@@ -337,18 +363,17 @@ class UploadBase {
         * @access private
         */
        function stashSession() {
-               $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
+               $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath );
 
-               if( !$stash ) {
+               if( !$status->isGood() ) {
                        # Couldn't save the file.
                        return false;
                }
 
                $key = mt_rand( 0, 0x7fffffff );
                $_SESSION['wsUploadData'][$key] = array(
-                       'mTempPath'       => $stash,
+                       'mTempPath'       => $status->value,
                        'mFileSize'       => $this->mFileSize,
-                       'mSrcName'        => $this->mSrcName,
                        'mFileProps'      => $this->mFileProps,
                        'version'         => self::SESSION_VERSION,
                );
index 34173f3..f6ffcf0 100644 (file)
@@ -1,20 +1,46 @@
 <?php
 
 class UploadFromStash extends UploadBase {
-       function initialize( &$sessionData ) {
+       static function isValidSessionKey( $key, $sessionData ) {
+               return !empty( $key ) && 
+                       is_array( $sessionData ) && 
+                       isset( $sessionData[$key] ) && 
+                       isset( $sessionData[$key]['version'] ) && 
+                       $sessionData[$key]['version'] == self::SESSION_VERSION
+               ;
+       }
+       static function isValidRequest( $request ) {
+               $sessionData = $request->getSessionData('wsUploadData');
+               return self::isValidSessionKey( 
+                       $request->getInt( 'wpSessionKey' ),
+                       $sessionData
+               );
+       }
+       
+       function initialize( $name, $sessionData ) {
                        /**
                         * Confirming a temporarily stashed upload.
                         * We don't want path names to be forged, so we keep
                         * them in the session on the server and just give
                         * an opaque key to the user agent.
                         */
+                       $this->initialize( $name, 
+                               $sessionData['mTempPath'], 
+                               $sessionData['mFileSize'],
+                               false
+                       );
 
-                       $this->mTempPath         = $sessionData['mTempPath'];
-                       $this->mFileSize         = $sessionData['mFileSize'];
-                       $this->mSrcName          = $sessionData['mSrcName'];
                        $this->mFileProps        = $sessionData['mFileProps'];
-                       $this->mStashed          = true;
-                       $this->mRemoveTempFile   = false;
+       }
+       function initializeFromRequest( &$request ) {
+               $sessionKey = $request->getInt( 'wpSessionKey' );
+               $sessionData = $request->getSessionData('wsUploadData');
+               
+               $desiredDestName = $request->getText( 'wpDestFile' );
+               if( !$desiredDestName )
+                       $desiredDestName = $request->getText( 'wpUploadFile' );
+                       
+               return $this->initialize( $desiredDestName, $sessionData[$sessionKey] );
        }
        
        /*
index c6ccd17..1b6762c 100644 (file)
@@ -1,12 +1,20 @@
 <?php
 
 class UploadFromUpload extends UploadBase {
-       function initialize( $tempPath, $fileSize, $fileName ) {
-               $this->mTempPath       = $tempPath;
-               $this->mFileSize       = $fileSize;
-               $this->mSrcName        = $fileName;
-               $this->mSessionKey     = false;
-               $this->mStashed        = false;
-               $this->mRemoveTempFile = false; // PHP will handle this
+
+       function initializeFromRequest( &$request ) {
+               $desiredDestName = $request->getText( 'wpDestFile' );
+               if( !$desiredDestName )
+                       $desiredDestName = $request->getText( 'wpUploadFile' );
+               
+               return $this->initialize( 
+                       $desiredDestName, 
+                       $request->getFileTempName( 'wpUploadFile' ), 
+                       $request->getFileSize( 'wpUploadFile' ) 
+               );
+       }
+       
+       static function isValidRequest( $request ) {
+               return (bool)$request->getFileTempName( 'wpUploadFile' );
        }
 }
index dd9010d..b908032 100644 (file)
@@ -2,7 +2,7 @@
 
 
 class UploadFromUrl extends UploadBase {
-       static function isAllowed( User $user ) {
+       static function isAllowed( $user ) {
                if( !$user->isAllowed( 'upload_by_url' ) )
                        return 'upload_by_url';
                return parent::isAllowed( $user );
@@ -12,75 +12,62 @@ class UploadFromUrl extends UploadBase {
                return $wgAllowCopyUploads && parent::isEnabled();
        }
        
-       function initialize( $url ) {
+       function initialize( $name, $url ) {
                global $wgTmpDirectory;
                $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' );
+               $this-initialize( $name, $local_file, 0, true );
 
-               $this->mTempPath       = $local_file;
-               $this->mFileSize       = 0; # Will be set by curlCopy
-               $this->mCurlError      = $this->curlCopy( $url, $local_file );
-               $pathParts             = explode( '/', $url );
-               $this->mSrcName        = array_pop( $pathParts );
-               $this->mSessionKey     = false;
-               $this->mStashed        = false;
-
-               // PHP won't auto-cleanup the file
-               $this->mRemoveTempFile = file_exists( $local_file );
+               $this->mUrl = trim( $url );
        }
        
+       function verifyUpload() {
+               if( stripos($this->mUrl, 'http://') !== 0 && stripos($this->mUrl, 'ftp://') !== 0 ) {
+                       return array(
+                               'status' => self::BEFORE_PROCESSING,
+                               'error' => 'upload-proto-error',
+                       );
+               }
+               $res = $this->curlCopy();
+               if( $res !== true ) {
+                       return array(
+                               'status' => self::BEFORE_PROCESSING,
+                               'error' => $res,
+                       );
+               }
+               return parent::verifyUpload();
+       }
        
                /**
         * Safe copy from URL
         * Returns true if there was an error, false otherwise
         */
-       private function curlCopy( $url, $dest ) {
+       private function curlCopy() {
                global $wgUser, $wgOut;
 
-               // Bad bad bad!
-               if( !$wgUser->isAllowed( 'upload_by_url' ) ) {
-                       $wgOut->permissionRequired( 'upload_by_url' );
-                       return true;
-               }
-
-               # Maybe remove some pasting blanks :-)
-               $url =  trim( $url );
-               if( stripos($url, 'http://') !== 0 && stripos($url, 'ftp://') !== 0 ) {
-                       # Only HTTP or FTP URLs
-                       $wgOut->showErrorPage( 'upload-proto-error', 'upload-proto-error-text' );
-                       return true;
-               }
-
                # Open temporary file
                $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" );
                if( $this->mCurlDestHandle === false ) {
                        # Could not open temporary file to write in
-                       $wgOut->showErrorPage( 'upload-file-error', 'upload-file-error-text');
-                       return true;
+                       return 'upload-file-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, $url);
+               curl_setopt( $ch, CURLOPT_URL, $this->mUrl);
                curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) );
                curl_exec( $ch );
-               $error = curl_errno( $ch ) ? true : false;
-               $errornum =  curl_errno( $ch );
-               // if ( $error ) print curl_error ( $ch ) ; # Debugging output
+               $error =  curl_errno( $ch );
                curl_close( $ch );
 
                fclose( $this->mCurlDestHandle );
                unset( $this->mCurlDestHandle );
-               if( $error ) {
-                       unlink( $dest );
-                       if( wfEmptyMsg( "upload-curl-error$errornum", wfMsg("upload-curl-error$errornum") ) )
-                               $wgOut->showErrorPage( 'upload-misc-error', 'upload-misc-error-text' );
-                       else
-                               $wgOut->showErrorPage( "upload-curl-error$errornum", "upload-curl-error$errornum-text" );
-               }
+               
+               if( $error ) 
+                       return "upload-curl-error$errornum";
 
-               return $error;
+               return true;
        }
        
        /**
@@ -99,12 +86,4 @@ class UploadFromUrl extends UploadBase {
                fwrite( $this->mCurlDestHandle, $data );
                return $length;
        }
-       
-       function execute( &$resultDetails ) {
-               /* Check for curl error */
-               if( $this->mCurlError ) {
-                       return self::BEFORE_PROCESSING;
-               }
-               return parent::execute( $resultDetails );
-       }
 }