MediaWiki
REL1_23
|
00001 <?php 00030 class SpecialRunJobs extends UnlistedSpecialPage { 00031 public function __construct() { 00032 parent::__construct( 'RunJobs' ); 00033 } 00034 00035 public function execute( $par = '' ) { 00036 $this->getOutput()->disable(); 00037 00038 if ( wfReadOnly() ) { 00039 header( "HTTP/1.0 423 Locked" ); 00040 print 'Wiki is in read-only mode'; 00041 00042 return; 00043 } elseif ( !$this->getRequest()->wasPosted() ) { 00044 header( "HTTP/1.0 400 Bad Request" ); 00045 print 'Request must be POSTed'; 00046 00047 return; 00048 } 00049 00050 $optional = array( 'maxjobs' => 0 ); 00051 $required = array_flip( array( 'title', 'tasks', 'signature', 'sigexpiry' ) ); 00052 00053 $params = array_intersect_key( $this->getRequest()->getValues(), $required + $optional ); 00054 $missing = array_diff_key( $required, $params ); 00055 if ( count( $missing ) ) { 00056 header( "HTTP/1.0 400 Bad Request" ); 00057 print 'Missing parameters: ' . implode( ', ', array_keys( $missing ) ); 00058 00059 return; 00060 } 00061 00062 $squery = $params; 00063 unset( $squery['signature'] ); 00064 $cSig = self::getQuerySignature( $squery ); // correct signature 00065 $rSig = $params['signature']; // provided signature 00066 00067 // Constant-time signature verification 00068 // http://www.emerose.com/timing-attacks-explained 00069 // @todo: make a common method for this 00070 if ( !is_string( $rSig ) || strlen( $rSig ) !== strlen( $cSig ) ) { 00071 $verified = false; 00072 } else { 00073 $result = 0; 00074 for ( $i = 0; $i < strlen( $cSig ); $i++ ) { 00075 $result |= ord( $cSig[$i] ) ^ ord( $rSig[$i] ); 00076 } 00077 $verified = ( $result == 0 ); 00078 } 00079 if ( !$verified || $params['sigexpiry'] < time() ) { 00080 header( "HTTP/1.0 400 Bad Request" ); 00081 print 'Invalid or stale signature provided'; 00082 00083 return; 00084 } 00085 00086 // Apply any default parameter values 00087 $params += $optional; 00088 00089 // Client will usually disconnect before checking the response, 00090 // but it needs to know when it is safe to disconnect. Until this 00091 // reaches ignore_user_abort(), it is not safe as the jobs won't run. 00092 ignore_user_abort( true ); // jobs may take a bit of time 00093 header( "HTTP/1.0 202 Accepted" ); 00094 ob_flush(); 00095 flush(); 00096 // Once the client receives this response, it can disconnect 00097 00098 // Do all of the specified tasks... 00099 if ( in_array( 'jobs', explode( '|', $params['tasks'] ) ) ) { 00100 self::executeJobs( (int)$params['maxjobs'] ); 00101 } 00102 } 00103 00108 public static function getQuerySignature( array $query ) { 00109 global $wgSecretKey; 00110 00111 ksort( $query ); // stable order 00112 return hash_hmac( 'sha1', wfArrayToCgi( $query ), $wgSecretKey ); 00113 } 00114 00123 public static function executeJobs( $maxJobs ) { 00124 $n = $maxJobs; // number of jobs to run 00125 if ( $n < 1 ) { 00126 return; 00127 } 00128 try { 00129 $group = JobQueueGroup::singleton(); 00130 $count = $group->executeReadyPeriodicTasks(); 00131 if ( $count > 0 ) { 00132 wfDebugLog( 'jobqueue', "Executed $count periodic queue task(s)." ); 00133 } 00134 00135 do { 00136 $job = $group->pop( JobQueueGroup::TYPE_DEFAULT, JobQueueGroup::USE_CACHE ); 00137 if ( $job ) { 00138 $output = $job->toString() . "\n"; 00139 $t = -microtime( true ); 00140 wfProfileIn( __METHOD__ . '-' . get_class( $job ) ); 00141 $success = $job->run(); 00142 wfProfileOut( __METHOD__ . '-' . get_class( $job ) ); 00143 $group->ack( $job ); // done 00144 $t += microtime( true ); 00145 $t = round( $t * 1000 ); 00146 if ( $success === false ) { 00147 $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n"; 00148 } else { 00149 $output .= "Success, Time: $t ms\n"; 00150 } 00151 wfDebugLog( 'jobqueue', $output ); 00152 } 00153 } while ( --$n && $job ); 00154 } catch ( MWException $e ) { 00155 MWExceptionHandler::rollbackMasterChangesAndLog( $e ); 00156 // We don't want exceptions thrown during job execution to 00157 // be reported to the user since the output is already sent. 00158 // Instead we just log them. 00159 MWExceptionHandler::logException( $e ); 00160 } 00161 } 00162 }