summaryrefslogtreecommitdiff
path: root/includes/api
diff options
context:
space:
mode:
Diffstat (limited to 'includes/api')
-rw-r--r--includes/api/ApiQuerySiteinfo.php1
-rw-r--r--includes/api/ApiStashEdit.php63
-rw-r--r--includes/api/ApiUpload.php88
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',
),