public function run() { $scope = RequestContext::importScopedSession($this->params['session']); $context = RequestContext::getMain(); try { $user = $context->getUser(); if (!$user->isLoggedIn()) { $this->setLastError("Could not load the author user from session."); return false; } if (count($_SESSION) === 0) { // Empty session probably indicates that we didn't associate // with the session correctly. Note that being able to load // the user does not necessarily mean the session was loaded. // Most likely cause by suhosin.session.encrypt = On. $this->setLastError("Error associating with user session. " . "Try setting suhosin.session.encrypt = Off"); return false; } UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Poll', 'stage' => 'publish', 'status' => Status::newGood())); $upload = new UploadFromStash($user); // @todo initialize() causes a GET, ideally we could frontload the antivirus // checks and anything else to the stash stage (which includes concatenation and // the local file is thus already there). That way, instead of GET+PUT, there could // just be a COPY operation from the stash to the public zone. $upload->initialize($this->params['filekey'], $this->params['filename']); // Check if the local file checks out (this is generally a no-op) $verification = $upload->verifyUpload(); if ($verification['status'] !== UploadBase::OK) { $status = Status::newFatal('verification-error'); $status->value = array('verification' => $verification); UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Failure', 'stage' => 'publish', 'status' => $status)); $this->setLastError("Could not verify upload."); return false; } // Upload the stashed file to a permanent location $status = $upload->performUpload($this->params['comment'], $this->params['text'], $this->params['watch'], $user); if (!$status->isGood()) { UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Failure', 'stage' => 'publish', 'status' => $status)); $this->setLastError($status->getWikiText()); return false; } // Build the image info array while we have the local reference handy $apiMain = new ApiMain(); // dummy object (XXX) $imageInfo = $upload->getImageInfo($apiMain->getResult()); // Cleanup any temporary local file $upload->cleanupTempFile(); // Cache the info so the user doesn't have to wait forever to get the final info UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Success', 'stage' => 'publish', 'filename' => $upload->getLocalFile()->getName(), 'imageinfo' => $imageInfo, 'status' => Status::newGood())); } catch (MWException $e) { UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Failure', 'stage' => 'publish', 'status' => Status::newFatal('api-error-publishfailed'))); $this->setLastError(get_class($e) . ": " . $e->getText()); // To prevent potential database referential integrity issues. // See bug 32551. MWExceptionHandler::rollbackMasterChangesAndLog($e); return false; } return true; }
public function run() { $scope = RequestContext::importScopedSession($this->params['session']); $this->addTeardownCallback(function () use(&$scope) { ScopedCallback::consume($scope); // T126450 }); $context = RequestContext::getMain(); $user = $context->getUser(); try { if (!$user->isLoggedIn()) { $this->setLastError("Could not load the author user from session."); return false; } UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Poll', 'stage' => 'publish', 'status' => Status::newGood()]); $upload = new UploadFromStash($user); // @todo initialize() causes a GET, ideally we could frontload the antivirus // checks and anything else to the stash stage (which includes concatenation and // the local file is thus already there). That way, instead of GET+PUT, there could // just be a COPY operation from the stash to the public zone. $upload->initialize($this->params['filekey'], $this->params['filename']); // Check if the local file checks out (this is generally a no-op) $verification = $upload->verifyUpload(); if ($verification['status'] !== UploadBase::OK) { $status = Status::newFatal('verification-error'); $status->value = ['verification' => $verification]; UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Failure', 'stage' => 'publish', 'status' => $status]); $this->setLastError("Could not verify upload."); return false; } // Upload the stashed file to a permanent location $status = $upload->performUpload($this->params['comment'], $this->params['text'], $this->params['watch'], $user, isset($this->params['tags']) ? $this->params['tags'] : []); if (!$status->isGood()) { UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Failure', 'stage' => 'publish', 'status' => $status]); $this->setLastError($status->getWikiText(false, false, 'en')); return false; } // Build the image info array while we have the local reference handy $apiMain = new ApiMain(); // dummy object (XXX) $imageInfo = $upload->getImageInfo($apiMain->getResult()); // Cleanup any temporary local file $upload->cleanupTempFile(); // Cache the info so the user doesn't have to wait forever to get the final info UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Success', 'stage' => 'publish', 'filename' => $upload->getLocalFile()->getName(), 'imageinfo' => $imageInfo, 'status' => Status::newGood()]); } catch (Exception $e) { UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Failure', 'stage' => 'publish', 'status' => Status::newFatal('api-error-publishfailed')]); $this->setLastError(get_class($e) . ": " . $e->getMessage()); // To prevent potential database referential integrity issues. // See bug 32551. MWExceptionHandler::rollbackMasterChangesAndLog($e); return false; } return true; }
public function testValidRequest() { $request = new FauxRequest(array('wpFileKey' => 'foo')); $this->assertFalse(UploadFromStash::isValidRequest($request), 'Check failure on bad wpFileKey'); $request = new FauxRequest(array('wpSessionKey' => 'foo')); $this->assertFalse(UploadFromStash::isValidRequest($request), 'Check failure on bad wpSessionKey'); $request = new FauxRequest(array('wpFileKey' => 'testkey-test.test')); $this->assertTrue(UploadFromStash::isValidRequest($request), 'Check good wpFileKey'); $request = new FauxRequest(array('wpFileKey' => 'testkey-test.test')); $this->assertTrue(UploadFromStash::isValidRequest($request), 'Check good wpSessionKey'); $request = new FauxRequest(array('wpFileKey' => 'testkey-test.test', 'wpSessionKey' => 'foo')); $this->assertTrue(UploadFromStash::isValidRequest($request), 'Check key precedence'); }
/** * Select an upload module and set it to mUpload. Dies on failure. If the * request was a status request and not a true upload, returns false; * otherwise true * * @return bool */ protected function selectUploadModule() { $request = $this->getMain()->getRequest(); // chunk or one and only one of the following parameters is needed if (!$this->mParams['chunk']) { $this->requireOnlyOneParameter($this->mParams, 'filekey', 'file', 'url'); } // Status report for "upload to stash"/"upload from stash" if ($this->mParams['filekey'] && $this->mParams['checkstatus']) { $progress = UploadBase::getSessionStatus($this->getUser(), $this->mParams['filekey']); if (!$progress) { $this->dieUsage('No result in status data', 'missingresult'); } elseif (!$progress['status']->isGood()) { $this->dieUsage($progress['status']->getWikiText(false, false, 'en'), 'stashfailed'); } if (isset($progress['status']->value['verification'])) { $this->checkVerification($progress['status']->value['verification']); } unset($progress['status']); // remove Status object $this->getResult()->addValue(null, $this->getModuleName(), $progress); return false; } // The following modules all require the filename parameter to be set if (is_null($this->mParams['filename'])) { $this->dieUsageMsg(['missingparam', 'filename']); } if ($this->mParams['chunk']) { // 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'], $this->mParams['filekey'], $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'], $request->getUpload('chunk')); } } elseif (isset($this->mParams['filekey'])) { // Upload stashed in a previous request if (!UploadFromStash::isValidKey($this->mParams['filekey'])) { $this->dieUsageMsg('invalid-file-key'); } $this->mUpload = new UploadFromStash($this->getUser()); // This will not download the temp file in initialize() in async mode. // We still have enough information to call checkWarnings() and such. $this->mUpload->initialize($this->mParams['filekey'], $this->mParams['filename'], !$this->mParams['async']); } elseif (isset($this->mParams['file'])) { $this->mUpload = new UploadFromFile(); $this->mUpload->initialize($this->mParams['filename'], $request->getUpload('file')); } elseif (isset($this->mParams['url'])) { // Make sure upload by URL is enabled: if (!UploadFromUrl::isEnabled()) { $this->dieUsageMsg('copyuploaddisabled'); } if (!UploadFromUrl::isAllowedHost($this->mParams['url'])) { $this->dieUsageMsg('copyuploadbaddomain'); } if (!UploadFromUrl::isAllowedUrl($this->mParams['url'])) { $this->dieUsageMsg('copyuploadbadurl'); } $this->mUpload = new UploadFromUrl(); $this->mUpload->initialize($this->mParams['filename'], $this->mParams['url']); } return true; }
/** * This function just appends the generic comment */ function performUpload($comment, $pageText, $watch, $user) { $pageText .= getResourceComment(); return parent::performUpload($comment, $pageText, $watch, $user); }
/** * Select an upload module and set it to mUpload. Dies on failure. If the * request was a status request and not a true upload, returns false; * otherwise true * * @return bool */ protected function selectUploadModule() { $request = $this->getMain()->getRequest(); // chunk or one and only one of the following parameters is needed if (!$this->mParams['chunk']) { $this->requireOnlyOneParameter($this->mParams, 'filekey', 'file', 'url', 'statuskey'); } if ($this->mParams['statuskey']) { $this->checkAsyncDownloadEnabled(); // Status request for an async upload $sessionData = UploadFromUrlJob::getSessionData($this->mParams['statuskey']); if (!isset($sessionData['result'])) { $this->dieUsage('No result in session data', 'missingresult'); } if ($sessionData['result'] == 'Warning') { $sessionData['warnings'] = $this->transformWarnings($sessionData['warnings']); $sessionData['sessionkey'] = $this->mParams['statuskey']; } $this->getResult()->addValue(null, $this->getModuleName(), $sessionData); return false; } // The following modules all require the filename parameter to be set if (is_null($this->mParams['filename'])) { $this->dieUsageMsg(array('missingparam', 'filename')); } if ($this->mParams['chunk']) { // Chunk upload $this->mUpload = new UploadFromChunks(); if (isset($this->mParams['filekey'])) { // handle new chunk $this->mUpload->continueChunks($this->mParams['filename'], $this->mParams['filekey'], $request->getUpload('chunk')); } else { // handle first chunk $this->mUpload->initialize($this->mParams['filename'], $request->getUpload('chunk')); } } elseif (isset($this->mParams['filekey'])) { // Upload stashed in a previous request if (!UploadFromStash::isValidKey($this->mParams['filekey'])) { $this->dieUsageMsg('invalid-file-key'); } $this->mUpload = new UploadFromStash($this->getUser()); $this->mUpload->initialize($this->mParams['filekey'], $this->mParams['filename']); } elseif (isset($this->mParams['file'])) { $this->mUpload = new UploadFromFile(); $this->mUpload->initialize($this->mParams['filename'], $request->getUpload('file')); } elseif (isset($this->mParams['url'])) { // Make sure upload by URL is enabled: if (!UploadFromUrl::isEnabled()) { $this->dieUsageMsg('copyuploaddisabled'); } $async = false; if ($this->mParams['asyncdownload']) { $this->checkAsyncDownloadEnabled(); if ($this->mParams['leavemessage'] && !$this->mParams['ignorewarnings']) { $this->dieUsage('Using leavemessage without ignorewarnings is not supported', 'missing-ignorewarnings'); } if ($this->mParams['leavemessage']) { $async = 'async-leavemessage'; } else { $async = 'async'; } } $this->mUpload = new UploadFromUrl(); $this->mUpload->initialize($this->mParams['filename'], $this->mParams['url'], $async); } return true; }
/** * Select an upload module and set it to mUpload. Dies on failure. If the * request was a status request and not a true upload, returns false; * otherwise true * * @return bool */ protected function selectUploadModule() { $request = $this->getMain()->getRequest(); // chunk or one and only one of the following parameters is needed if ( !$this->mParams['chunk'] ) { $this->requireOnlyOneParameter( $this->mParams, 'filekey', 'file', 'url', 'statuskey' ); } // Status report for "upload to stash"/"upload from stash" if ( $this->mParams['filekey'] && $this->mParams['checkstatus'] ) { $progress = UploadBase::getSessionStatus( $this->mParams['filekey'] ); if ( !$progress ) { $this->dieUsage( 'No result in status data', 'missingresult' ); } elseif ( !$progress['status']->isGood() ) { $this->dieUsage( $progress['status']->getWikiText(), 'stashfailed' ); } if ( isset( $progress['status']->value['verification'] ) ) { $this->checkVerification( $progress['status']->value['verification'] ); } unset( $progress['status'] ); // remove Status object $this->getResult()->addValue( null, $this->getModuleName(), $progress ); return false; } if ( $this->mParams['statuskey'] ) { $this->checkAsyncDownloadEnabled(); // Status request for an async upload $sessionData = UploadFromUrlJob::getSessionData( $this->mParams['statuskey'] ); if ( !isset( $sessionData['result'] ) ) { $this->dieUsage( 'No result in session data', 'missingresult' ); } if ( $sessionData['result'] == 'Warning' ) { $sessionData['warnings'] = $this->transformWarnings( $sessionData['warnings'] ); $sessionData['sessionkey'] = $this->mParams['statuskey']; } $this->getResult()->addValue( null, $this->getModuleName(), $sessionData ); return false; } // The following modules all require the filename parameter to be set if ( is_null( $this->mParams['filename'] ) ) { $this->dieUsageMsg( array( 'missingparam', 'filename' ) ); } if ( $this->mParams['chunk'] ) { // Chunk upload $this->mUpload = new UploadFromChunks(); if ( isset( $this->mParams['filekey'] ) ) { // handle new chunk $this->mUpload->continueChunks( $this->mParams['filename'], $this->mParams['filekey'], $request->getUpload( 'chunk' ) ); } else { // handle first chunk $this->mUpload->initialize( $this->mParams['filename'], $request->getUpload( 'chunk' ) ); } } elseif ( isset( $this->mParams['filekey'] ) ) { // Upload stashed in a previous request if ( !UploadFromStash::isValidKey( $this->mParams['filekey'] ) ) { $this->dieUsageMsg( 'invalid-file-key' ); } $this->mUpload = new UploadFromStash( $this->getUser() ); // This will not download the temp file in initialize() in async mode. // We still have enough information to call checkWarnings() and such. $this->mUpload->initialize( $this->mParams['filekey'], $this->mParams['filename'], !$this->mParams['async'] ); } elseif ( isset( $this->mParams['file'] ) ) { $this->mUpload = new UploadFromFile(); $this->mUpload->initialize( $this->mParams['filename'], $request->getUpload( 'file' ) ); } elseif ( isset( $this->mParams['url'] ) ) { // Make sure upload by URL is enabled: if ( !UploadFromUrl::isEnabled() ) { $this->dieUsageMsg( 'copyuploaddisabled' ); } if ( !UploadFromUrl::isAllowedHost( $this->mParams['url'] ) ) { $this->dieUsageMsg( 'copyuploadbaddomain' ); } if ( !UploadFromUrl::isAllowedUrl( $this->mParams['url'] ) ) { $this->dieUsageMsg( 'copyuploadbadurl' ); } $async = false; if ( $this->mParams['asyncdownload'] ) { $this->checkAsyncDownloadEnabled(); if ( $this->mParams['leavemessage'] && !$this->mParams['ignorewarnings'] ) { $this->dieUsage( 'Using leavemessage without ignorewarnings is not supported', 'missing-ignorewarnings' ); } if ( $this->mParams['leavemessage'] ) { $async = 'async-leavemessage'; } else { $async = 'async'; } } $this->mUpload = new UploadFromUrl; $this->mUpload->initialize( $this->mParams['filename'], $this->mParams['url'], $async ); } return true; }
/** * @dataProvider provideValidRequests */ public function testValidRequestWithValidRequests($request) { $this->assertTrue(UploadFromStash::isValidRequest($request)); }
/** * Select an upload module and set it to mUpload. Dies on failure. If the * request was a status request and not a true upload, returns false; * otherwise true * * @return bool */ protected function selectUploadModule() { $request = $this->getMain()->getRequest(); // chunk or one and only one of the following parameters is needed if (!$this->mParams['chunk']) { $this->requireOnlyOneParameter($this->mParams, 'filekey', 'file', 'url', 'statuskey'); } // The following modules all require the filename parameter to be set if (is_null($this->mParams['filename'])) { $this->dieUsageMsg(array('missingparam', 'filename')); } if ($this->mParams['chunk']) { // Chunk upload $this->mUpload = new UploadFromChunks(); if (isset($this->mParams['filekey'])) { // handle new chunk $this->mUpload->continueChunks($this->mParams['filename'], $this->mParams['filekey'], $request->getUpload('chunk')); } else { // handle first chunk $this->mUpload->initialize($this->mParams['filename'], $request->getUpload('chunk')); } } elseif (isset($this->mParams['filekey'])) { // Upload stashed in a previous request if (!UploadFromStash::isValidKey($this->mParams['filekey'])) { $this->dieUsageMsg('invalid-file-key'); } $this->mUpload = new UploadFromStash($this->getUser()); $this->mUpload->initialize($this->mParams['filekey'], $this->mParams['filename']); } elseif (isset($this->mParams['file'])) { $this->mUpload = new UploadFromFile(); $this->mUpload->initialize($this->mParams['filename'], $request->getUpload('file')); } elseif (isset($this->mParams['url'])) { // Make sure upload by URL is enabled: if (!UploadFromUrl::isEnabled()) { $this->dieUsageMsg('copyuploaddisabled'); } $this->mUpload = new UploadFromUrl(); $this->mUpload->initialize($this->mParams['filename'], $this->mParams['url']); } return true; }
function approveEditById($id) { $dbw = wfGetDB(DB_MASTER); $row = $dbw->selectRow('moderation', array('mod_id AS id', 'mod_timestamp AS timestamp', 'mod_user AS user', 'mod_user_text AS user_text', 'mod_cur_id AS cur_id', 'mod_namespace AS namespace', 'mod_title AS title', 'mod_comment AS comment', 'mod_minor AS minor', 'mod_bot AS bot', 'mod_last_oldid AS last_oldid', 'mod_ip AS ip', 'mod_header_xff AS header_xff', 'mod_header_ua AS header_ua', 'mod_text AS text', 'mod_merged_revid AS merged_revid', 'mod_rejected AS rejected', 'mod_stash_key AS stash_key'), array('mod_id' => $id), __METHOD__); if (!$row) { throw new ModerationError('moderation-edit-not-found'); } if ($row->merged_revid) { throw new ModerationError('moderation-already-merged'); } if ($row->rejected && $row->timestamp < $this->mSpecial->earliestReapprovableTimestamp) { throw new ModerationError('moderation-rejected-long-ago'); } # Prepare everything $title = Title::makeTitle($row->namespace, $row->title); $model = $title->getContentModel(); $user = $row->user ? User::newFromId($row->user) : User::newFromName($row->user_text, false); $flags = EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY; if ($row->bot && $user->isAllowed('bot')) { $flags |= EDIT_FORCE_BOT; } if ($row->minor) { # doEditContent() checks the right $flags |= EDIT_MINOR; } # For CheckUser extension to work properly, IP, XFF and UA # should be set to the correct values for the original user # (not from the moderator) $cuHook = new ModerationCheckUserHook(); $cuHook->install($row->ip, $row->header_xff, $row->header_ua); $approveHook = new ModerationApproveHook(); $approveHook->install(array('rev_timestamp' => $dbw->timestamp($row->timestamp), 'rev_user' => $user->getId(), 'rev_user_text' => $user->getName())); $status = Status::newGood(); if ($row->stash_key) { # This is the upload from stash. $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash($user); try { $file = $stash->getFile($row->stash_key); } catch (MWException $e) { throw new ModerationError('moderation-missing-stashed-image'); } $upload = new UploadFromStash($user, $stash); $upload->initialize($row->stash_key, $title->getText()); $status = $upload->performUpload($row->comment, $row->text, 0, $user); } else { # This is normal edit (not an upload). $new_content = ContentHandler::makeContent($row->text, null, $model); $page = new WikiPage($title); if (!$page->exists()) { # New page $status = $page->doEditContent($new_content, $row->comment, $flags, false, $user); } else { # Existing page $latest = $page->getLatest(); if ($latest == $row->last_oldid) { # Page hasn't changed since this edit was queued for moderation. $status = $page->doEditContent($new_content, $row->comment, $flags, $row->last_oldid, $user); } else { # Page has changed! # Let's attempt merging, as MediaWiki does in private EditPage::mergeChangesIntoContent(). $base_content = $row->last_oldid ? Revision::newFromId($row->last_oldid)->getContent(Revision::RAW) : ContentHandler::makeContent('', null, $model); $latest_content = Revision::newFromId($latest)->getContent(Revision::RAW); $handler = ContentHandler::getForModelID($base_content->getModel()); $merged_content = $handler->merge3($base_content, $new_content, $latest_content); if ($merged_content) { $status = $page->doEditContent($merged_content, $row->comment, $flags, $latest, $user); } else { $dbw = wfGetDB(DB_MASTER); $dbw->update('moderation', array('mod_conflict' => 1), array('mod_id' => $id), __METHOD__); $dbw->commit(__METHOD__); throw new ModerationError('moderation-edit-conflict'); } } } } $approveHook->deinstall(); $cuHook->deinstall(); if (!$status->isGood()) { throw new ModerationError($status->getMessage()); } $logEntry = new ManualLogEntry('moderation', 'approve'); $logEntry->setPerformer($this->moderator); $logEntry->setTarget($title); $logEntry->setParameters(array('revid' => $approveHook->lastRevId)); $logid = $logEntry->insert(); $logEntry->publish($logid); # Approved edits are removed from "moderation" table, # because they already exist in page history, recentchanges etc. $dbw = wfGetDB(DB_MASTER); $dbw->delete('moderation', array('mod_id' => $id), __METHOD__); }