Merge r63621 and r63636 from REL1_16:
* Rollback chunked uploading support for 1.16 * Pass $sessionKey as parameter to UploadFromStash::initialize.
This commit is contained in:
parent
60df5636cc
commit
dd349d7b3a
5 changed files with 11 additions and 306 deletions
|
|
@ -236,7 +236,6 @@ $wgAutoloadLocalClasses = array(
|
|||
'UploadFromStash' => 'includes/upload/UploadFromStash.php',
|
||||
'UploadFromFile' => 'includes/upload/UploadFromFile.php',
|
||||
'UploadFromUrl' => 'includes/upload/UploadFromUrl.php',
|
||||
'UploadFromChunks' => 'includes/upload/UploadFromChunks.php',
|
||||
'User' => 'includes/User.php',
|
||||
'UserArray' => 'includes/UserArray.php',
|
||||
'UserArrayFromResult' => 'includes/UserArray.php',
|
||||
|
|
|
|||
|
|
@ -927,8 +927,6 @@ abstract class ApiBase {
|
|||
'invalid-session-key' => array( 'code' => 'invalid-session-key', 'info' => 'Not a valid session key' ),
|
||||
'nouploadmodule' => array( 'code' => 'nouploadmodule', 'info' => 'No upload module set' ),
|
||||
'uploaddisabled' => array( 'code' => 'uploaddisabled', 'info' => 'Uploads are not enabled. Make sure $wgEnableUploads is set to true in LocalSettings.php and the PHP ini setting file_uploads is true' ),
|
||||
'chunked-error' => array( 'code' => 'chunked-error', 'info' => 'There was a problem initializing the chunked upload.' ),
|
||||
'chunk-init-error' => array( 'code' => 'chunk-init-error', 'info' => 'Insufficient information for initialization.' ),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -53,25 +53,9 @@ class ApiUpload extends ApiBase {
|
|||
|
||||
// One and only one of the following parameters is needed
|
||||
$this->requireOnlyOneParameter( $this->mParams,
|
||||
'sessionkey', 'file', 'url', 'enablechunks' );
|
||||
'sessionkey', 'file', 'url' );
|
||||
|
||||
// Initialize $this->mUpload
|
||||
if ( $this->mParams['enablechunks'] ) {
|
||||
$this->mUpload = new UploadFromChunks();
|
||||
|
||||
$this->mUpload->initialize(
|
||||
$request->getVal( 'done', null ),
|
||||
$request->getVal( 'filename', null ),
|
||||
$request->getVal( 'chunksession', null ),
|
||||
$request->getFileTempName( 'chunk' ),
|
||||
$request->getFileSize( 'chunk' ),
|
||||
$request->getSessionData( 'wsUploadData' )
|
||||
);
|
||||
|
||||
if ( !$this->mUpload->status->isOK() ) {
|
||||
$this->dieUsageMsg( $this->mUpload->status->getErrorsArray() );
|
||||
}
|
||||
} elseif ( $this->mParams['sessionkey'] ) {
|
||||
if ( $this->mParams['sessionkey'] ) {
|
||||
/**
|
||||
* Upload stashed in a previous request
|
||||
*/
|
||||
|
|
@ -82,6 +66,7 @@ class ApiUpload extends ApiBase {
|
|||
|
||||
$this->mUpload = new UploadFromStash();
|
||||
$this->mUpload->initialize( $this->mParams['filename'],
|
||||
$this->mParams['sessionkey'],
|
||||
$_SESSION['wsUploadData'][$this->mParams['sessionkey']] );
|
||||
} elseif ( isset( $this->mParams['filename'] ) ) {
|
||||
/**
|
||||
|
|
@ -139,16 +124,7 @@ class ApiUpload extends ApiBase {
|
|||
// Cleanup any temporary mess
|
||||
$this->mUpload->cleanupTempFile();
|
||||
|
||||
if ( isset( $result['chunked-output'] ) ) {
|
||||
foreach ( $result['chunked-output'] as $key => $value ) {
|
||||
if ( $value === null ) {
|
||||
$value = '';
|
||||
}
|
||||
$this->getResult()->addValue( null, $key, $value );
|
||||
}
|
||||
} else {
|
||||
$this->getResult()->addValue( null, $this->getModuleName(), $result );
|
||||
}
|
||||
$this->getResult()->addValue( null, $this->getModuleName(), $result );
|
||||
}
|
||||
|
||||
protected function performUpload() {
|
||||
|
|
@ -252,8 +228,6 @@ class ApiUpload extends ApiBase {
|
|||
$this->getResult()->setIndexedTagName( $result['details'], 'error' );
|
||||
|
||||
$this->dieUsage( 'An internal error occurred', 'internal-error', 0, $error );
|
||||
} elseif ( $this->mParams['enablechunks'] ) {
|
||||
return array( 'chunked-output' => $status->value );
|
||||
}
|
||||
|
||||
$file = $this->mUpload->getLocalFile();
|
||||
|
|
@ -283,10 +257,6 @@ class ApiUpload extends ApiBase {
|
|||
'watch' => false,
|
||||
'ignorewarnings' => false,
|
||||
'file' => null,
|
||||
'enablechunks' => false,
|
||||
'chunksession' => null,
|
||||
'chunk' => null,
|
||||
'done' => false,
|
||||
'url' => null,
|
||||
'sessionkey' => null,
|
||||
);
|
||||
|
|
@ -306,10 +276,6 @@ class ApiUpload extends ApiBase {
|
|||
'watch' => 'Watch the page',
|
||||
'ignorewarnings' => 'Ignore any warnings',
|
||||
'file' => 'File contents',
|
||||
'enablechunks' => 'Set to use chunk mode; see http://firefogg.org/dev/chunk_post.html for protocol',
|
||||
'chunksession' => 'The session key, established on the first contact during the chunked upload',
|
||||
'chunk' => 'The data in this chunk of a chunked upload',
|
||||
'done' => 'Set to 1 on the last chunk of a chunked upload',
|
||||
'url' => 'Url to fetch the file from',
|
||||
'sessionkey' => array(
|
||||
'Session key returned by a previous upload that failed due to warnings',
|
||||
|
|
@ -321,11 +287,10 @@ class ApiUpload extends ApiBase {
|
|||
return array(
|
||||
'Upload a file, or get the status of pending uploads. Several methods are available:',
|
||||
' * Upload file contents directly, using the "file" parameter',
|
||||
' * Upload a file in chunks, using the "enablechunks",',
|
||||
' * Have the MediaWiki server fetch a file from a URL, using the "url" parameter',
|
||||
' * Complete an earlier upload that failed due to warnings, using the "sessionkey" parameter',
|
||||
'Note that the HTTP POST must be done as a file upload (i.e. using multipart/form-data) when',
|
||||
'sending the "file" or "chunk" parameters. Note also that queries using session keys must be',
|
||||
'sending the "file". Note also that queries using session keys must be',
|
||||
'done in the same login session as the query that originally returned the key (i.e. do not',
|
||||
'log out and then log back in). Also you must get and send an edit token before doing any upload stuff.'
|
||||
);
|
||||
|
|
@ -362,8 +327,6 @@ class ApiUpload extends ApiBase {
|
|||
' api.php?action=upload&filename=Wiki.png&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png',
|
||||
'Complete an upload that failed due to warnings:',
|
||||
' api.php?action=upload&filename=Wiki.png&sessionkey=sessionkey&ignorewarnings=1',
|
||||
'Begin a chunked upload:',
|
||||
' api.php?action=upload&filename=Wiki.png&enablechunks=1'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,254 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* @ingroup upload
|
||||
*
|
||||
* First, destination checks are made, and, if ignorewarnings is not
|
||||
* checked, errors / warning is returned.
|
||||
*
|
||||
* 1. We return the uploadUrl.
|
||||
* 2. We then accept chunk uploads from the client.
|
||||
* 3. Return chunk id on each POSTED chunk.
|
||||
* 4. Once the client posts "done=1", the files are concatenated together.
|
||||
*
|
||||
* (More info at: http://firefogg.org/dev/chunk_post.html)
|
||||
*/
|
||||
class UploadFromChunks extends UploadBase {
|
||||
|
||||
const INIT = 1;
|
||||
const CHUNK = 2;
|
||||
const DONE = 3;
|
||||
|
||||
protected $chunkMode; // INIT, CHUNK, DONE
|
||||
protected $sessionKey;
|
||||
protected $comment;
|
||||
protected $repoPath;
|
||||
protected $pageText;
|
||||
protected $watch;
|
||||
|
||||
public $status;
|
||||
|
||||
// Parent class requires this function even though it is only
|
||||
// used from SpecialUpload.php and we don't do chunked uploading
|
||||
// from SpecialUpload -- best to raise an exception for
|
||||
// now.
|
||||
public function initializeFromRequest( &$request ) {
|
||||
throw new MWException( 'not implemented' );
|
||||
}
|
||||
|
||||
public function initialize( $done, $filename, $sessionKey, $path, $fileSize, $sessionData ) {
|
||||
$this->status = Status::newGood();
|
||||
|
||||
$this->initializePathInfo( $filename, $path, 0, true );
|
||||
if ( $sessionKey !== null ) {
|
||||
$this->initFromSessionKey( $sessionKey, $sessionData, $fileSize );
|
||||
|
||||
if ( $done ) {
|
||||
$this->chunkMode = self::DONE;
|
||||
} else {
|
||||
$this->mTempPath = $path;
|
||||
$this->chunkMode = self::CHUNK;
|
||||
}
|
||||
} else {
|
||||
// session key not set, init the chunk upload system:
|
||||
$this->chunkMode = self::INIT;
|
||||
}
|
||||
|
||||
if ( $this->status->isOk()
|
||||
&& ( $this->mDesiredDestName === null || $this->mFileSize === null ) ) {
|
||||
$this->status = Status::newFatal( 'chunk-init-error' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session information for chunked uploads and allocate a unique key.
|
||||
* @param $comment string
|
||||
* @param $pageText string
|
||||
* @param $watch boolean
|
||||
*
|
||||
* @returns string the session key for this chunked upload
|
||||
*/
|
||||
protected function setupChunkSession( $comment, $pageText, $watch ) {
|
||||
if ( !isset( $this->sessionKey ) ) {
|
||||
$this->sessionKey = $this->getSessionKey();
|
||||
}
|
||||
foreach ( array( 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
|
||||
as $key ) {
|
||||
if ( isset( $this->$key ) ) {
|
||||
$_SESSION['wsUploadData'][$this->sessionKey][$key] = $this->$key;
|
||||
}
|
||||
}
|
||||
if ( isset( $comment ) ) {
|
||||
$_SESSION['wsUploadData'][$this->sessionKey]['commment'] = $comment;
|
||||
}
|
||||
if ( isset( $pageText ) ) {
|
||||
$_SESSION['wsUploadData'][$this->sessionKey]['pageText'] = $pageText;
|
||||
}
|
||||
if ( isset( $watch ) ) {
|
||||
$_SESSION['wsUploadData'][$this->sessionKey]['watch'] = $watch;
|
||||
}
|
||||
$_SESSION['wsUploadData'][$this->sessionKey]['version'] = self::SESSION_VERSION;
|
||||
|
||||
return $this->sessionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a continuation of a chunked upload from a session key
|
||||
* @param $sessionKey string
|
||||
* @param $request WebRequest
|
||||
* @param $fileSize int Size of this chunk
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
protected function initFromSessionKey( $sessionKey, $sessionData, $fileSize ) {
|
||||
// testing against null because we don't want to cause obscure
|
||||
// bugs when $sessionKey is full of "0"
|
||||
$this->sessionKey = $sessionKey;
|
||||
|
||||
if ( isset( $sessionData[$this->sessionKey]['version'] )
|
||||
&& $sessionData[$this->sessionKey]['version'] == self::SESSION_VERSION )
|
||||
{
|
||||
foreach ( array( 'comment', 'pageText', 'watch', 'mFilteredName', 'repoPath', 'mFileSize', 'mDesiredDestName' )
|
||||
as $key ) {
|
||||
if ( isset( $sessionData[$this->sessionKey][$key] ) ) {
|
||||
$this->$key = $sessionData[$this->sessionKey][$key];
|
||||
}
|
||||
}
|
||||
|
||||
$this->mFileSize += $fileSize;
|
||||
} else {
|
||||
$this->status = Status::newFatal( 'invalid-session-key' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a chunk of the upload. Overrides the parent method
|
||||
* because Chunked Uploading clients (i.e. Firefogg) require
|
||||
* specific API responses.
|
||||
* @see UploadBase::performUpload
|
||||
*/
|
||||
public function performUpload( $comment, $pageText, $watch, $user ) {
|
||||
wfDebug( "\n\n\performUpload(chunked): comment:" . $comment . ' pageText: ' . $pageText . ' watch:' . $watch );
|
||||
global $wgUser, $wgOut;
|
||||
|
||||
if ( $this->chunkMode == self::INIT ) {
|
||||
// firefogg expects a specific result per:
|
||||
// http://www.firefogg.org/dev/chunk_post.html
|
||||
|
||||
// it's okay to return the token here because
|
||||
// a) the user must have requested the token to get here and
|
||||
// b) should only happen over POST
|
||||
// c) we need the token to validate chunks are coming from a non-xss request
|
||||
return Status::newGood(
|
||||
array( 'uploadUrl' => wfExpandUrl( wfScript( 'api' ) ) . "?" .
|
||||
wfArrayToCGI( array(
|
||||
'action' => 'upload',
|
||||
'token' => $wgUser->editToken(),
|
||||
'format' => 'json',
|
||||
'filename' => $this->mDesiredDestName,
|
||||
'enablechunks' => 'true',
|
||||
'chunksession' =>
|
||||
$this->setupChunkSession( $comment, $pageText, $watch ) ) ) ) );
|
||||
} else if ( $this->chunkMode == self::CHUNK ) {
|
||||
$this->setupChunkSession();
|
||||
$this->appendChunk();
|
||||
if ( !$this->status->isOK() ) {
|
||||
return $this->status;
|
||||
}
|
||||
// return success:
|
||||
// firefogg expects a specific result
|
||||
// http://www.firefogg.org/dev/chunk_post.html
|
||||
return Status::newGood(
|
||||
array( 'result' => 1, 'filesize' => $this->mFileSize )
|
||||
);
|
||||
} else if ( $this->chunkMode == self::DONE ) {
|
||||
$this->finalizeFile();
|
||||
// We ignore the passed-in parameters because these were set on the first contact.
|
||||
$status = parent::performUpload( $this->comment, $this->pageText, $this->watch, $user );
|
||||
|
||||
if ( !$status->isGood() ) {
|
||||
return $status;
|
||||
}
|
||||
$file = $this->getLocalFile();
|
||||
|
||||
// firefogg expects a specific result
|
||||
// http://www.firefogg.org/dev/chunk_post.html
|
||||
return Status::newGood(
|
||||
array( 'result' => 1, 'done' => 1, 'resultUrl' => wfExpandUrl( $file->getDescriptionUrl() ) )
|
||||
);
|
||||
}
|
||||
|
||||
return Status::newGood();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a chunk to the Repo file
|
||||
*
|
||||
* @param string $srcPath Path to file to append from
|
||||
* @param string $toAppendPath Path to file to append to
|
||||
* @return Status Status
|
||||
*/
|
||||
protected function appendToUploadFile( $srcPath, $toAppendPath ) {
|
||||
$repo = RepoGroup::singleton()->getLocalRepo();
|
||||
$status = $repo->append( $srcPath, $toAppendPath );
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a chunk to the temporary file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function appendChunk() {
|
||||
global $wgMaxUploadSize;
|
||||
|
||||
if ( !$this->repoPath ) {
|
||||
$this->status = $this->saveTempUploadedFile( $this->mDesiredDestName, $this->mTempPath );
|
||||
|
||||
if ( $this->status->isOK() ) {
|
||||
$this->repoPath = $this->status->value;
|
||||
$_SESSION['wsUploadData'][$this->sessionKey]['repoPath'] = $this->repoPath;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ( $this->getRealPath( $this->repoPath ) ) {
|
||||
$this->status = $this->appendToUploadFile( $this->repoPath, $this->mTempPath );
|
||||
|
||||
if ( $this->mFileSize > $wgMaxUploadSize )
|
||||
$this->status = Status::newFatal( 'largefileserver' );
|
||||
|
||||
} else {
|
||||
$this->status = Status::newFatal( 'filenotfound', $this->repoPath );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the final chunk and ready file for parent::performUpload()
|
||||
* @return void
|
||||
*/
|
||||
protected function finalizeFile() {
|
||||
$this->appendChunk();
|
||||
$this->mTempPath = $this->getRealPath( $this->repoPath );
|
||||
}
|
||||
|
||||
public function verifyUpload() {
|
||||
if ( $this->chunkMode != self::DONE ) {
|
||||
return array( 'status' => UploadBase::OK );
|
||||
}
|
||||
return parent::verifyUpload();
|
||||
}
|
||||
|
||||
public function checkWarnings() {
|
||||
if ( $this->chunkMode != self::DONE ) {
|
||||
return null;
|
||||
}
|
||||
return parent::checkWarnings();
|
||||
}
|
||||
|
||||
public function getImageInfo( $result ) {
|
||||
if ( $this->chunkMode != self::DONE ) {
|
||||
return null;
|
||||
}
|
||||
return parent::getImageInfo( $result );
|
||||
}
|
||||
}
|
||||
|
|
@ -24,10 +24,8 @@ class UploadFromStash extends UploadBase {
|
|||
$sessionData
|
||||
);
|
||||
}
|
||||
/*
|
||||
* some $na vars for uploadBase method compatibility.
|
||||
*/
|
||||
public function initialize( $name, $sessionData, $na=false, $na2=false ) {
|
||||
|
||||
public function initialize( $name, $sessionKey, $sessionData ) {
|
||||
/**
|
||||
* Confirming a temporarily stashed upload.
|
||||
* We don't want path names to be forged, so we keep
|
||||
|
|
@ -40,19 +38,20 @@ class UploadFromStash extends UploadBase {
|
|||
$sessionData['mFileSize'],
|
||||
false
|
||||
);
|
||||
|
||||
|
||||
$this->mSessionKey = $sessionKey;
|
||||
$this->mVirtualTempPath = $sessionData['mTempPath'];
|
||||
$this->mFileProps = $sessionData['mFileProps'];
|
||||
}
|
||||
|
||||
public function initializeFromRequest( &$request ) {
|
||||
$this->mSessionKey = $request->getInt( 'wpSessionKey' );
|
||||
$sessionKey = $request->getInt( 'wpSessionKey' );
|
||||
$sessionData = $request->getSessionData('wsUploadData');
|
||||
|
||||
$desiredDestName = $request->getText( 'wpDestFile' );
|
||||
if( !$desiredDestName )
|
||||
$desiredDestName = $request->getText( 'wpUploadFile' );
|
||||
return $this->initialize( $desiredDestName, $sessionData[$this->mSessionKey], false );
|
||||
return $this->initialize( $desiredDestName, $sessionKey, $sessionData[$sessionKey] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue