initFromSessionKey( $param['chunksessionkey'], $request ); //set the chunk mode: if( !$this->mSessionKey && !$param['done'] ){ //session key not set init the chunk upload system: $this->chunk_mode = UploadFromChunks::INIT; $this->mDesiredDestName = $param['filename']; }else if( $this->mSessionKey && !$param['done']){ //this is a chunk piece $this->chunk_mode = UploadFromChunks::CHUNK; }else if( $this->mSessionKey && $param['done']){ //this is the last chunk $this->chunk_mode = UploadFromChunks::DONE; } if( $this->chunk_mode == UploadFromChunks::CHUNK || $this->chunk_mode == UploadFromChunks::DONE ){ //set chunk related vars: $this->mTempPath = $request->getFileTempName( 'chunk' ); $this->mFileSize = $request->getFileSize( 'chunk' ); } return $this->status; } static function isValidRequest( $request ) { $sessionData = $request->getSessionData('wsUploadData'); if(! self::isValidSessionKey( $request->getInt( 'wpSessionKey' ), $sessionData) ) return false; //check for the file: return (bool)$request->getFileTempName( 'file' ); } /* check warnings depending on chunk_mode*/ function checkWarnings(){ $warning = array(); return $warning; } function isEmptyFile(){ //does not apply to chunk init if( $this->chunk_mode == UploadFromChunks::INIT ){ return false; }else{ return parent::isEmptyFile(); } } /* Verify whether the upload is sane. * Returns self::OK or else an array with error information */ function verifyUpload( $resultDetails ) { //no checks on chunk upload mode: if( $this->chunk_mode == UploadFromChunks::INIT ) return self::OK; //verify on init and last chunk request if( $this->chunk_mode == UploadFromChunks::CHUNK || $this->chunk_mode == UploadFromChunks::DONE ) return parent::verifyUpload( $resultDetails ); } //only run verifyFile on completed uploaded chunks function verifyFile( $tmpFile ){ if( $this->chunk_mode == UploadFromChunks::DONE ){ //first append last chunk (so we can do a real verifyFile check... (check file type etc) $status = $this->doChunkAppend(); if( $status->isOK() ){ $this->mTempPath = $this->getRealPath( $this->mTempAppendPath ); //verify the completed merged chunks as if it was the file that got uploaded: return parent::verifyFile( $this->mTempPath ) ; }else{ //conflict of status returns (have to return the error ary) ... why we don't consistantly use a status object is beyond me.. return $status->getErrorsArray(); } }else{ return true; } } function getRealPath($srcPath){ $repo = RepoGroup::singleton()->getLocalRepo(); if ( $repo->isVirtualUrl( $srcPath) ) { return $repo->resolveVirtualUrl( $srcPath ); } } //pretty ugly inter-mixing of mParam and local vars function setupChunkSession( $summary, $comment, $watch ) { $this->mSessionKey = $this->getSessionKey(); $_SESSION['wsUploadData'][ $this->mSessionKey ] = array( 'mComment' => $comment, 'mSummary' => $summary, 'mWatch' => $watch, 'mFilteredName' => $this->mFilteredName, 'mTempAppendPath' => null, //the repo append path (not temporary local node mTempPath) 'mDesiredDestName' => $this->mDesiredDestName, 'version' => self::SESSION_VERSION, ); return $this->mSessionKey; } function initFromSessionKey( $sessionKey, $request ){ if( !$sessionKey || empty( $sessionKey ) ){ return false; } $this->mSessionKey = $sessionKey; //load the sessionData array: $sessionData = $request->getSessionData('wsUploadData'); if( isset( $sessionData[$this->mSessionKey]['version'] ) && $sessionData[$this->mSessionKey]['version'] == self::SESSION_VERSION ) { //update the local object from the session // $this->mComment = $sessionData[ $this->mSessionKey ][ 'mComment' ]; $this->mSummary = $sessionData[ $this->mSessionKey ][ 'mSummary' ]; $this->mWatch = $sessionData[ $this->mSessionKey ][ 'mWatch' ]; $this->mIgnorewarnings = $sessionData[ $this->mSessionKey ][ 'mIgnorewarnings' ]; $this->mFilteredName = $sessionData[ $this->mSessionKey ][ 'mFilteredName' ]; $this->mTempAppendPath = $sessionData[ $this->mSessionKey ][ 'mTempAppendPath' ]; $this->mDesiredDestName = $sessionData[ $this->mSessionKey ][ 'mDesiredDestName' ]; }else{ $this->status = Array( 'error'=> 'missing session data'); return false; } } //lets us return an api result (as flow for chunk uploads is kind of different than others. function performUpload($summary='', $comment='', $watch='', $user){ global $wgServer, $wgScriptPath, $wgUser; if( $this->chunk_mode == UploadFromChunks::INIT ){ //firefogg expects a specific result per: //http://www.firefogg.org/dev/chunk_post.html //its oky to return the token here because //a) the user must have requested the token to get here and //b) should only happen over POST //c) (we need the token to validate chunks are coming from a non-xss request) $token = urlencode( $wgUser->editToken() ); ob_clean(); echo ApiFormatJson::getJsonEncode( array( "uploadUrl" => "{$wgServer}{$wgScriptPath}/api.php?action=upload&". "token={$token}&format=json&enablechunks=true&chunksessionkey=". $this->setupChunkSession($summary, $comment, $watch ) ) ); exit(0); }else if( $this->chunk_mode == UploadFromChunks::CHUNK ){ $status = $this->doChunkAppend(); if( $status->isOK() ){ //return success: //firefogg expects a specific result per: //http://www.firefogg.org/dev/chunk_post.html ob_clean(); echo ApiFormatJson::getJsonEncode( array( "result"=>1, "filesize"=> filesize( $this->getRealPath( $this->mTempAppendPath ) ) ) ); exit(0); /*return array( 'result' => 1 );*/ }else{ return $status; } }else if( $this->chunk_mode == UploadFromChunks::DONE ){ //update the values from the local (session init) if not paseed again) if($summary == '') $summary = $this->mSummary; if($comment == '') $comment = $this->mComment; if($watch == '') $watch = $this->mWatch; $status = parent::performUpload($summary, $comment, $watch, $user ); if( !$status->isGood() ) { return $status; } $file = $this->getLocalFile(); //firefogg expects a specific result per: //http://www.firefogg.org/dev/chunk_post.html ob_clean(); echo ApiFormatJson::getJsonEncode( array( "result"=>1, "done"=>1, "resultUrl"=> $file->getDescriptionUrl() ) ); exit(0); } } //append the given chunk to the temporary uploaded file. (if no temporary uploaded file exists created it. function doChunkAppend(){ //if we don't have a mTempAppendPath to generate a file from the chunk packaged var: if( ! $this->mTempAppendPath ){ //die(); //get temp name: //make a chunk store path. (append tmp file to chunk) $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); if( $status->isOK() ) { $this->mTempAppendPath = $status->value; $_SESSION[ 'wsUploadData' ][ $this->mSessionKey ][ 'mTempAppendPath' ] = $this->mTempAppendPath; } return $status; }else{ if( is_file( $this->getRealPath( $this->mTempAppendPath ) ) ){ $status = $this->appendToUploadFile( $this->mTempAppendPath, $this->mTempPath ); }else{ $status->fatal( 'filenotfound', $this->mTempAppendPath ); } return $status; } } }