34 if ( !UploadBase::isEnabled() ) {
42 $request = $this->
getMain()->getRequest();
44 $this->mParams[
'async'] = ( $this->mParams[
'async'] &&
45 $this->
getConfig()->get(
'EnableAsyncUploads' ) );
47 $this->mParams[
'file'] = $request->getFileName(
'file' );
48 $this->mParams[
'chunk'] = $request->getFileName(
'chunk' );
51 if ( !$this->mParams[
'filekey'] && $this->mParams[
'sessionkey'] ) {
52 $this->mParams[
'filekey'] = $this->mParams[
'sessionkey'];
59 } elseif ( !isset( $this->mUpload ) ) {
60 $this->
dieDebug( __METHOD__,
'No upload module set' );
71 $status = $this->mUpload->fetchFile();
77 if ( $this->mParams[
'chunk'] ) {
78 $maxSize = UploadBase::getMaxUploadSize();
79 if ( $this->mParams[
'filesize'] > $maxSize ) {
82 if ( !$this->mUpload->getTitle() ) {
85 } elseif ( $this->mParams[
'async'] && $this->mParams[
'filekey'] ) {
88 wfDebug( __METHOD__ .
" about to verify\n" );
95 if ( !$this->mParams[
'stash'] ) {
96 $permErrors = $this->mUpload->verifyTitlePermissions( $user );
97 if ( $permErrors !==
true ) {
112 if ( $result[
'result'] ===
'Success' ) {
113 $imageinfo = $this->mUpload->getImageInfo( $this->
getResult() );
118 $this->mUpload->cleanupTempFile();
127 if ( $warnings && !$this->mParams[
'ignorewarnings'] ) {
130 } elseif ( $this->mParams[
'chunk'] ) {
133 } elseif ( $this->mParams[
'stash'] ) {
139 if ( UploadBase::isThrottled( $this->
getUser() )
156 $result[
'result'] =
'Success';
157 if ( $warnings && count( $warnings ) > 0 ) {
158 $result[
'warnings'] = $warnings;
174 $result[
'result'] =
'Warning';
175 $result[
'warnings'] = $warnings;
191 if ( $warnings && count( $warnings ) > 0 ) {
192 $result[
'warnings'] = $warnings;
195 $request = $this->
getMain()->getRequest();
196 $chunkPath = $request->getFileTempname(
'chunk' );
197 $chunkSize = $request->getUpload(
'chunk' )->getSize();
198 $totalSoFar = $this->mParams[
'offset'] + $chunkSize;
199 $minChunkSize = $this->
getConfig()->get(
'MinUploadChunkSize' );
202 if ( $totalSoFar > $this->mParams[
'filesize'] ) {
207 if ( $totalSoFar != $this->mParams[
'filesize'] && $chunkSize < $minChunkSize ) {
208 $this->
dieWithError( [
'apierror-chunk-too-small', Message::numParam( $minChunkSize ) ] );
211 if ( $this->mParams[
'offset'] == 0 ) {
214 $filekey = $this->mParams[
'filekey'];
217 $progress = UploadBase::getSessionStatus( $this->
getUser(), $filekey );
220 $this->
dieWithError(
'apierror-stashfailed-nosession',
'stashfailed' );
221 } elseif ( $progress[
'result'] !==
'Continue' || $progress[
'stage'] !==
'uploading' ) {
222 $this->
dieWithError(
'apierror-stashfailed-complete',
'stashfailed' );
225 $status = $this->mUpload->addChunk(
226 $chunkPath, $chunkSize, $this->mParams[
'offset'] );
229 'offset' => $this->mUpload->getOffset(),
237 if ( $totalSoFar == $this->mParams[
'filesize'] ) {
238 if ( $this->mParams[
'async'] ) {
239 UploadBase::setSessionStatus(
242 [
'result' =>
'Poll',
248 'filename' => $this->mParams[
'filename'],
249 'filekey' => $filekey,
253 $result[
'result'] =
'Poll';
254 $result[
'stage'] =
'queued';
256 $status = $this->mUpload->concatenateChunks();
258 UploadBase::setSessionStatus(
261 [
'result' =>
'Failure',
'stage' =>
'assembling',
'status' =>
$status ]
269 $result[
'warnings'] = $warnings;
274 UploadBase::setSessionStatus( $this->
getUser(), $filekey,
false );
275 $this->mUpload->stash->removeFile( $filekey );
276 $filekey = $this->mUpload->getStashFile()->getFileKey();
278 $result[
'result'] =
'Success';
281 UploadBase::setSessionStatus(
285 'result' =>
'Continue',
286 'stage' =>
'uploading',
287 'offset' => $totalSoFar,
291 $result[
'result'] =
'Continue';
292 $result[
'offset'] = $totalSoFar;
295 $result[
'filekey'] = $filekey;
313 $isPartial = (bool)$this->mParams[
'chunk'];
315 $status = $this->mUpload->tryStashFile( $this->
getUser(), $isPartial );
321 }
catch ( Exception $e ) {
322 $debugMessage =
'Stashing temporary file failed: ' . get_class( $e ) .
' ' . $e->getMessage();
323 wfDebug( __METHOD__ .
' ' . $debugMessage .
"\n" );
325 $e, [
'wrap' =>
new ApiMessage(
'apierror-stashexception',
'stashfailed' ) ]
330 $stashFile =
$status->getValue();
331 $data[
'filekey'] = $stashFile->getFileKey();
333 $data[
'sessionkey'] = $data[
'filekey'];
334 return $data[
'filekey'];
337 if (
$status->getMessage()->getKey() ===
'uploadstash-exception' ) {
340 list( $exceptionType, $message ) =
$status->getMessage()->getParams();
341 $debugMessage =
'Stashing temporary file failed: ' . $exceptionType .
' ' . $message;
342 wfDebug( __METHOD__ .
' ' . $debugMessage .
"\n" );
346 if ( $failureMode !==
'optional' ) {
367 $data[
'invalidparameter'] = $parameter;
371 foreach ( $errors as $error ) {
373 $msg->setApiData( $msg->getApiData() + $data );
390 foreach (
$status->getErrors() as $error ) {
392 if ( $moreExtraData ) {
393 $msg->setApiData( $msg->getApiData() + $moreExtraData );
408 $request = $this->
getMain()->getRequest();
411 if ( !$this->mParams[
'chunk'] ) {
413 'filekey',
'file',
'url' );
417 if ( $this->mParams[
'filekey'] && $this->mParams[
'checkstatus'] ) {
418 $progress = UploadBase::getSessionStatus( $this->
getUser(), $this->mParams[
'filekey'] );
420 $this->
dieWithError(
'apierror-upload-missingresult',
'missingresult' );
421 } elseif ( !$progress[
'status']->isGood() ) {
424 if ( isset( $progress[
'status']->
value[
'verification'] ) ) {
427 if ( isset( $progress[
'status']->
value[
'warnings'] ) ) {
430 $progress[
'warnings'] = $warnings;
433 unset( $progress[
'status'] );
435 if ( isset( $progress[
'imageinfo'] ) ) {
436 $imageinfo = $progress[
'imageinfo'];
437 unset( $progress[
'imageinfo'] );
451 if ( is_null( $this->mParams[
'filename'] ) ) {
452 $this->
dieWithError( [
'apierror-missingparam',
'filename' ] );
455 if ( $this->mParams[
'chunk'] ) {
458 if ( isset( $this->mParams[
'filekey'] ) ) {
459 if ( $this->mParams[
'offset'] === 0 ) {
460 $this->
dieWithError(
'apierror-upload-filekeynotallowed',
'filekeynotallowed' );
464 $this->mUpload->continueChunks(
465 $this->mParams[
'filename'],
466 $this->mParams[
'filekey'],
467 $request->getUpload(
'chunk' )
470 if ( $this->mParams[
'offset'] !== 0 ) {
471 $this->
dieWithError(
'apierror-upload-filekeyneeded',
'filekeyneeded' );
475 $this->mUpload->initialize(
476 $this->mParams[
'filename'],
477 $request->getUpload(
'chunk' )
480 } elseif ( isset( $this->mParams[
'filekey'] ) ) {
489 $this->mUpload->initialize(
490 $this->mParams[
'filekey'], $this->mParams[
'filename'], !$this->mParams[
'async']
492 } elseif ( isset( $this->mParams[
'file'] ) ) {
496 if ( $this->mParams[
'async'] ) {
497 $this->
dieWithError(
'apierror-cannot-async-upload-file' );
501 $this->mUpload->initialize(
502 $this->mParams[
'filename'],
503 $request->getUpload(
'file' )
505 } elseif ( isset( $this->mParams[
'url'] ) ) {
520 $this->mUpload->
initialize( $this->mParams[
'filename'],
521 $this->mParams[
'url'] );
534 $permission = $this->mUpload->isAllowed( $user );
536 if ( $permission !==
true ) {
537 if ( !$user->isLoggedIn() ) {
538 $this->
dieWithError( [
'apierror-mustbeloggedin', $this->
msg(
'action-upload' ) ] );
545 if ( $user->isBlockedFromUpload() ) {
550 if ( $user->isBlockedGlobally() ) {
559 $verification = $this->mUpload->verifyUpload();
560 if ( $verification[
'status'] === UploadBase::OK ) {
572 switch ( $verification[
'status'] ) {
574 case UploadBase::MIN_LENGTH_PARTNAME:
577 case UploadBase::ILLEGAL_FILENAME:
580 'illegal-filename',
null, [
'filename' => $verification[
'filtered'] ]
584 case UploadBase::FILENAME_TOO_LONG:
587 case UploadBase::FILETYPE_MISSING:
590 case UploadBase::WINDOWS_NONASCII_FILENAME:
595 case UploadBase::EMPTY_FILE:
598 case UploadBase::FILE_TOO_LARGE:
602 case UploadBase::FILETYPE_BADTYPE:
604 'filetype' => $verification[
'finalExt'],
605 'allowed' => array_values( array_unique( $this->
getConfig()->
get(
'FileExtensions' ) ) )
607 $extensions = array_unique( $this->
getConfig()->
get(
'FileExtensions' ) );
609 'filetype-banned-type',
611 Message::listParam( $extensions,
'comma' ),
612 count( $extensions ),
617 if ( isset( $verification[
'blacklistedExt'] ) ) {
618 $msg[1] = Message::listParam( $verification[
'blacklistedExt'],
'comma' );
619 $msg[4] = count( $verification[
'blacklistedExt'] );
620 $extradata[
'blacklisted'] = array_values( $verification[
'blacklistedExt'] );
623 $msg[1] = $verification[
'finalExt'];
627 $this->
dieWithError( $msg,
'filetype-banned', $extradata );
630 case UploadBase::VERIFICATION_ERROR:
633 $details = array_merge( [ $msg->getKey() ], $msg->getParams() );
635 $details = $verification[
'details'];
638 $msg->setApiData( $msg->getApiData() + [
'details' => $details ] );
643 case UploadBase::HOOK_ABORTED:
644 $msg = $verification[
'error'] ===
'' ?
'hookaborted' : $verification[
'error'];
645 $this->
dieWithError( $msg,
'hookaborted', [
'details' => $verification[
'error'] ] );
648 $this->
dieWithError(
'apierror-unknownerror-nocode',
'unknown-error',
649 [
'details' => [
'code' => $verification[
'status'] ] ] );
662 $warnings = UploadBase::makeWarningsSerializable( $this->mUpload->checkWarnings() );
672 if ( isset( $warnings[
'duplicate'] ) ) {
674 foreach ( $warnings[
'duplicate'] as $dupe ) {
675 $dupes[] = $dupe[
'fileName'];
678 $warnings[
'duplicate'] = $dupes;
681 if ( isset( $warnings[
'exists'] ) ) {
682 $warning = $warnings[
'exists'];
683 unset( $warnings[
'exists'] );
684 $localFile = $warning[
'normalizedFile'] ?? $warning[
'file'];
685 $warnings[$warning[
'warning']] = $localFile[
'fileName'];
688 if ( isset( $warnings[
'no-change'] ) ) {
689 $file = $warnings[
'no-change'];
690 unset( $warnings[
'no-change'] );
692 $warnings[
'nochange'] = [
697 if ( isset( $warnings[
'duplicate-version'] ) ) {
699 foreach ( $warnings[
'duplicate-version'] as $dupe ) {
701 'timestamp' =>
wfTimestamp( TS_ISO_8601, $dupe[
'timestamp'] )
704 unset( $warnings[
'duplicate-version'] );
707 $warnings[
'duplicateversions'] = $dupes;
721 switch ( get_class( $e ) ) {
722 case UploadStashFileNotFoundException::class:
723 $wrap =
'apierror-stashedfilenotfound';
725 case UploadStashBadPathException::class:
726 $wrap =
'apierror-stashpathinvalid';
728 case UploadStashFileException::class:
729 $wrap =
'apierror-stashfilestorage';
731 case UploadStashZeroLengthFileException::class:
732 $wrap =
'apierror-stashzerolength';
734 case UploadStashNotLoggedInException::class:
736 [
'apierror-mustbeloggedin', $this->
msg(
'action-upload' ) ],
'stashnotloggedin'
738 case UploadStashWrongOwnerException::class:
739 $wrap =
'apierror-stashwrongowner';
741 case UploadStashNoSuchKeyException::class:
742 $wrap =
'apierror-stashnosuchfilekey';
745 $wrap = [
'uploadstash-exception', get_class( $e ) ];
762 if ( is_null( $this->mParams[
'text'] ) ) {
763 $this->mParams[
'text'] = $this->mParams[
'comment'];
767 $file = $this->mUpload->getLocalFile();
775 $this->mParams[
'watchlist'],
$file->getTitle(),
'watchdefault'
778 if ( !$watch && $this->mParams[
'watchlist'] ==
'preferences' && !
$file->exists() ) {
786 if ( $this->mParams[
'watch'] ) {
790 if ( $this->mParams[
'tags'] ) {
799 if ( $this->mParams[
'async'] ) {
800 $progress = UploadBase::getSessionStatus( $this->
getUser(), $this->mParams[
'filekey'] );
801 if ( $progress && $progress[
'result'] ===
'Poll' ) {
802 $this->
dieWithError(
'apierror-upload-inprogress',
'publishfailed' );
804 UploadBase::setSessionStatus(
806 $this->mParams[
'filekey'],
807 [
'result' =>
'Poll',
'stage' =>
'queued',
'status' =>
Status::newGood() ]
812 'filename' => $this->mParams[
'filename'],
813 'filekey' => $this->mParams[
'filekey'],
814 'comment' => $this->mParams[
'comment'],
815 'tags' => $this->mParams[
'tags'],
816 'text' => $this->mParams[
'text'],
821 $result[
'result'] =
'Poll';
822 $result[
'stage'] =
'queued';
825 $status = $this->mUpload->performUpload( $this->mParams[
'comment'],
826 $this->mParams[
'text'], $watch, $this->
getUser(), $this->mParams[
'tags'] );
831 $result[
'result'] =
'Success';
834 $result[
'filename'] =
$file->getName();
835 if ( $warnings && count( $warnings ) > 0 ) {
836 $result[
'warnings'] = $warnings;
877 'ignorewarnings' =>
false,
902 'checkstatus' =>
false,
914 'action=upload&filename=Wiki.png' .
915 '&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&token=123ABC'
916 =>
'apihelp-upload-example-url',
917 'action=upload&filename=Wiki.png&filekey=filekey&ignorewarnings=1&token=123ABC'
918 =>
'apihelp-upload-example-filekey',
923 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Upload';