6 * First, destination checks are made, and, if ignorewarnings is not
7 * checked, errors / warning is returned.
9 * 1. We return the uploadUrl.
10 * 2. We then accept chunk uploads from the client.
11 * 3. Return chunk id on each POSTED chunk.
12 * 4. Once the client posts "done=1", the files are concatenated together.
14 * (More info at: http://firefogg.org/dev/chunk_post.html)
16 class UploadFromChunks
extends UploadBase
{
22 protected $chunkMode; // INIT, CHUNK, DONE
23 protected $sessionKey;
25 protected $fileSize = 0;
32 // Parent class requires this function even though it is only
33 // used from SpecialUpload.php and we don't do chunked uploading
34 // from SpecialUpload -- best to raise an exception for
36 public function initializeFromRequest( &$request ) {
37 throw new MWException( 'not implemented' );
40 public function initialize( $done, $filename, $sessionKey, $path,
41 $fileSize, $sessionData )
43 $this->status
= new Status
;
45 $this->initFromSessionKey( $sessionKey, $sessionData );
47 if ( !$this->sessionKey
&& !$done ) {
48 // session key not set, init the chunk upload system:
49 $this->chunkMode
= self
::INIT
;
50 $this->mDesiredDestName
= $filename;
51 } else if ( $this->sessionKey
&& !$done ) {
52 $this->chunkMode
= self
::CHUNK
;
53 } else if ( $this->sessionKey
&& $done ) {
54 $this->chunkMode
= self
::DONE
;
56 if ( $this->chunkMode
== self
::CHUNK ||
$this->chunkMode
== self
::DONE
) {
57 $this->mTempPath
= $path;
58 $this->fileSize +
= $fileSize;
63 * Set session information for chunked uploads and allocate a unique key.
64 * @param $comment string
65 * @param $pageText string
66 * @param $watch boolean
68 * @returns string the session key for this chunked upload
70 protected function setupChunkSession( $comment, $pageText, $watch ) {
71 $this->sessionKey
= $this->getSessionKey();
72 $_SESSION['wsUploadData'][$this->sessionKey
] = array(
73 'comment' => $comment,
74 'pageText' => $pageText,
76 'mFilteredName' => $this->mFilteredName
,
78 'mDesiredDestName' => $this->mDesiredDestName
,
79 'version' => self
::SESSION_VERSION
,
81 return $this->sessionKey
;
85 * Initialize a continuation of a chunked upload from a session key
86 * @param $sessionKey string
87 * @param $request WebRequest
91 protected function initFromSessionKey( $sessionKey, $sessionData ) {
92 // testing against null because we don't want to cause obscure
93 // bugs when $sessionKey is full of "0"
94 if ( $sessionKey === null ) {
97 $this->sessionKey
= $sessionKey;
99 if ( isset( $sessionData[$this->sessionKey
]['version'] )
100 && $sessionData[$this->sessionKey
]['version'] == self
::SESSION_VERSION
)
102 $this->comment
= $sessionData[$this->sessionKey
]['comment'];
103 $this->pageText
= $sessionData[$this->sessionKey
]['pageText'];
104 $this->watch
= $sessionData[$this->sessionKey
]['watch'];
105 $this->mFilteredName
= $sessionData[$this->sessionKey
]['mFilteredName'];
106 $this->repoPath
= $sessionData[$this->sessionKey
]['repoPath'];
107 $this->mDesiredDestName
= $sessionData[$this->sessionKey
]['mDesiredDestName'];
109 $this->status
= Status
::newFatal( 'invalid-session-key' );
114 * Handle a chunk of the upload. Overrides the parent method
115 * because Chunked Uploading clients (i.e. Firefogg) require
116 * specific API responses.
117 * @see UploadBase::performUpload
119 public function performUpload( $comment, $pageText, $watch, $user ) {
120 wfDebug( "\n\n\performUpload(chunked): comment:" . $comment . ' pageText: ' . $pageText . ' watch:' . $watch );
121 global $wgUser, $wgOut;
123 if ( $this->chunkMode
== self
::INIT
) {
124 // firefogg expects a specific result per:
125 // http://www.firefogg.org/dev/chunk_post.html
127 // it's okay to return the token here because
128 // a) the user must have requested the token to get here and
129 // b) should only happen over POST
130 // c) we need the token to validate chunks are coming from a non-xss request
131 $token = urlencode( $wgUser->editToken() );
132 echo FormatJson
::encode( array(
133 'uploadUrl' => wfExpandUrl( wfScript( 'api' ) ) . "?action=upload&" .
134 "token={$token}&format=json&enablechunks=true&chunksessionkey=" .
135 $this->setupChunkSession( $comment, $pageText, $watch ) ) );
137 } else if ( $this->chunkMode
== self
::CHUNK
) {
138 $status = $this->appendChunk();
139 if ( !$status->isOK() ) {
143 // firefogg expects a specific result
144 // http://www.firefogg.org/dev/chunk_post.html
145 echo FormatJson
::encode(
146 array( 'result' => 1, 'filesize' => $this->fileSize
)
149 } else if ( $this->chunkMode
== self
::DONE
) {
151 $comment = $this->comment
;
154 $pageText = $this->pageText
;
157 $watch = $this->watch
;
159 $status = parent
::performUpload( $comment, $pageText, $watch, $user );
160 if ( !$status->isGood() ) {
163 $file = $this->getLocalFile();
165 // firefogg expects a specific result
166 // http://www.firefogg.org/dev/chunk_post.html
167 echo FormatJson
::encode( array(
170 'resultUrl' => $file->getDescriptionUrl() )
175 return Status
::newGood();
179 * Append a chunk to the Repo file
181 * @param string $srcPath Path to file to append from
182 * @param string $toAppendPath Path to file to append to
183 * @return Status Status
185 protected function appendToUploadFile( $srcPath, $toAppendPath ) {
186 $repo = RepoGroup
::singleton()->getLocalRepo();
187 $status = $repo->append( $srcPath, $toAppendPath );
192 * Append a chunk to the temporary file.
196 protected function appendChunk() {
197 global $wgMaxUploadSize;
199 if ( !$this->repoPath
) {
200 $this->status
= $this->saveTempUploadedFile( $this->mDesiredDestName
, $this->mTempPath
);
202 if ( $status->isOK() ) {
203 $this->repoPath
= $status->value
;
204 $_SESSION['wsUploadData'][$this->sessionKey
]['repoPath'] = $this->repoPath
;
208 if ( $this->getRealPath( $this->repoPath
) ) {
209 $this->status
= $this->appendToUploadFile( $this->repoPath
, $this->mTempPath
);
211 $this->status
= Status
::newFatal( 'filenotfound', $this->repoPath
);
213 if ( $this->fileSize
> $wgMaxUploadSize )
214 $this->status
= Status
::newFatal( 'largefileserver' );
217 public function verifyUpload() {
218 if ( $this->chunkMode
!= self
::DONE
) {
219 return array('status' => UploadBase
::OK
);
221 return parent
::verifyUpload();
224 public function checkWarnings() {
225 if ( $this->chunkMode
!= self
::DONE
) {
228 return parent
::checkWarnings();
231 public function getImageInfo( $result ) {
232 if ( $this->chunkMode
!= self
::DONE
) {
235 return parent
::getImageInfo( $result );