diff options
Diffstat (limited to 'includes/api')
-rw-r--r-- | includes/api/ApiQuerySiteinfo.php | 1 | ||||
-rw-r--r-- | includes/api/ApiStashEdit.php | 63 | ||||
-rw-r--r-- | includes/api/ApiUpload.php | 88 |
3 files changed, 116 insertions, 36 deletions
diff --git a/includes/api/ApiQuerySiteinfo.php b/includes/api/ApiQuerySiteinfo.php index b81e993b..b7b10846 100644 --- a/includes/api/ApiQuerySiteinfo.php +++ b/includes/api/ApiQuerySiteinfo.php @@ -245,6 +245,7 @@ class ApiQuerySiteinfo extends ApiQueryBase { $data['misermode'] = (bool)$config->get( 'MiserMode' ); $data['maxuploadsize'] = UploadBase::getMaxUploadSize(); + $data['minuploadchunksize'] = (int)$this->getConfig()->get( 'MinUploadChunkSize' ); $data['thumblimits'] = $config->get( 'ThumbLimits' ); ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' ); diff --git a/includes/api/ApiStashEdit.php b/includes/api/ApiStashEdit.php index c4b717c7..d068e945 100644 --- a/includes/api/ApiStashEdit.php +++ b/includes/api/ApiStashEdit.php @@ -276,36 +276,55 @@ class ApiStashEdit extends ApiBase { } $dbr = wfGetDB( DB_SLAVE ); - // Check that no templates used in the output changed... - $cWhr = array(); // conditions to find changes/creations - $dWhr = array(); // conditions to find deletions + + $templates = array(); // conditions to find changes/creations + $templateUses = 0; // expected existing templates foreach ( $editInfo->output->getTemplateIds() as $ns => $stuff ) { foreach ( $stuff as $dbkey => $revId ) { - $cWhr[] = array( 'page_namespace' => $ns, 'page_title' => $dbkey, - 'page_latest != ' . intval( $revId ) ); - $dWhr[] = array( 'page_namespace' => $ns, 'page_title' => $dbkey ); + $templates[(string)$ns][$dbkey] = (int)$revId; + ++$templateUses; } } - $change = $dbr->selectField( 'page', '1', $dbr->makeList( $cWhr, LIST_OR ), __METHOD__ ); - $n = $dbr->selectField( 'page', 'COUNT(*)', $dbr->makeList( $dWhr, LIST_OR ), __METHOD__ ); - if ( $change || $n != count( $dWhr ) ) { - wfDebugLog( 'StashEdit', "Stale cache for key '$key'; template changed." ); - return false; + // Check that no templates used in the output changed... + if ( count( $templates ) ) { + $res = $dbr->select( + 'page', + array( 'ns' => 'page_namespace', 'dbk' => 'page_title', 'page_latest' ), + $dbr->makeWhereFrom2d( $templates, 'page_namespace', 'page_title' ), + __METHOD__ + ); + $changed = false; + foreach ( $res as $row ) { + $changed = $changed || ( $row->page_latest != $templates[$row->ns][$row->dbk] ); + } + + if ( $changed || $res->numRows() != $templateUses ) { + wfDebugLog( 'StashEdit', "Stale cache for key '$key'; template changed." ); + return false; + } } - // Check that no files used in the output changed... - $cWhr = array(); // conditions to find changes/creations - $dWhr = array(); // conditions to find deletions + $files = array(); // conditions to find changes/creations foreach ( $editInfo->output->getFileSearchOptions() as $name => $options ) { - $cWhr[] = array( 'img_name' => $dbkey, - 'img_sha1 != ' . $dbr->addQuotes( strval( $options['sha1'] ) ) ); - $dWhr[] = array( 'img_name' => $dbkey ); + $files[$name] = (string)$options['sha1']; } - $change = $dbr->selectField( 'image', '1', $dbr->makeList( $cWhr, LIST_OR ), __METHOD__ ); - $n = $dbr->selectField( 'image', 'COUNT(*)', $dbr->makeList( $dWhr, LIST_OR ), __METHOD__ ); - if ( $change || $n != count( $dWhr ) ) { - wfDebugLog( 'StashEdit', "Stale cache for key '$key'; file changed." ); - return false; + // Check that no files used in the output changed... + if ( count( $files ) ) { + $res = $dbr->select( + 'image', + array( 'name' => 'img_name', 'img_sha1' ), + array( 'img_name' => array_keys( $files ) ), + __METHOD__ + ); + $changed = false; + foreach ( $res as $row ) { + $changed = $changed || ( $row->img_sha1 != $files[$row->name] ); + } + + if ( $changed || $res->numRows() != count( $files ) ) { + wfDebugLog( 'StashEdit', "Stale cache for key '$key'; file changed." ); + return false; + } } wfDebugLog( 'StashEdit', "Cache hit for key '$key'." ); diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index 74ae05a8..7661625c 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -81,7 +81,7 @@ class ApiUpload extends ApiBase { // Check if the uploaded file is sane if ( $this->mParams['chunk'] ) { - $maxSize = $this->mUpload->getMaxUploadSize(); + $maxSize = UploadBase::getMaxUploadSize(); if ( $this->mParams['filesize'] > $maxSize ) { $this->dieUsage( 'The file you submitted was too large', 'file-too-large' ); } @@ -138,6 +138,12 @@ class ApiUpload extends ApiBase { return $this->getStashResult( $warnings ); } + // Check throttle after we've handled warnings + if ( UploadBase::isThrottled( $this->getUser() ) + ) { + $this->dieUsageMsg( 'actionthrottledtext' ); + } + // This is the most common case -- a normal upload with no warnings // performUpload will return a formatted properly for the API with status return $this->performUpload( $warnings ); @@ -197,13 +203,30 @@ class ApiUpload extends ApiBase { private function getChunkResult( $warnings ) { $result = array(); - $result['result'] = 'Continue'; if ( $warnings && count( $warnings ) > 0 ) { $result['warnings'] = $warnings; } + $request = $this->getMain()->getRequest(); $chunkPath = $request->getFileTempname( 'chunk' ); $chunkSize = $request->getUpload( 'chunk' )->getSize(); + $totalSoFar = $this->mParams['offset'] + $chunkSize; + $minChunkSize = $this->getConfig()->get( 'MinUploadChunkSize' ); + + // Sanity check sizing + if ( $totalSoFar > $this->mParams['filesize'] ) { + $this->dieUsage( + 'Offset plus current chunk is greater than claimed file size', 'invalid-chunk' + ); + } + + // Enforce minimum chunk size + if ( $totalSoFar != $this->mParams['filesize'] && $chunkSize < $minChunkSize ) { + $this->dieUsage( + "Minimum chunk size is $minChunkSize bytes for non-final chunks", 'chunk-too-small' + ); + } + if ( $this->mParams['offset'] == 0 ) { try { $filekey = $this->performStash(); @@ -215,6 +238,18 @@ class ApiUpload extends ApiBase { } } else { $filekey = $this->mParams['filekey']; + + // Don't allow further uploads to an already-completed session + $progress = UploadBase::getSessionStatus( $this->getUser(), $filekey ); + if ( !$progress ) { + // Probably can't get here, but check anyway just in case + $this->dieUsage( 'No chunked upload session with this key', 'stashfailed' ); + } elseif ( $progress['result'] !== 'Continue' || $progress['stage'] !== 'uploading' ) { + $this->dieUsage( + 'Chunked upload is already completed, check status for details', 'stashfailed' + ); + } + $status = $this->mUpload->addChunk( $chunkPath, $chunkSize, $this->mParams['offset'] ); if ( !$status->isGood() ) { @@ -223,18 +258,12 @@ class ApiUpload extends ApiBase { ); $this->dieUsage( $status->getWikiText(), 'stashfailed', 0, $extradata ); - - return array(); } } // Check we added the last chunk: - if ( $this->mParams['offset'] + $chunkSize == $this->mParams['filesize'] ) { + if ( $totalSoFar == $this->mParams['filesize'] ) { if ( $this->mParams['async'] ) { - $progress = UploadBase::getSessionStatus( $this->getUser(), $filekey ); - if ( $progress && $progress['result'] === 'Poll' ) { - $this->dieUsage( "Chunk assembly already in progress.", 'stashfailed' ); - } UploadBase::setSessionStatus( $this->getUser(), $filekey, @@ -254,21 +283,37 @@ class ApiUpload extends ApiBase { } else { $status = $this->mUpload->concatenateChunks(); if ( !$status->isGood() ) { + UploadBase::setSessionStatus( + $this->getUser(), + $filekey, + array( 'result' => 'Failure', 'stage' => 'assembling', 'status' => $status ) + ); $this->dieUsage( $status->getWikiText(), 'stashfailed' ); - - return array(); } // The fully concatenated file has a new filekey. So remove // the old filekey and fetch the new one. + UploadBase::setSessionStatus( $this->getUser(), $filekey, false ); $this->mUpload->stash->removeFile( $filekey ); $filekey = $this->mUpload->getLocalFile()->getFileKey(); $result['result'] = 'Success'; } + } else { + UploadBase::setSessionStatus( + $this->getUser(), + $filekey, + array( + 'result' => 'Continue', + 'stage' => 'uploading', + 'offset' => $totalSoFar, + 'status' => Status::newGood(), + ) + ); + $result['result'] = 'Continue'; + $result['offset'] = $totalSoFar; } $result['filekey'] = $filekey; - $result['offset'] = $this->mParams['offset'] + $chunkSize; return $result; } @@ -378,6 +423,10 @@ class ApiUpload extends ApiBase { // Chunk upload $this->mUpload = new UploadFromChunks(); if ( isset( $this->mParams['filekey'] ) ) { + if ( $this->mParams['offset'] === 0 ) { + $this->dieUsage( 'Cannot supply a filekey when offset is 0', 'badparams' ); + } + // handle new chunk $this->mUpload->continueChunks( $this->mParams['filename'], @@ -385,6 +434,10 @@ class ApiUpload extends ApiBase { $request->getUpload( 'chunk' ) ); } else { + if ( $this->mParams['offset'] !== 0 ) { + $this->dieUsage( 'Must supply a filekey when offset is non-zero', 'badparams' ); + } + // handle first chunk $this->mUpload->initialize( $this->mParams['filename'], @@ -760,8 +813,15 @@ class ApiUpload extends ApiBase { ), 'stash' => false, - 'filesize' => null, - 'offset' => null, + 'filesize' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_MIN => 0, + ApiBase::PARAM_MAX => UploadBase::getMaxUploadSize(), + ), + 'offset' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_MIN => 0, + ), 'chunk' => array( ApiBase::PARAM_TYPE => 'upload', ), |