3 * first destination checks are made (if ignorewarnings is not checked) errors / warning is returned.
5 * we return the uploadUrl
6 * we then accept chunk uploads from the client.
7 * return chunk id on each POSTED chunk
8 * once the client posts done=1 concatenated the files together.
9 * more info at: http://firefogg.org/dev/chunk_post.html
11 class UploadFromChunks
extends UploadBase
{
13 var $chunk_mode; //init, chunk, done
14 var $mSessionKey = false;
15 var $status = array();
21 function initializeFromParams( $param , &$request) {
22 $this->initFromSessionKey( $param['chunksessionkey'], $request );
24 if( !$this->mSessionKey
&& !$param['done'] ){
25 //session key not set init the chunk upload system:
26 $this->chunk_mode
= UploadFromChunks
::INIT
;
27 $this->mDesiredDestName
= $param['filename'];
29 }else if( $this->mSessionKey
&& !$param['done']){
30 //this is a chunk piece
31 $this->chunk_mode
= UploadFromChunks
::CHUNK
;
32 }else if( $this->mSessionKey
&& $param['done']){
33 //this is the last chunk
34 $this->chunk_mode
= UploadFromChunks
::DONE
;
36 if( $this->chunk_mode
== UploadFromChunks
::CHUNK ||
37 $this->chunk_mode
== UploadFromChunks
::DONE
){
38 //set chunk related vars:
39 $this->mTempPath
= $request->getFileTempName( 'chunk' );
40 $this->mFileSize
= $request->getFileSize( 'chunk' );
46 static function isValidRequest( $request ) {
47 $sessionData = $request->getSessionData('wsUploadData');
48 if(! self
::isValidSessionKey(
49 $request->getInt( 'wpSessionKey' ),
53 return (bool)$request->getFileTempName( 'file' );
56 /* check warnings depending on chunk_mode*/
57 function checkWarnings(){
62 function isEmptyFile(){
63 //does not apply to chunk init
64 if( $this->chunk_mode
== UploadFromChunks
::INIT
){
67 return parent
::isEmptyFile();
70 /* Verify whether the upload is sane.
71 * Returns self::OK or else an array with error information
73 function verifyUpload( $resultDetails ) {
74 //no checks on chunk upload mode:
75 if( $this->chunk_mode
== UploadFromChunks
::INIT
)
78 //verify on init and last chunk request
79 if( $this->chunk_mode
== UploadFromChunks
::CHUNK ||
80 $this->chunk_mode
== UploadFromChunks
::DONE
)
81 return parent
::verifyUpload( $resultDetails );
83 //only run verifyFile on completed uploaded chunks
84 function verifyFile( $tmpFile ){
85 if( $this->chunk_mode
== UploadFromChunks
::DONE
){
86 //first append last chunk (so we can do a real verifyFile check... (check file type etc)
87 $status = $this->doChunkAppend();
88 if( $status->isOK() ){
89 $this->mTempPath
= $this->getRealPath( $this->mTempAppendPath
);
90 //verify the completed merged chunks as if it was the file that got uploaded:
91 return parent
::verifyFile( $this->mTempPath
) ;
93 //conflict of status returns (have to return the error ary) ... why we don't consistantly use a status object is beyond me..
94 return $status->getErrorsArray();
100 function getRealPath($srcPath){
101 $repo = RepoGroup
::singleton()->getLocalRepo();
102 if ( $repo->isVirtualUrl( $srcPath) ) {
103 return $repo->resolveVirtualUrl( $srcPath );
106 //pretty ugly inter-mixing of mParam and local vars
107 function setupChunkSession( $summary, $comment, $watch ) {
108 $this->mSessionKey
= $this->getSessionKey();
109 $_SESSION['wsUploadData'][ $this->mSessionKey
] = array(
110 'mComment' => $comment,
111 'mSummary' => $summary,
113 'mFilteredName' => $this->mFilteredName
,
114 'mTempAppendPath' => null, //the repo append path (not temporary local node mTempPath)
115 'mDesiredDestName' => $this->mDesiredDestName
,
116 'version' => self
::SESSION_VERSION
,
118 return $this->mSessionKey
;
120 function initFromSessionKey( $sessionKey, $request ){
121 if( !$sessionKey ||
empty( $sessionKey ) ){
124 $this->mSessionKey
= $sessionKey;
125 //load the sessionData array:
126 $sessionData = $request->getSessionData('wsUploadData');
128 if( isset( $sessionData[$this->mSessionKey
]['version'] ) &&
129 $sessionData[$this->mSessionKey
]['version'] == self
::SESSION_VERSION
) {
130 //update the local object from the session //
131 $this->mComment
= $sessionData[ $this->mSessionKey
][ 'mComment' ];
132 $this->mSummary
= $sessionData[ $this->mSessionKey
][ 'mSummary' ];
133 $this->mWatch
= $sessionData[ $this->mSessionKey
][ 'mWatch' ];
134 $this->mIgnorewarnings
= $sessionData[ $this->mSessionKey
][ 'mIgnorewarnings' ];
135 $this->mFilteredName
= $sessionData[ $this->mSessionKey
][ 'mFilteredName' ];
136 $this->mTempAppendPath
= $sessionData[ $this->mSessionKey
][ 'mTempAppendPath' ];
137 $this->mDesiredDestName
= $sessionData[ $this->mSessionKey
][ 'mDesiredDestName' ];
139 $this->status
= Array( 'error'=> 'missing session data');
143 //lets us return an api result (as flow for chunk uploads is kind of different than others.
144 function performUpload($summary='', $comment='', $watch='', $user){
145 global $wgServer, $wgScriptPath, $wgUser;
146 if( $this->chunk_mode
== UploadFromChunks
::INIT
){
147 //firefogg expects a specific result per:
148 //http://www.firefogg.org/dev/chunk_post.html
150 //its oky to return the token here because
151 //a) the user must have requested the token to get here and
152 //b) should only happen over POST
153 //c) (we need the token to validate chunks are coming from a non-xss request)
154 $token = urlencode( $wgUser->editToken() );
156 echo ApiFormatJson
::getJsonEncode( array(
157 "uploadUrl" => "{$wgServer}{$wgScriptPath}/api.php?action=upload&".
158 "token={$token}&format=json&enablechunks=true&chunksessionkey=".
159 $this->setupChunkSession($summary, $comment, $watch ) ) );
161 }else if( $this->chunk_mode
== UploadFromChunks
::CHUNK
){
162 $status = $this->doChunkAppend();
163 if( $status->isOK() ){
165 //firefogg expects a specific result per:
166 //http://www.firefogg.org/dev/chunk_post.html
168 echo ApiFormatJson
::getJsonEncode( array(
170 "filesize"=> filesize( $this->getRealPath( $this->mTempAppendPath
) )
180 }else if( $this->chunk_mode
== UploadFromChunks
::DONE
){
181 //update the values from the local (session init) if not paseed again)
183 $summary = $this->mSummary
;
186 $comment = $this->mComment
;
189 $watch = $this->mWatch
;
190 $status = parent
::performUpload($summary, $comment, $watch, $user );
191 if( !$status->isGood() ) {
194 $file = $this->getLocalFile();
195 //firefogg expects a specific result per:
196 //http://www.firefogg.org/dev/chunk_post.html
198 echo ApiFormatJson
::getJsonEncode( array(
201 "resultUrl"=> $file->getDescriptionUrl()
208 //append the given chunk to the temporary uploaded file. (if no temporary uploaded file exists created it.
209 function doChunkAppend(){
210 //if we don't have a mTempAppendPath to generate a file from the chunk packaged var:
211 if( ! $this->mTempAppendPath
){
214 //make a chunk store path. (append tmp file to chunk)
215 $status = $this->saveTempUploadedFile( $this->mDestName
, $this->mTempPath
);
217 if( $status->isOK() ) {
218 $this->mTempAppendPath
= $status->value
;
219 $_SESSION[ 'wsUploadData' ][ $this->mSessionKey
][ 'mTempAppendPath' ] = $this->mTempAppendPath
;
223 if( is_file( $this->getRealPath( $this->mTempAppendPath
) ) ){
224 $status = $this->appendToUploadFile( $this->mTempAppendPath
, $this->mTempPath
);
226 $status->fatal( 'filenotfound', $this->mTempAppendPath
);