getOutput()->disable(); if ( wfReadOnly() ) { wfHttpError( 423, 'Locked', 'Wiki is in read-only mode.' ); return; } // Validate request method if ( !$this->getRequest()->wasPosted() ) { wfHttpError( 400, 'Bad Request', 'Request must be POSTed.' ); return; } // Validate request parameters $optional = [ 'maxjobs' => 0, 'maxtime' => 30, 'type' => false, 'async' => true ]; $required = array_flip( [ 'title', 'tasks', 'signature', 'sigexpiry' ] ); $params = array_intersect_key( $this->getRequest()->getValues(), $required + $optional ); $missing = array_diff_key( $required, $params ); if ( count( $missing ) ) { wfHttpError( 400, 'Bad Request', 'Missing parameters: ' . implode( ', ', array_keys( $missing ) ) ); return; } // Validate request signature $squery = $params; unset( $squery['signature'] ); $correctSignature = self::getQuerySignature( $squery, $this->getConfig()->get( 'SecretKey' ) ); $providedSignature = $params['signature']; $verified = is_string( $providedSignature ) && hash_equals( $correctSignature, $providedSignature ); if ( !$verified || $params['sigexpiry'] < time() ) { wfHttpError( 400, 'Bad Request', 'Invalid or stale signature provided.' ); return; } // Apply any default parameter values $params += $optional; if ( $params['async'] ) { // HTTP 202 Accepted HttpStatus::header( 202 ); // Clients are meant to disconnect without waiting for the full response. // Let the page output happen before the jobs start, so that clients know it's // safe to disconnect. MediaWiki::preOutputCommit() calls ignore_user_abort() // or similar to make sure we stay alive to run the deferred update. DeferredUpdates::addUpdate( new TransactionRoundDefiningUpdate( function () use ( $params ) { $this->doRun( $params ); }, __METHOD__ ), DeferredUpdates::POSTSEND ); } else { $this->doRun( $params ); print "Done\n"; } } protected function doRun( array $params ) { $runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) ); $runner->run( [ 'type' => $params['type'], 'maxJobs' => $params['maxjobs'] ?: 1, 'maxTime' => $params['maxtime'] ?: 30 ] ); } /** * @param array $query * @param string $secretKey * @return string */ public static function getQuerySignature( array $query, $secretKey ) { ksort( $query ); // stable order return hash_hmac( 'sha1', wfArrayToCgi( $query ), $secretKey ); } }