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();
* 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;
* 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,
global $wgCapitalLinks;
if( $this->mDesiredDestName != $filename )
- // Use mFilteredName so that we don't have to bother about spaces
$warning['badfilename'] = $filename;
global $wgCheckFileExtensions, $wgFileExtensions;
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;
}
/**
* @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,
);
<?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] );
}
/*
<?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' );
}
}
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 );
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;
}
/**
fwrite( $this->mCurlDestHandle, $data );
return $length;
}
-
- function execute( &$resultDetails ) {
- /* Check for curl error */
- if( $this->mCurlError ) {
- return self::BEFORE_PROCESSING;
- }
- return parent::execute( $resultDetails );
- }
}