From: Mark A. Hershberger Date: Mon, 22 Feb 2010 02:15:30 +0000 (+0000) Subject: follow up r62231, r61779, r62175 X-Git-Tag: 1.31.0-rc.0~37658 X-Git-Url: http://git.cyclocoop.org/fichier?a=commitdiff_plain;h=c871b75a3e1d7fc7995af2c3d253adb62ab8e9d3;p=lhc%2Fweb%2Fwiklou.git follow up r62231, r61779, r62175 * Fix up messages * For new FileRepo::append(), use flags to determine whether to delete or not * Add more error checking for appending * Fix a couple of places in Revision.php and LogPage.php where DB errors were produced when comment was null * Remove bogus checking for !$comment, etc on the DONE phase of chunked uploading * Don't pretend to return a value when raising an exception * Add more tests for chunked uploads * Verify that Status::getErrorsArray() (at least where it is used in ApiUpload::execute()) returns an array that we can pass to dieUsageMessage() * Ensure that checkWarnings(), etc work only on the complete file --- diff --git a/includes/LogPage.php b/includes/LogPage.php index 229b3d8770..1d8d6c1c30 100644 --- a/includes/LogPage.php +++ b/includes/LogPage.php @@ -378,6 +378,8 @@ class LogPage { $params = array( $params ); } + if ( $comment === null ) $comment = ""; + $this->action = $action; $this->target = $target; $this->comment = $comment; diff --git a/includes/Revision.php b/includes/Revision.php index cbf5625f1d..8d2c7e9d6b 100644 --- a/includes/Revision.php +++ b/includes/Revision.php @@ -835,6 +835,8 @@ class Revision { $this->mTextId = $dbw->insertId(); } + if ( $this->mComment === null ) $this->mComment = ""; + # Record the edit in revisions $rev_id = isset( $this->mId ) ? $this->mId diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index ebf3b25079..25b5690fef 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -921,6 +921,7 @@ abstract class ApiBase { '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.' ), ); /** diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index ed0df7e2fb..7e5c77d0a0 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -68,7 +68,7 @@ class ApiUpload extends ApiBase { ); if ( !$this->mUpload->status->isOK() ) { - return $this->dieUsageMsg( $this->mUpload->status->getErrorsArray() ); + $this->dieUsageMsg( $this->mUpload->status->getErrorsArray() ); } } elseif ( $this->mParams['sessionkey'] ) { /** @@ -76,7 +76,7 @@ class ApiUpload extends ApiBase { */ // Check the session key if ( !isset( $_SESSION['wsUploadData'][$this->mParams['sessionkey']] ) ) - return $this->dieUsageMsg( array( 'invalid-session-key' ) ); + $this->dieUsageMsg( array( 'invalid-session-key' ) ); $this->mUpload = new UploadFromStash(); $this->mUpload->initialize( $this->mParams['filename'], @@ -109,7 +109,7 @@ class ApiUpload extends ApiBase { $status = $this->mUpload->fetchFile(); if ( !$status->isOK() ) { - return $this->dieUsage( $status->getWikiText(), 'fetchfileerror' ); + $this->dieUsage( $status->getWikiText(), 'fetchfileerror' ); } } } else $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); diff --git a/includes/filerepo/FSRepo.php b/includes/filerepo/FSRepo.php index 3696e7e909..0dd9d0f74e 100644 --- a/includes/filerepo/FSRepo.php +++ b/includes/filerepo/FSRepo.php @@ -227,7 +227,7 @@ class FSRepo extends FileRepo { return $status; } - function append( $srcPath, $toAppendPath ) { + function append( $srcPath, $toAppendPath, $flags = 0 ) { $status = $this->newGood(); // Resolve the virtual URL @@ -236,20 +236,30 @@ class FSRepo extends FileRepo { } // Make sure the files are there if ( !is_file( $srcPath ) ) - $status->fatal( 'append-src-filenotfound', $srcPath ); + $status->fatal( 'filenotfound', $srcPath ); if ( !is_file( $toAppendPath ) ) - $status->fatal( 'append-toappend-filenotfound', $toAppendPath ); + $status->fatal( 'filenotfound', $toAppendPath ); + + if ( !$status->isOk() ) return $status; // Do the append - if( file_put_contents( $srcPath, file_get_contents( $toAppendPath ), FILE_APPEND ) ) { - $status->value = $srcPath; - } else { - $status->fatal( 'fileappenderror', $toAppendPath, $srcPath); + $chunk = file_get_contents( $toAppendPath ); + if( $chunk === false ) { + $status->fatal( 'fileappenderrorread', $toAppendPath ); } - // Remove the source file - unlink( $toAppendPath ); + if( $status->isOk() ) { + if ( file_put_contents( $srcPath, $chunk, FILE_APPEND ) ) { + $status->value = $srcPath; + } else { + $status->fatal( 'fileappenderror', $toAppendPath, $srcPath); + } + } + + if ( $flags & self::DELETE_SOURCE ) { + unlink( $toAppendPath ); + } return $status; } diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php index 7ae5db22ba..3cfbbf9e03 100644 --- a/includes/filerepo/FileRepo.php +++ b/includes/filerepo/FileRepo.php @@ -402,9 +402,11 @@ abstract class FileRepo { * Append the contents of the source path to the given file. * @param $srcPath string location of the source file * @param $toAppendPath string path to append to. + * @param $flags Bitfield, may be FileRepo::DELETE_SOURCE to indicate + * that the source file should be deleted if possible * @return mixed Status or false */ - abstract function append( $srcPath, $toAppendPath ); + abstract function append( $srcPath, $toAppendPath, $flags ); /** * Remove a temporary file or mark it for garbage collection diff --git a/includes/filerepo/ForeignAPIRepo.php b/includes/filerepo/ForeignAPIRepo.php index 2e50383622..264cb920f8 100644 --- a/includes/filerepo/ForeignAPIRepo.php +++ b/includes/filerepo/ForeignAPIRepo.php @@ -63,7 +63,7 @@ class ForeignAPIRepo extends FileRepo { function storeTemp( $originalName, $srcPath ) { return false; } - function append( $srcPath, $toAppendPath ){ + function append( $srcPath, $toAppendPath, $flags = 0 ){ return false; } function publishBatch( $triplets, $flags = 0 ) { diff --git a/includes/filerepo/NullRepo.php b/includes/filerepo/NullRepo.php index f5784986e6..2bc61bdec8 100644 --- a/includes/filerepo/NullRepo.php +++ b/includes/filerepo/NullRepo.php @@ -14,7 +14,7 @@ class NullRepo extends FileRepo { function storeTemp( $originalName, $srcPath ) { return false; } - function append( $srcPath, $toAppendPath ){ + function append( $srcPath, $toAppendPath, $flags = 0 ){ return false; } function publishBatch( $triplets, $flags = 0 ) { diff --git a/includes/upload/UploadFromChunks.php b/includes/upload/UploadFromChunks.php index 2feccb4969..4e3e1df8b3 100644 --- a/includes/upload/UploadFromChunks.php +++ b/includes/upload/UploadFromChunks.php @@ -37,23 +37,26 @@ class UploadFromChunks extends UploadBase { } public function initialize( $done, $filename, $sessionKey, $path, $fileSize, $sessionData ) { - global $wgTmpDirectory; - $this->status = new Status; + $this->status = Status::newGood(); - $this->initFromSessionKey( $sessionKey, $sessionData ); + $this->initializePathInfo( $filename, $path, 0, true ); + if ( $sessionKey !== null ) { + $this->initFromSessionKey( $sessionKey, $sessionData, $fileSize ); - if ( !$this->sessionKey && !$done ) { + 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; - $this->initializePathInfo( $filename, $path, 0, true); - } else if ( $this->sessionKey && !$done ) { - $this->chunkMode = self::CHUNK; - } else if ( $this->sessionKey && $done ) { - $this->chunkMode = self::DONE; } - if ( $this->chunkMode == self::CHUNK || $this->chunkMode == self::DONE ) { - $this->mTempPath = $path; - $this->mFileSize += $fileSize; + + if ( $this->status->isOk() + && ( $this->mDesiredDestName === null || $this->mFileSize === null ) ) { + $this->status = Status::newFatal( 'chunk-init-error' ); } } @@ -66,16 +69,26 @@ class UploadFromChunks extends UploadBase { * @returns string the session key for this chunked upload */ protected function setupChunkSession( $comment, $pageText, $watch ) { - $this->sessionKey = $this->getSessionKey(); - $_SESSION['wsUploadData'][$this->sessionKey] = array( - 'comment' => $comment, - 'pageText' => $pageText, - 'watch' => $watch, - 'mFilteredName' => $this->mFilteredName, - 'repoPath' => null, - 'mDesiredDestName' => $this->mDesiredDestName, - 'version' => self::SESSION_VERSION, - ); + 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; } @@ -83,26 +96,26 @@ class UploadFromChunks extends UploadBase { * 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 ) { + protected function initFromSessionKey( $sessionKey, $sessionData, $fileSize ) { // testing against null because we don't want to cause obscure // bugs when $sessionKey is full of "0" - if ( $sessionKey === null ) { - return; - } $this->sessionKey = $sessionKey; if ( isset( $sessionData[$this->sessionKey]['version'] ) && $sessionData[$this->sessionKey]['version'] == self::SESSION_VERSION ) { - $this->comment = $sessionData[$this->sessionKey]['comment']; - $this->pageText = $sessionData[$this->sessionKey]['pageText']; - $this->watch = $sessionData[$this->sessionKey]['watch']; - $this->mFilteredName = $sessionData[$this->sessionKey]['mFilteredName']; - $this->repoPath = $sessionData[$this->sessionKey]['repoPath']; - $this->mDesiredDestName = $sessionData[$this->sessionKey]['mDesiredDestName']; + 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' ); } @@ -127,14 +140,17 @@ class UploadFromChunks extends UploadBase { // 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' => $pageText, - 'enablechunks' => 'true', - 'chunksession' => $this->setupChunkSession( $comment, $pageText, $watch ) ) ) ) ); + 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; @@ -146,16 +162,10 @@ class UploadFromChunks extends UploadBase { array( 'result' => 1, 'filesize' => $this->mFileSize ) ); } else if ( $this->chunkMode == self::DONE ) { - if ( !$comment ) - $comment = $this->comment; + $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 ( !$pageText ) - $pageText = $this->pageText; - - if ( !$watch ) - $watch = $this->watch; - - $status = parent::performUpload( $comment, $pageText, $watch, $user ); if ( !$status->isGood() ) { return $status; } @@ -164,7 +174,7 @@ class UploadFromChunks extends UploadBase { // 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() ) ) + array( 'result' => 1, 'done' => 1, 'resultUrl' => wfExpandUrl( $file->getDescriptionUrl() ) ) ); } @@ -203,16 +213,27 @@ class UploadFromChunks extends UploadBase { } 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 ); } - if ( $this->mFileSize > $wgMaxUploadSize ) - $this->status = Status::newFatal( 'largefileserver' ); + } + + /** + * 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 array( 'status' => UploadBase::OK ); } return parent::verifyUpload(); } diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 93fcaae42c..ea64f7e2df 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -979,6 +979,8 @@ Please report this to an [[Special:ListUsers/sysop|administrator]], making note 'readonly_lag' => 'The database has been automatically locked while the slave database servers catch up to the master', 'internalerror' => 'Internal error', 'internalerror_info' => 'Internal error: $1', +'fileappenderrorread' => 'Could not read "$1" during append.', +'fileappenderror' => 'Could not append "$1" to "$2".', 'filecopyerror' => 'Could not copy file "$1" to "$2".', 'filerenameerror' => 'Could not rename file "$1" to "$2".', 'filedeleteerror' => 'Could not delete file "$1".', diff --git a/maintenance/tests/UploadFromChunksTest.php b/maintenance/tests/UploadFromChunksTest.php index b0decd69cc..51fe9793ce 100644 --- a/maintenance/tests/UploadFromChunksTest.php +++ b/maintenance/tests/UploadFromChunksTest.php @@ -1,27 +1,30 @@ markTestIncomplete("Couldn't open $file!\n"); + $fh = fopen( $file, "w" ); + if ( $fh == false ) { + $this->markTestIncomplete( "Couldn't open $file!\n" ); return; } - fwrite($fh, "123"); - fclose($fh); + fwrite( $fh, $content ); + fclose( $fh ); $_FILES['chunk']['tmp_name'] = $file; $_FILES['chunk']['size'] = 3; @@ -30,133 +33,286 @@ class UploadFromChunksTest extends ApiSetup { } function cleanChunk() { - if(file_exists($_FILES['chunk']['tmp_name'])) - unlink($_FILES['chunk']['tmp_name']); + if ( file_exists( $_FILES['chunk']['tmp_name'] ) ) + unlink( $_FILES['chunk']['tmp_name'] ); } - function doApiRequest($params) { - $session = isset( $_SESSION ) ? $_SESSION : array(); - $req = new FauxRequest($params, true, $session); - $module = new ApiMain($req, true); + function doApiRequest( $params, $data = null ) { + $session = isset( $data[2] ) ? $data[2] : array(); + $_SESSION = $session; + + $req = new FauxRequest( $params, true, $session ); + $module = new ApiMain( $req, true ); $module->execute(); - return $module->getResultData(); + return array( $module->getResultData(), $req, $_SESSION ); } function testGetTitle() { $filename = tempnam( wfTempDir(), "" ); $c = new UploadFromChunks(); - $c->initialize(false, "temp.txt", null, $filename, 0, null); - $this->assertEquals(null, $c->getTitle()); + $c->initialize( false, "temp.txt", null, $filename, 0, null ); + $this->assertEquals( null, $c->getTitle() ); $c = new UploadFromChunks(); - $c->initialize(false, "temp.png", null, $filename, 0, null); - $this->assertEquals(Title::makeTitleSafe(NS_FILE, "Temp.png"), $c->getTitle()); + $c->initialize( false, "temp.png", null, $filename, 0, null ); + $this->assertEquals( Title::makeTitleSafe( NS_FILE, "Temp.png" ), $c->getTitle() ); } function testLogin() { - $data = $this->doApiRequest(array('action' => 'login', - "lgname" => self::$userName, - "lgpassword" => self::$passWord ) ); - $this->assertArrayHasKey("login", $data); - $this->assertArrayHasKey("result", $data['login']); - $this->assertEquals("Success", $data['login']['result']); + $data = $this->doApiRequest( array( + 'action' => 'login', + 'lgname' => self::$userName, + 'lgpassword' => self::$passWord ) ); + $this->assertArrayHasKey( "login", $data[0] ); + $this->assertArrayHasKey( "result", $data[0]['login'] ); + $this->assertEquals( "Success", $data[0]['login']['result'] ); + $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] ); return $data; } - /** * @depends testLogin */ - function testGetEditToken($data) { + function testSetupChunkSession( $data ) { global $wgUser; - $wgUser = User::newFromName(self::$userName); + $wgUser = User::newFromName( self::$userName ); $wgUser->load(); + $data[2]['wsEditToken'] = $data[2]['wsToken']; + $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX; + $exception = false; + + $data = $this->doApiRequest( array( + 'filename' => 'tmp.txt', + 'action' => 'upload', + 'enablechunks' => true, + 'token' => $token ), $data ); + $this->assertArrayHasKey( 'uploadUrl', $data[0] ); + $this->assertRegexp( '/action=upload/', $data[0]['uploadUrl'] ); + $this->assertRegexp( '/enablechunks=true/', $data[0]['uploadUrl'] ); + $this->assertRegexp( '/format=json/', $data[0]['uploadUrl'] ); + $this->assertRegexp( '/chunksession=/', $data[0]['uploadUrl'] ); + $this->assertRegexp( '/token=/', $data[0]['uploadUrl'] ); - $data = $this->doApiRequest(array('action' => 'query', - 'prop' => 'info', - 'intoken' => 'edit')); + return $data; } - function testSetupChunkSession() { - } + /** + * @depends testSetupChunkSession + */ + function testAppendChunkTypeBanned( $data ) { + global $wgUser; + $wgUser = User::newFromName( self::$userName ); + $url = $data[0]['uploadUrl']; + $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) ); - /** - * @expectedException UsageException - */ - function testPerformUploadInitError() { - global $wgUser; - $wgUser = User::newFromId(1); + $size = 0; + for ( $i = 0; $i < 4; $i++ ) { + $this->makeChunk( "123" ); + $size += $_FILES['chunk']['size']; - $req = new FauxRequest( - array('action' => 'upload', - 'enablechunks' => '1', - 'filename' => 'test.png', - )); - $module = new ApiMain($req, true); - $module->execute(); + $data = $this->doApiRequest( $params, $data ); + $this->assertArrayHasKey( "result", $data[0] ); + $this->assertTrue( (bool)$data[0]["result"] ); + + $this->assertArrayHasKey( "filesize", $data[0] ); + $this->assertEquals( $size, $data[0]['filesize'] ); + + $this->cleanChunk(); + } + + $data['param'] = $params; + return $data; } /** * @depends testLogin */ - function testPerformUploadInitSuccess($login) { + function testInvalidSessionKey( $data ) { global $wgUser; + $wgUser = User::newFromName( self::$userName ); + $wgUser->load(); + $data[2]['wsEditToken'] = $data[2]['wsToken']; + $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX; + $exception = false; + + try { + $this->doApiRequest( array( + 'action' => 'upload', + 'enablechunks' => true, + 'token' => $token, + 'chunksession' => 'bogus' ), $data ); + } catch ( UsageException $e ) { + $exception = true; + $this->assertEquals( "Not a valid session key", $e->getMessage() ); + } - $wgUser = User::newFromName(self::$userName); - $token = $wgUser->editToken(); + $this->assertTrue( $exception, "Got exception" ); + } - $data = $this->doApiRequest( - array('action' => 'upload', - 'enablechunks' => '1', - 'filename' => 'test.png', - 'token' => $token, - )); + function testPerformUploadInitError() { + global $wgUser; + $wgUser = User::newFromId( 1 ); - $this->assertArrayHasKey("uploadUrl", $data); + $req = new FauxRequest( + array( + 'action' => 'upload', + 'enablechunks' => 'false', + 'sessionkey' => '1', + 'filename' => 'test.png', + ) ); + $module = new ApiMain( $req, true ); + $gotException = false; + try { + $module->execute(); + } catch ( UsageException $e ) { + $this->assertEquals( "The token parameter must be set", $e->getMessage() ); + $gotException = true; + } - return array('data' => $data, 'session' => $_SESSION, 'token' => $token); + $this->assertTrue( $gotException ); } /** - * @depends testPerformUploadInitSuccess + * @depends testAppendChunkTypeBanned */ - function testAppendChunk($combo) { + function testUploadChunkDoneTypeBanned( $data ) { global $wgUser; - $data = $combo['data']; - $_SESSION = $combo['session']; - $wgUser = User::newFromName(self::$userName); + $wgUser = User::newFromName( self::$userName ); $token = $wgUser->editToken(); + $params = $data['param']; + $params['done'] = 1; - $url = $data['uploadUrl']; - $params = wfCgiToArray(substr($url, strpos($url, "?"))); + $this->makeChunk( "123" ); + + $gotException = false; + try { + $data = $this->doApiRequest( $params, $data ); + } catch ( UsageException $e ) { + $this->assertEquals( "This type of file is banned", + $e->getMessage() ); + $gotException = true; + } + $this->cleanChunk(); + $this->assertTrue( $gotException ); + } + + /** + * @depends testLogin + */ + function testUploadChunkDoneDuplicate( $data ) { + global $wgUser, $wgVerifyMimeType; + + $wgVerifyMimeType = false; + $wgUser = User::newFromName( self::$userName ); + $data[2]['wsEditToken'] = $data[2]['wsToken']; + $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX; + $data = $this->doApiRequest( array( + 'filename' => 'tmp.png', + 'action' => 'upload', + 'enablechunks' => true, + 'token' => $token ), $data ); + + $url = $data[0]['uploadUrl']; + $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) ); + $size = 0; + for ( $i = 0; $i < 4; $i++ ) { + $this->makeChunk( "123" ); + $size += $_FILES['chunk']['size']; + + $data = $this->doApiRequest( $params, $data ); + $this->assertArrayHasKey( "result", $data[0] ); + $this->assertTrue( (bool)$data[0]["result"] ); + + $this->assertArrayHasKey( "filesize", $data[0] ); + $this->assertEquals( $size, $data[0]['filesize'] ); - for($i=0;$i<10;$i++) { - $this->makeChunk(); - $data = $this->doApiRequest($params); $this->cleanChunk(); } - return array('data' => $data, 'session' => $_SESSION, 'token' => $token, 'params' => $params); + $params['done'] = true; + + $this->makeChunk( "123" ); + $data = $this->doApiRequest( $params, $data ); + $this->cleanChunk(); + + $this->assertArrayHasKey( 'upload', $data[0] ); + $this->assertArrayHasKey( 'result', $data[0]['upload'] ); + $this->assertEquals( 'Warning', $data[0]['upload']['result'] ); + + $this->assertArrayHasKey( 'warnings', $data[0]['upload'] ); + $this->assertArrayHasKey( 'exists', + $data[0]['upload']['warnings'] ); + $this->assertEquals( 'Tmp.png', + $data[0]['upload']['warnings']['exists'] ); + } /** - * @depends testAppendChunk + * @depends testLogin */ - function testUploadChunkDone($combo) { - global $wgUser; - $data = $combo['data']; - $params = $combo['params']; - $_SESSION = $combo['session']; - $wgUser = User::newFromName(self::$userName); - $token = $wgUser->editToken(); + function testUploadChunkDoneGood( $data ) { + global $wgUser, $wgVerifyMimeType; + $wgVerifyMimeType = false; - $params['done'] = 1; + $id = Title::newFromText( "Twar.png", NS_FILE )->getArticleID(); + + $oldFile = Article::newFromID( $id ); + if ( $oldFile ) { + $oldFile->doDeleteArticle(); + $oldFile->doPurge(); + } + $oldFile = wfFindFile( "Twar.png" ); + if ( $oldFile ) { + $oldFile->delete(); + } + + $wgUser = User::newFromName( self::$userName ); + $data[2]['wsEditToken'] = $data[2]['wsToken']; + $token = md5( $data[2]['wsToken'] ) . EDIT_TOKEN_SUFFIX; + $data = $this->doApiRequest( array( + 'filename' => 'twar.png', + 'action' => 'upload', + 'enablechunks' => true, + 'token' => $token ), $data ); + + $url = $data[0]['uploadUrl']; + $params = wfCgiToArray( substr( $url, strpos( $url, "?" ) ) ); + $size = 0; + for ( $i = 0; $i < 5; $i++ ) { + $this->makeChunk( "123" ); + $size += $_FILES['chunk']['size']; + + $data = $this->doApiRequest( $params, $data ); + $this->assertArrayHasKey( "result", $data[0] ); + $this->assertTrue( (bool)$data[0]["result"] ); + + $this->assertArrayHasKey( "filesize", $data[0] ); + $this->assertEquals( $size, $data[0]['filesize'] ); + + $this->cleanChunk(); + } + + $params['done'] = true; + + $this->makeChunk( "456" ); + $data = $this->doApiRequest( $params, $data ); - $this->makeChunk(); - $data = $this->doApiRequest($params); $this->cleanChunk(); + + if ( isset( $data[0]['upload'] ) ) { + $this->markTestSkipped( "Please run 'php maintenance/deleteArchivedFiles.php --delete --force' and 'php maintenance/deleteArchivedRevisions.php --delete'" ); + } + + $this->assertArrayHasKey( 'result', $data[0] ); + $this->assertEquals( 1, $data[0]['result'] ); + + $this->assertArrayHasKey( 'done', $data[0] ); + $this->assertEquals( 1, $data[0]['done'] ); + + $this->assertArrayHasKey( 'resultUrl', $data[0] ); + $this->assertRegExp( '/File:Twar.png/', $data[0]['resultUrl'] ); } }