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