b4e0ddaedde217cf9356236347edb5bc2570dcdb
[lhc/web/wiklou.git] / includes / api / ApiUpload.php
1 <?php
2 /*
3 * Created on Aug 21, 2008
4 * API for MediaWiki 1.8+
5 *
6 * Copyright (C) 2008 - 2009 Bryan Tong Minh <Bryan.TongMinh@Gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * http://www.gnu.org/copyleft/gpl.html
22 */
23
24 if ( !defined( 'MEDIAWIKI' ) ) {
25 // Eclipse helper - will be ignored in production
26 require_once("ApiBase.php");
27 }
28
29 /**
30 * @ingroup API
31 */
32 class ApiUpload extends ApiBase {
33 var $mUpload = null;
34
35 public function __construct( $main, $action ) {
36 parent::__construct( $main, $action );
37 }
38
39 public function execute() {
40 global $wgUser;
41
42 $this->getMain()->isWriteMode();
43 $this->mParams = $this->extractRequestParams();
44 $request = $this->getMain()->getRequest();
45
46 // do token checks:
47 if( is_null( $this->mParams['token'] ) )
48 $this->dieUsageMsg( array( 'missingparam', 'token' ) );
49 if( !$wgUser->matchEditToken( $this->mParams['token'] ) )
50 $this->dieUsageMsg( array( 'sessionfailure' ) );
51
52
53 // Add the uploaded file to the params array
54 $this->mParams['file'] = $request->getFileName( 'file' );
55
56 // Check whether upload is enabled
57 if( !UploadBase::isEnabled() )
58 $this->dieUsageMsg( array( 'uploaddisabled' ) );
59
60 wfDebug( __METHOD__ . "running require param\n" );
61 // One and only one of the following parameters is needed
62 $this->requireOnlyOneParameter( $this->mParams,
63 'sessionkey', 'file', 'url', 'enablechunks' );
64
65 if( $this->mParams['enablechunks'] ){
66 // chunks upload enabled
67 $this->mUpload = new UploadFromChunks();
68 $this->mUpload->initializeFromParams( $this->mParams, $request );
69
70 //if getAPIresult did not exit report the status error:
71 if( isset( $this->mUpload->status['error'] ) )
72 $this->dieUsageMsg( $this->mUpload->status['error'] );
73
74 } else if( $this->mParams['internalhttpsession'] ){
75 $sd = & $_SESSION['wsDownload'][$this->mParams['internalhttpsession']];
76
77 // get the params from the init session:
78 $this->mUpload = new UploadFromFile();
79
80 $this->mUpload->initialize( $this->mParams['filename'],
81 $sd['target_file_path'],
82 filesize( $sd['target_file_path'] )
83 );
84
85 if( !isset( $this->mUpload ) )
86 $this->dieUsage( 'No upload module set', 'nomodule' );
87
88 } else if( $this->mParams['httpstatus'] && $this->mParams['sessionkey'] ){
89 // return the status of the given upload session_key:
90 if( !isset( $_SESSION['wsDownload'][ $this->mParams['sessionkey'] ] ) ){
91 return $this->dieUsageMsg( array( 'invalid-session-key' ) );
92 }
93 $sd = & $_SESSION['wsDownload'][$this->mParams['sessionkey']];
94 // keep passing down the upload sessionkey
95 $statusResult = array(
96 'upload_session_key' => $this->mParams['sessionkey']
97 );
98
99 // put values into the final apiResult if available
100 if( isset( $sd['apiUploadResult'] ) ) $statusResult['apiUploadResult'] = $sd['apiUploadResult'];
101 if( isset( $sd['loaded'] ) ) $statusResult['loaded'] = $sd['loaded'];
102 if( isset( $sd['content_length'] ) ) $statusResult['content_length'] = $sd['content_length'];
103
104 return $this->getResult()->addValue( null, $this->getModuleName(),
105 $statusResult
106 );
107 } else if( $this->mParams['sessionkey'] ) {
108 // Stashed upload
109 $this->mUpload = new UploadFromStash();
110 $this->mUpload->initialize( $this->mParams['filename'], $_SESSION['wsUploadData'][$this->mParams['sessionkey']] );
111 } else {
112 // Upload from url or file
113 // Parameter filename is required
114 if( !isset( $this->mParams['filename'] ) )
115 $this->dieUsageMsg( array( 'missingparam', 'filename' ) );
116
117 // Initialize $this->mUpload
118 if( isset( $this->mParams['file'] ) ) {
119 $this->mUpload = new UploadFromFile();
120 $this->mUpload->initialize(
121 $request->getFileName( 'file' ),
122 $request->getFileTempName( 'file' ),
123 $request->getFileSize( 'file' )
124 );
125 } elseif( isset( $this->mParams['url'] ) ) {
126
127 $this->mUpload = new UploadFromUrl();
128 $this->mUpload->initialize( $this->mParams['filename'], $this->mParams['url'], $this->mParams['asyncdownload'] );
129
130 $status = $this->mUpload->fetchFile();
131 if( !$status->isOK() ){
132 return $this->dieUsage( 'fetchfileerror', $status->getWikiText() );
133 }
134 //check if we doing a async request set session info and return the upload_session_key)
135 if( $this->mUpload->isAsync() ){
136 $upload_session_key = $status->value;
137 // update the session with anything with the params we will need to finish up the upload later on:
138 if( !isset( $_SESSION['wsDownload'][$upload_session_key] ) )
139 $_SESSION['wsDownload'][$upload_session_key] = array();
140
141 $sd =& $_SESSION['wsDownload'][$upload_session_key];
142
143 // copy mParams for finishing up after:
144 $sd['mParams'] = $this->mParams;
145
146 return $this->getResult()->addValue( null, $this->getModuleName(),
147 array( 'upload_session_key' => $upload_session_key
148 ));
149 }
150 //else the file downloaded in place continue with validation:
151 }
152 }
153
154 if( !isset( $this->mUpload ) )
155 $this->dieUsage( 'No upload module set', 'nomodule' );
156
157 //finish up the exec command:
158 $this->doExecUpload();
159 }
160
161 function doExecUpload(){
162 global $wgUser;
163 // Check whether the user has the appropriate permissions to upload anyway
164 $permission = $this->mUpload->isAllowed( $wgUser );
165
166 if( $permission !== true ) {
167 if( !$wgUser->isLoggedIn() )
168 $this->dieUsageMsg( array( 'mustbeloggedin', 'upload' ) );
169 else
170 $this->dieUsageMsg( array( 'badaccess-groups' ) );
171 }
172 // Perform the upload
173 $result = $this->performUpload();
174 // Cleanup any temporary mess
175 $this->mUpload->cleanupTempFile();
176 $this->getResult()->addValue( null, $this->getModuleName(), $result );
177 }
178
179 private function performUpload() {
180 global $wgUser;
181 $result = array();
182 $resultDetails = null;
183 $permErrors = $this->mUpload->verifyPermissions( $wgUser );
184 if( $permErrors !== true ) {
185 $result['result'] = 'Failure';
186 $result['error'] = 'permission-denied';
187 return $result;
188 }
189 $verification = $this->mUpload->verifyUpload( $resultDetails );
190 if( $verification != UploadBase::OK ) {
191 $result['result'] = 'Failure';
192 switch( $verification ) {
193 case UploadBase::EMPTY_FILE:
194 $result['error'] = 'empty-file';
195 break;
196 case UploadBase::FILETYPE_MISSING:
197 $result['error'] = 'filetype-missing';
198 break;
199 case UploadBase::FILETYPE_BADTYPE:
200 global $wgFileExtensions;
201 $result['error'] = 'filetype-banned';
202 $result['filetype'] = $resultDetails['finalExt'];
203 $result['allowed-filetypes'] = $wgFileExtensions;
204 break;
205 case UploadBase::MIN_LENGHT_PARTNAME:
206 $result['error'] = 'filename-tooshort';
207 break;
208 case UploadBase::ILLEGAL_FILENAME:
209 $result['error'] = 'illegal-filename';
210 $result['filename'] = $resultDetails['filtered'];
211 break;
212 case UploadBase::OVERWRITE_EXISTING_FILE:
213 $result['error'] = 'overwrite';
214 break;
215 case UploadBase::VERIFICATION_ERROR:
216 $result['error'] = 'verification-error';
217 $args = $resultDetails['veri'];
218 $code = array_shift( $args );
219 $result['verification-error'] = $code;
220 $result['args'] = $args;
221 $this->getResult()->setIndexedTagName( $result['args'], 'arg' );
222 break;
223 case UploadBase::UPLOAD_VERIFICATION_ERROR:
224 $result['error'] = 'upload-verification-error';
225 $result['upload-verification-error'] = $resultDetails['error'];
226 break;
227 default:
228 $result['error'] = 'unknown-error';
229 $result['code'] = $verification;
230 break;
231 }
232 return $result;
233 }
234
235 if( !$this->mParams['ignorewarnings'] ) {
236 $warnings = $this->mUpload->checkWarnings();
237 if( $warnings ) {
238 $this->getResult()->setIndexedTagName( $warnings, 'warning' );
239
240 $result['result'] = 'Warning';
241 $result['warnings'] = $warnings;
242 if( isset( $result['filewasdeleted'] ) )
243 $result['filewasdeleted'] = $result['filewasdeleted']->getDBkey();
244
245 $sessionKey = $this->mUpload->stashSession();
246 if( $sessionKey )
247 $result['sessionkey'] = $sessionKey;
248 return $result;
249 }
250 }
251
252 // do the upload
253 $status = $this->mUpload->performUpload( $this->mParams['comment'],
254 $this->mParams['comment'], $this->mParams['watch'], $wgUser );
255
256 if( !$status->isGood() ) {
257 $result['result'] = 'Failure';
258 $result['error'] = 'internal-error';
259 $result['details'] = $status->getErrorsArray();
260 $this->getResult()->setIndexedTagName( $result['details'], 'error' );
261 return $result;
262 }
263
264 $file = $this->mUpload->getLocalFile();
265 $result['result'] = 'Success';
266 $result['filename'] = $file->getName();
267
268 // Append imageinfo to the result
269
270 // might be a cleaner way to call this:
271 $imParam = ApiQueryImageInfo::getAllowedParams();
272 $imProp = $imParam['prop'][ApiBase::PARAM_TYPE];
273 $result['imageinfo'] = ApiQueryImageInfo::getInfo( $file,
274 array_flip( $imProp ),
275 $this->getResult() );
276
277 wfDebug( "\n\n return result: " . print_r( $result, true ) );
278
279 return $result;
280 }
281
282 public function mustBePosted() {
283 return true;
284 }
285
286 public function isWriteMode() {
287 return true;
288 }
289
290 public function getAllowedParams() {
291 return array(
292 'filename' => null,
293 'file' => null,
294 'chunk' => null,
295 'url' => null,
296 'token' => null,
297 'enablechunks' => null,
298 'comment' => array(
299 ApiBase::PARAM_DFLT => ''
300 ),
301 'asyncdownload' => false,
302 'watch' => false,
303 'ignorewarnings' => false,
304 'done' => false,
305 'sessionkey' => null,
306 'httpstatus' => false,
307 'chunksessionkey' => null,
308 'internalhttpsession' => null,
309 );
310 }
311
312 public function getParamDescription() {
313 return array(
314 'filename' => 'Target filename',
315 'file' => 'File contents',
316 'chunk'=> 'Chunk File Contents',
317 'url' => 'Url to upload from',
318 'comment' => 'Upload comment or initial page text',
319 'token' => 'Edit token. You can get one of these through prop=info (this helps avoid remote ajax upload requests with your credentials)',
320 'enablechunks' => 'Boolean If we are in chunk mode; accepts many small file POSTs',
321 'asyncdownload' => 'If we should download the url asyncrously usefull for large http downloads (returns a upload session key to get status updates in subquent calls)',
322 'watch' => 'Watch the page',
323 'ignorewarnings' => 'Ignore any warnings',
324 'done' => 'When used with "chunks", Is sent to notify the api The last chunk is being uploaded.',
325 'sessionkey' => 'Session key in case there were any warnings.',
326 'httpstatus' => 'When set to true, will return the status of a given sessionKey (used for progress meters)',
327 'chunksessionkey' => 'Used to sync uploading of chunks',
328 'internalhttpsession' => 'Used internally for http session downloads',
329 );
330 }
331
332 public function getDescription() {
333 return array(
334 'Upload a file'
335 );
336 }
337
338 protected function getExamples() {
339 return array(
340 'api.php?action=upload&filename=Wiki.png&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png&ignorewarnings'
341 );
342 }
343
344 public function getVersion() {
345 return __CLASS__ . ': $Id: ApiUpload.php 51812 2009-06-12 23:45:20Z dale $';
346 }
347 }
348