Index: trunk/extensions/SemanticForms/specials/SF_UploadWindow.php |
— | — | @@ -1,1552 +0,0 @@ |
2 | | -<?php |
3 | | -/** |
4 | | - * SF_UploadWindow - used for uploading files from within a form, for |
5 | | - * versions of MediaWiki below 1.16. |
6 | | - * This class is nearly identical to the pre-1.16 version of MediaWiki's |
7 | | - * SpecialUpload class, but with a few changes to remove skin CSS and |
8 | | - * HTML, and to populate the relevant field in the form with the name |
9 | | - * of the uploaded form. |
10 | | - * |
11 | | - * This class is based almost entirely on the upload functionality |
12 | | - * developed by the Chickipedia.com team. |
13 | | - * |
14 | | - * @author Yaron Koren |
15 | | - * @file |
16 | | - * @ingroup SF |
17 | | - */ |
18 | | - |
19 | | -/** |
20 | | - * @ingroup SFSpecialPages |
21 | | - */ |
22 | | -class SFUploadWindow extends UnlistedSpecialPage { |
23 | | - |
24 | | - /** |
25 | | - * Constructor |
26 | | - */ |
27 | | - function __construct() { |
28 | | - parent::__construct( 'UploadWindow' ); |
29 | | - SFUtils::loadMessages(); |
30 | | - } |
31 | | - |
32 | | - function execute( $query ) { |
33 | | - $this->setHeaders(); |
34 | | - doSpecialUploadWindow(); |
35 | | - } |
36 | | -} |
37 | | - |
38 | | -/** |
39 | | - * Entry point |
40 | | - */ |
41 | | -function doSpecialUploadWindow() { |
42 | | - global $wgRequest, $wgOut, $wgUser, $wgServer; |
43 | | - global $wgScript, $wgJsMimeType, $wgStylePath, $wgStyleVersion; |
44 | | - global $wgContLang, $wgLanguageCode, $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces; |
45 | | - global $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview; |
46 | | - |
47 | | - // disable $wgOut - we'll print out the page manually, taking the |
48 | | - // body created by the form, plus the necessary Javascript files, |
49 | | - // and turning them into an HTML page |
50 | | - $wgOut->disable(); |
51 | | - $form = new UploadWindowForm( $wgRequest ); |
52 | | - $form->execute(); |
53 | | - $sk = $wgUser->getSkin(); |
54 | | - $sk->initPage( $wgOut ); // need to call this to set skin name correctly |
55 | | - // call to get user JS was changed in MW 1.14 |
56 | | - if ( method_exists( $sk, 'generateUserJs' ) ) { |
57 | | - $skin_user_js = $sk->generateUserJs(); |
58 | | - } else { |
59 | | - $skin_user_js = $sk->getUserJs(); |
60 | | - } |
61 | | - $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; |
62 | | - $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview; |
63 | | - $adc = wfBoolToStr( $useAjaxDestCheck ); |
64 | | - $alp = wfBoolToStr( $useAjaxLicensePreview ); |
65 | | - $autofill = wfBoolToStr( true ); |
66 | | - |
67 | | - $user_js = <<<END |
68 | | -<script type="{$wgJsMimeType}"> |
69 | | -$skin_user_js; |
70 | | -wgServer="{$wgServer}"; |
71 | | -wgScript="{$wgScript}" |
72 | | -wgAjaxUploadDestCheck = {$adc}; |
73 | | -wgAjaxLicensePreview = {$alp}; |
74 | | -wgUploadAutoFill = {$autofill}; |
75 | | -</script> |
76 | | - |
77 | | -END; |
78 | | - $vars_js = Skin::makeGlobalVariablesScript( array( 'skinname' => $sk->getSkinName() ) ); |
79 | | - $wikibits_include = "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/wikibits.js?$wgStyleVersion\"></script>"; |
80 | | - $ajax_include = "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js?$wgStyleVersion\"></script>"; |
81 | | - $ajaxwatch_include = "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxwatch.js?$wgStyleVersion\"></script>"; |
82 | | - $text = <<<END |
83 | | -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
84 | | -<html xmlns="{$wgXhtmlDefaultNamespace}" |
85 | | -END; |
86 | | - foreach ( $wgXhtmlNamespaces as $tag => $ns ) { |
87 | | - $text .= "xmlns:{$tag}=\"{$ns}\" "; |
88 | | - } |
89 | | - $dir = $wgContLang->isRTL() ? "rtl" : "ltr"; |
90 | | - $text .= "xml:lang=\"{$wgLanguageCode}\" lang=\"{$wgLanguageCode}\" dir=\"{$dir}\">"; |
91 | | - |
92 | | - $text .= <<<END |
93 | | - |
94 | | -<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
95 | | -<head> |
96 | | -$vars_js |
97 | | -$wikibits_include |
98 | | -$user_js |
99 | | -$ajax_include |
100 | | -$ajaxwatch_include |
101 | | -{$wgOut->getScript()} |
102 | | -</head> |
103 | | -<body> |
104 | | -{$wgOut->getHTML()} |
105 | | -</body> |
106 | | -</html> |
107 | | - |
108 | | -END; |
109 | | - print $text; |
110 | | -} |
111 | | - |
112 | | -/** |
113 | | - * implements Special:UploadWindow |
114 | | - * @ingroup SFSpecialPages |
115 | | - */ |
116 | | -class UploadWindowForm { |
117 | | - /**#@+ |
118 | | - * @access private |
119 | | - */ |
120 | | - var $mComment, $mLicense, $mIgnoreWarning, $mCurlError; |
121 | | - var $mDestName, $mTempPath, $mFileSize, $mFileProps; |
122 | | - var $mCopyrightStatus, $mCopyrightSource, $mReUpload, $mAction, $mUploadClicked; |
123 | | - var $mSrcName, $mSessionKey, $mStashed, $mDesiredDestName, $mRemoveTempFile, $mSourceType; |
124 | | - var $mDestWarningAck, $mCurlDestHandle; |
125 | | - var $mLocalFile; |
126 | | - |
127 | | - # Placeholders for text injection by hooks (must be HTML) |
128 | | - # extensions should take care to _append_ to the present value |
129 | | - var $uploadFormTextTop; |
130 | | - var $uploadFormTextAfterSummary; |
131 | | - |
132 | | - # used by Semantic Forms |
133 | | - var $mInputID; |
134 | | - var $mDelimiter; |
135 | | - |
136 | | - const SESSION_VERSION = 1; |
137 | | - /**#@-*/ |
138 | | - |
139 | | - /** |
140 | | - * Constructor : initialise object |
141 | | - * Get data POSTed through the form and assign them to the object |
142 | | - * @param $request Data posted. |
143 | | - */ |
144 | | - function __construct( &$request ) { |
145 | | - global $wgAllowCopyUploads; |
146 | | - $this->mDesiredDestName = $request->getText( 'wpDestFile' ); |
147 | | - $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ); |
148 | | - $this->mComment = $request->getText( 'wpUploadDescription' ); |
149 | | - $this->mInputID = $request->getText( 'sfInputID' ); |
150 | | - $this->mDelimiter = $request->getText( 'sfDelimiter' ); |
151 | | - |
152 | | - if ( !$request->wasPosted() ) { |
153 | | - # GET requests just give the main form; no data except destination |
154 | | - # filename and description |
155 | | - return; |
156 | | - } |
157 | | - |
158 | | - # Placeholders for text injection by hooks (empty per default) |
159 | | - $this->uploadFormTextTop = ""; |
160 | | - $this->uploadFormTextAfterSummary = ""; |
161 | | - |
162 | | - $this->mReUpload = $request->getCheck( 'wpReUpload' ); |
163 | | - $this->mUploadClicked = $request->getCheck( 'wpUpload' ); |
164 | | - |
165 | | - $this->mLicense = $request->getText( 'wpLicense' ); |
166 | | - $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); |
167 | | - $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); |
168 | | - $this->mWatchthis = $request->getBool( 'wpWatchthis' ); |
169 | | - $this->mSourceType = $request->getText( 'wpSourceType' ); |
170 | | - $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); |
171 | | - |
172 | | - $this->mAction = $request->getVal( 'action' ); |
173 | | - |
174 | | - $this->mSessionKey = $request->getInt( 'wpSessionKey' ); |
175 | | - if ( !empty( $this->mSessionKey ) && |
176 | | - isset( $_SESSION['wsUploadData'][$this->mSessionKey]['version'] ) && |
177 | | - $_SESSION['wsUploadData'][$this->mSessionKey]['version'] == self::SESSION_VERSION ) { |
178 | | - /** |
179 | | - * Confirming a temporarily stashed upload. |
180 | | - * We don't want path names to be forged, so we keep |
181 | | - * them in the session on the server and just give |
182 | | - * an opaque key to the user agent. |
183 | | - */ |
184 | | - $data = $_SESSION['wsUploadData'][$this->mSessionKey]; |
185 | | - $this->mTempPath = $data['mTempPath']; |
186 | | - $this->mFileSize = $data['mFileSize']; |
187 | | - $this->mSrcName = $data['mSrcName']; |
188 | | - $this->mFileProps = $data['mFileProps']; |
189 | | - $this->mCurlError = 0/*UPLOAD_ERR_OK*/; |
190 | | - $this->mStashed = true; |
191 | | - $this->mRemoveTempFile = false; |
192 | | - } else { |
193 | | - /** |
194 | | - *Check for a newly uploaded file. |
195 | | - */ |
196 | | - if ( $wgAllowCopyUploads && $this->mSourceType == 'web' ) { |
197 | | - $this->initializeFromUrl( $request ); |
198 | | - } else { |
199 | | - $this->initializeFromUpload( $request ); |
200 | | - } |
201 | | - } |
202 | | - } |
203 | | - |
204 | | - /** |
205 | | - * Initialize the uploaded file from PHP data |
206 | | - * @access private |
207 | | - */ |
208 | | - function initializeFromUpload( $request ) { |
209 | | - $this->mTempPath = $request->getFileTempName( 'wpUploadFile' ); |
210 | | - $this->mFileSize = $request->getFileSize( 'wpUploadFile' ); |
211 | | - $this->mSrcName = $request->getFileName( 'wpUploadFile' ); |
212 | | - $this->mCurlError = $request->getUploadError( 'wpUploadFile' ); |
213 | | - $this->mSessionKey = false; |
214 | | - $this->mStashed = false; |
215 | | - $this->mRemoveTempFile = false; // PHP will handle this |
216 | | - $this->mInputID = $request->getText( 'sfInputID' ); |
217 | | - $this->mDelimiter = $request->getText( 'sfDelimiter' ); |
218 | | - } |
219 | | - |
220 | | - /** |
221 | | - * Copy a web file to a temporary file |
222 | | - * @access private |
223 | | - */ |
224 | | - function initializeFromUrl( $request ) { |
225 | | - global $wgTmpDirectory; |
226 | | - $url = $request->getText( 'wpUploadFileURL' ); |
227 | | - $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' ); |
228 | | - |
229 | | - $this->mTempPath = $local_file; |
230 | | - $this->mFileSize = 0; # Will be set by curlCopy |
231 | | - $this->mCurlError = $this->curlCopy( $url, $local_file ); |
232 | | - $this->mSrcName = array_pop( explode( '/', $url ) ); |
233 | | - $this->mSessionKey = false; |
234 | | - $this->mStashed = false; |
235 | | - |
236 | | - // PHP won't auto-cleanup the file |
237 | | - $this->mRemoveTempFile = file_exists( $local_file ); |
238 | | - } |
239 | | - |
240 | | - /** |
241 | | - * Safe copy from URL |
242 | | - * Returns true if there was an error, false otherwise |
243 | | - */ |
244 | | - private function curlCopy( $url, $dest ) { |
245 | | - global $wgUser, $wgOut; |
246 | | - |
247 | | - if ( !$wgUser->isAllowed( 'upload_by_url' ) ) { |
248 | | - $wgOut->permissionRequired( 'upload_by_url' ); |
249 | | - return true; |
250 | | - } |
251 | | - |
252 | | - # Maybe remove some pasting blanks :-) |
253 | | - $url = trim( $url ); |
254 | | - if ( stripos( $url, 'http://' ) !== 0 && stripos( $url, 'ftp://' ) !== 0 ) { |
255 | | - # Only HTTP or FTP URLs |
256 | | - $wgOut->errorPage( 'upload-proto-error', 'upload-proto-error-text' ); |
257 | | - return true; |
258 | | - } |
259 | | - |
260 | | - # Open temporary file |
261 | | - $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" ); |
262 | | - if ( $this->mCurlDestHandle === false ) { |
263 | | - # Could not open temporary file to write in |
264 | | - $wgOut->errorPage( 'upload-file-error', 'upload-file-error-text' ); |
265 | | - return true; |
266 | | - } |
267 | | - |
268 | | - $ch = curl_init(); |
269 | | - curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0 ); # Probably not needed, but apparently can work around some bug |
270 | | - curl_setopt( $ch, CURLOPT_TIMEOUT, 10 ); # 10 seconds timeout |
271 | | - curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512 ); # 0.5KB per second minimum transfer speed |
272 | | - curl_setopt( $ch, CURLOPT_URL, $url ); |
273 | | - curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) ); |
274 | | - curl_exec( $ch ); |
275 | | - $error = curl_errno( $ch ) ? true : false; |
276 | | - $errornum = curl_errno( $ch ); |
277 | | - // if ( $error ) print curl_error ( $ch ) ; # Debugging output |
278 | | - curl_close( $ch ); |
279 | | - |
280 | | - fclose( $this->mCurlDestHandle ); |
281 | | - unset( $this->mCurlDestHandle ); |
282 | | - if ( $error ) { |
283 | | - unlink( $dest ); |
284 | | - if ( wfEmptyMsg( "upload-curl-error$errornum", wfMsg( "upload-curl-error$errornum" ) ) ) |
285 | | - $wgOut->errorPage( 'upload-misc-error', 'upload-misc-error-text' ); |
286 | | - else |
287 | | - $wgOut->errorPage( "upload-curl-error$errornum", "upload-curl-error$errornum-text" ); |
288 | | - } |
289 | | - |
290 | | - return $error; |
291 | | - } |
292 | | - |
293 | | - /** |
294 | | - * Callback function for CURL-based web transfer |
295 | | - * Write data to file unless we've passed the length limit; |
296 | | - * if so, abort immediately. |
297 | | - * @access private |
298 | | - */ |
299 | | - function uploadCurlCallback( $ch, $data ) { |
300 | | - global $wgMaxUploadSize; |
301 | | - $length = strlen( $data ); |
302 | | - $this->mFileSize += $length; |
303 | | - if ( $this->mFileSize > $wgMaxUploadSize ) { |
304 | | - return 0; |
305 | | - } |
306 | | - fwrite( $this->mCurlDestHandle, $data ); |
307 | | - return $length; |
308 | | - } |
309 | | - |
310 | | - /** |
311 | | - * Start doing stuff |
312 | | - * @access public |
313 | | - */ |
314 | | - function execute() { |
315 | | - global $wgUser, $wgOut; |
316 | | - global $wgEnableUploads; |
317 | | - |
318 | | - # Check uploading enabled |
319 | | - if ( !$wgEnableUploads ) { |
320 | | - $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext', array( $this->mDesiredDestName ) ); |
321 | | - return; |
322 | | - } |
323 | | - |
324 | | - # Check permissions |
325 | | - if ( !$wgUser->isAllowed( 'upload' ) ) { |
326 | | - if ( !$wgUser->isLoggedIn() ) { |
327 | | - $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); |
328 | | - } else { |
329 | | - $wgOut->permissionRequired( 'upload' ); |
330 | | - } |
331 | | - return; |
332 | | - } |
333 | | - |
334 | | - # Check blocks |
335 | | - if ( $wgUser->isBlocked() ) { |
336 | | - $wgOut->blockedPage(); |
337 | | - return; |
338 | | - } |
339 | | - |
340 | | - if ( wfReadOnly() ) { |
341 | | - $wgOut->readOnlyPage(); |
342 | | - return; |
343 | | - } |
344 | | - |
345 | | - if ( $this->mReUpload ) { |
346 | | - if ( !$this->unsaveUploadedFile() ) { |
347 | | - return; |
348 | | - } |
349 | | - $this->mainUploadWindowForm(); |
350 | | - } elseif ( 'submit' == $this->mAction || $this->mUploadClicked ) { |
351 | | - $this->processUpload(); |
352 | | - } else { |
353 | | - $this->mainUploadWindowForm(); |
354 | | - } |
355 | | - |
356 | | - $this->cleanupTempFile(); |
357 | | - } |
358 | | - |
359 | | - /* -------------------------------------------------------------- */ |
360 | | - |
361 | | - /** |
362 | | - * Really do the upload |
363 | | - * Checks are made in SpecialUpload::execute() |
364 | | - * @access private |
365 | | - */ |
366 | | - function processUpload() { |
367 | | - global $wgUser, $wgOut; |
368 | | - |
369 | | - if ( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) |
370 | | - { |
371 | | - wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file." ); |
372 | | - return false; |
373 | | - } |
374 | | - |
375 | | - /* Check for PHP error if any, requires php 4.2 or newer */ |
376 | | - if ( $this->mCurlError == 1/*UPLOAD_ERR_INI_SIZE*/ ) { |
377 | | - $this->mainUploadWindowForm( wfMsgHtml( 'largefileserver' ) ); |
378 | | - return; |
379 | | - } |
380 | | - |
381 | | - /** |
382 | | - * If there was no filename or a zero size given, give up quick. |
383 | | - */ |
384 | | - if ( trim( $this->mSrcName ) === '' || empty( $this->mFileSize ) ) { |
385 | | - $this->mainUploadWindowForm( wfMsgHtml( 'emptyfile' ) ); |
386 | | - return; |
387 | | - } |
388 | | - |
389 | | - # Chop off any directories in the given filename |
390 | | - if ( $this->mDesiredDestName ) { |
391 | | - $basename = $this->mDesiredDestName; |
392 | | - } else { |
393 | | - $basename = $this->mSrcName; |
394 | | - } |
395 | | - $filtered = wfBaseName( $basename ); |
396 | | - |
397 | | - /** |
398 | | - * We'll want to blacklist against *any* 'extension', and use |
399 | | - * only the final one for the whitelist. |
400 | | - */ |
401 | | - list( $partname, $ext ) = $this->splitExtensions( $filtered ); |
402 | | - |
403 | | - if ( count( $ext ) ) { |
404 | | - $finalExt = $ext[count( $ext ) - 1]; |
405 | | - } else { |
406 | | - $finalExt = ''; |
407 | | - } |
408 | | - |
409 | | - # If there was more than one "extension", reassemble the base |
410 | | - # filename to prevent bogus complaints about length |
411 | | - if ( count( $ext ) > 1 ) { |
412 | | - for ( $i = 0; $i < count( $ext ) - 1; $i++ ) |
413 | | - $partname .= '.' . $ext[$i]; |
414 | | - } |
415 | | - |
416 | | - if ( strlen( $partname ) < 1 ) { |
417 | | - $this->mainUploadWindowForm( wfMsgHtml( 'minlength1' ) ); |
418 | | - return; |
419 | | - } |
420 | | - |
421 | | - /** |
422 | | - * Filter out illegal characters, and try to make a legible name |
423 | | - * out of it. We'll strip some silently that Title would die on. |
424 | | - */ |
425 | | - $filtered = wfStripIllegalFilenameChars ( $filtered ); |
426 | | - $nt = Title::makeTitleSafe( NS_IMAGE, $filtered ); |
427 | | - if ( is_null( $nt ) ) { |
428 | | - $this->uploadError( wfMsgWikiHtml( 'illegalfilename', htmlspecialchars( $filtered ) ) ); |
429 | | - return; |
430 | | - } |
431 | | - $this->mLocalFile = wfLocalFile( $nt ); |
432 | | - $this->mDestName = $this->mLocalFile->getName(); |
433 | | - |
434 | | - /** |
435 | | - * If the image is protected, non-sysop users won't be able |
436 | | - * to modify it by uploading a new revision. |
437 | | - */ |
438 | | - if ( !$nt->userCan( 'edit' ) ) { |
439 | | - return $this->uploadError( wfMsgWikiHtml( 'protectedpage' ) ); |
440 | | - } |
441 | | - |
442 | | - /** |
443 | | - * In some cases we may forbid overwriting of existing files. |
444 | | - */ |
445 | | - $overwrite = $this->checkOverwrite( $this->mDestName ); |
446 | | - if ( WikiError::isError( $overwrite ) ) { |
447 | | - return $this->uploadError( $overwrite->toString() ); |
448 | | - } |
449 | | - |
450 | | - /* Don't allow users to override the blacklist (check file extension) */ |
451 | | - global $wgStrictFileExtensions; |
452 | | - global $wgFileExtensions, $wgFileBlacklist; |
453 | | - if ( $finalExt === '' ) { |
454 | | - return $this->uploadError( wfMsgExt( 'filetype-missing', array ( 'parseinline' ) ) ); |
455 | | - } elseif ( $this->checkFileExtensionList( $ext, $wgFileBlacklist ) || |
456 | | - ( $wgStrictFileExtensions && !$this->checkFileExtension( $finalExt, $wgFileExtensions ) ) ) { |
457 | | - return $this->uploadError( wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), |
458 | | - htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ) ); |
459 | | - } |
460 | | - |
461 | | - /** |
462 | | - * Look at the contents of the file; if we can recognize the |
463 | | - * type but it's corrupt or data of the wrong type, we should |
464 | | - * probably not accept it. |
465 | | - */ |
466 | | - if ( !$this->mStashed ) { |
467 | | - $this->mFileProps = File::getPropsFromPath( $this->mTempPath, $finalExt ); |
468 | | - $veri = $this->verify( $this->mTempPath, $finalExt ); |
469 | | - |
470 | | - if ( $veri !== true ) { // it's a wiki error... |
471 | | - return $this->uploadError( $veri->toString() ); |
472 | | - } |
473 | | - |
474 | | - /** |
475 | | - * Provide an opportunity for extensions to add further checks |
476 | | - */ |
477 | | - $error = ''; |
478 | | - if ( !wfRunHooks( 'UploadVerification', |
479 | | - array( $this->mDestName, $this->mTempPath, &$error ) ) ) { |
480 | | - return $this->uploadError( $error ); |
481 | | - } |
482 | | - } |
483 | | - |
484 | | - |
485 | | - /** |
486 | | - * Check for non-fatal conditions |
487 | | - */ |
488 | | - if ( ! $this->mIgnoreWarning ) { |
489 | | - $warning = ''; |
490 | | - |
491 | | - global $wgCapitalLinks; |
492 | | - if ( $wgCapitalLinks ) { |
493 | | - $filtered = ucfirst( $filtered ); |
494 | | - } |
495 | | - if ( $basename != $filtered ) { |
496 | | - $warning .= '<li>' . wfMsgHtml( 'badfilename', htmlspecialchars( $this->mDestName ) ) . '</li>'; |
497 | | - } |
498 | | - |
499 | | - global $wgCheckFileExtensions; |
500 | | - if ( $wgCheckFileExtensions ) { |
501 | | - if ( ! $this->checkFileExtension( $finalExt, $wgFileExtensions ) ) { |
502 | | - $warning .= '<li>' . wfMsgExt( 'filetype-badtype', array ( 'parseinline' ), |
503 | | - htmlspecialchars( $finalExt ), implode ( ', ', $wgFileExtensions ) ) . '</li>'; |
504 | | - } |
505 | | - } |
506 | | - |
507 | | - global $wgUploadSizeWarning; |
508 | | - if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) { |
509 | | - $skin = $wgUser->getSkin(); |
510 | | - $wsize = $skin->formatSize( $wgUploadSizeWarning ); |
511 | | - $asize = $skin->formatSize( $this->mFileSize ); |
512 | | - $warning .= '<li>' . wfMsgHtml( 'large-file', $wsize, $asize ) . '</li>'; |
513 | | - } |
514 | | - if ( $this->mFileSize == 0 ) { |
515 | | - $warning .= '<li>' . wfMsgHtml( 'emptyfile' ) . '</li>'; |
516 | | - } |
517 | | - |
518 | | - if ( !$this->mDestWarningAck ) { |
519 | | - $warning .= self::getExistsWarning( $this->mLocalFile ); |
520 | | - } |
521 | | - if ( $warning !== '' ) { |
522 | | - /** |
523 | | - * Stash the file in a temporary location; the user can choose |
524 | | - * to let it through and we'll complete the upload then. |
525 | | - */ |
526 | | - return $this->uploadWarning( $warning ); |
527 | | - } |
528 | | - } |
529 | | - |
530 | | - /** |
531 | | - * Try actually saving the thing... |
532 | | - * It will show an error form on failure. |
533 | | - */ |
534 | | - $pageText = self::getInitialPageText( $this->mComment, $this->mLicense, |
535 | | - $this->mCopyrightStatus, $this->mCopyrightSource ); |
536 | | - |
537 | | - $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText, |
538 | | - File::DELETE_SOURCE, $this->mFileProps ); |
539 | | - if ( !$status->isGood() ) { |
540 | | - $this->showError( $status->getWikiText() ); |
541 | | - } else { |
542 | | - if ( $this->mWatchthis ) { |
543 | | - global $wgUser; |
544 | | - $wgUser->addWatch( $this->mLocalFile->getTitle() ); |
545 | | - |
546 | | - } |
547 | | - // Success, redirect to description page |
548 | | - // $wgOut->redirect( $this->mLocalFile->getTitle()->getFullURL() ); |
549 | | - |
550 | | - // Semantic Forms change - output Javascript to either |
551 | | - // fill in or append to the field in original form, and |
552 | | - // close the window |
553 | | - $basename = str_replace( '_', ' ', $basename ); |
554 | | - $output = ' <script type="text/javascript">' . "\n"; |
555 | | - if ( $this->mDelimiter == null ) { |
556 | | - $output .= <<<END |
557 | | - parent.document.getElementById("{$this->mInputID}").value = "$basename"; |
558 | | - |
559 | | -END; |
560 | | - } else { |
561 | | - $output .= <<<END |
562 | | - // if the current value is blank, set it to this file name; |
563 | | - // if it's not blank and ends in a space or delimiter, append |
564 | | - // the file name; if it ends with a normal character, append |
565 | | - // both a delimiter and a file name; and add on a delimiter |
566 | | - // at the end in any case |
567 | | - var cur_value = parent.document.getElementById("{$this->mInputID}").value; |
568 | | - if (cur_value === '') { |
569 | | - parent.document.getElementById("{$this->mInputID}").value = "$basename{$this->mDelimiter} "; |
570 | | - } else { |
571 | | - var last_char = cur_value.charAt(cur_value.length - 1); |
572 | | - if (last_char == '{$this->mDelimiter}' || last_char == ' ') { |
573 | | - parent.document.getElementById("{$this->mInputID}").value += "$basename{$this->mDelimiter} "; |
574 | | - } else { |
575 | | - parent.document.getElementById("{$this->mInputID}").value += "{$this->mDelimiter} $basename{$this->mDelimiter} "; |
576 | | - } |
577 | | - } |
578 | | - |
579 | | -END; |
580 | | - } |
581 | | - $output .= <<<END |
582 | | - parent.jQuery.fancybox.close(); |
583 | | - </script> |
584 | | - |
585 | | -END; |
586 | | - $wgOut->addHTML( $output ); |
587 | | - $img = null; // @todo: added to avoid passing a ref to null - should this be defined somewhere? |
588 | | - wfRunHooks( 'UploadComplete', array( &$img ) ); |
589 | | - } |
590 | | - } |
591 | | - |
592 | | - /** |
593 | | - * Do existence checks on a file and produce a warning |
594 | | - * This check is static and can be done pre-upload via AJAX |
595 | | - * Returns an HTML fragment consisting of one or more LI elements if there is a warning |
596 | | - * Returns an empty string if there is no warning |
597 | | - */ |
598 | | - static function getExistsWarning( $file ) { |
599 | | - global $wgUser; |
600 | | - // Check for uppercase extension. We allow these filenames but check if an image |
601 | | - // with lowercase extension exists already |
602 | | - $warning = ''; |
603 | | - |
604 | | - if ( strpos( $file->getName(), '.' ) == false ) { |
605 | | - $partname = $file->getName(); |
606 | | - $rawExtension = ''; |
607 | | - } else { |
608 | | - list( $partname, $rawExtension ) = explode( '.', $file->getName(), 2 ); |
609 | | - } |
610 | | - $sk = $wgUser->getSkin(); |
611 | | - |
612 | | - if ( $rawExtension != $file->getExtension() ) { |
613 | | - // We're not using the normalized form of the extension. |
614 | | - // Normal form is lowercase, using most common of alternate |
615 | | - // extensions (eg 'jpg' rather than 'JPEG'). |
616 | | - // |
617 | | - // Check for another file using the normalized form... |
618 | | - $nt_lc = Title::newFromText( $partname . '.' . $file->getExtension() ); |
619 | | - $file_lc = wfLocalFile( $nt_lc ); |
620 | | - } else { |
621 | | - $file_lc = false; |
622 | | - } |
623 | | - |
624 | | - if ( $file->exists() ) { |
625 | | - $dlink = $sk->makeKnownLinkObj( $file->getTitle() ); |
626 | | - if ( $file->allowInlineDisplay() ) { |
627 | | - $dlink2 = $sk->makeImageLinkObj( $file->getTitle(), wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), |
628 | | - $file->getName(), 'right', array(), false, true ); |
629 | | - } elseif ( !$file->allowInlineDisplay() && $file->isSafeFile() ) { |
630 | | - $icon = $file->iconThumb(); |
631 | | - $dlink2 = '<div style="float:right" id="mw-media-icon">' . |
632 | | - $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' . $dlink . '</div>'; |
633 | | - } else { |
634 | | - $dlink2 = ''; |
635 | | - } |
636 | | - |
637 | | - $warning .= '<li>' . wfMsgExt( 'fileexists', 'parseinline', $dlink ) . '</li>' . $dlink2; |
638 | | - |
639 | | - } elseif ( $file_lc && $file_lc->exists() ) { |
640 | | - # Check if image with lowercase extension exists. |
641 | | - # It's not forbidden but in 99% it makes no sense to upload the same filename with uppercase extension |
642 | | - $dlink = $sk->makeKnownLinkObj( $nt_lc ); |
643 | | - if ( $file_lc->allowInlineDisplay() ) { |
644 | | - $dlink2 = $sk->makeImageLinkObj( $nt_lc, wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), |
645 | | - $nt_lc->getText(), 'right', array(), false, true ); |
646 | | - } elseif ( !$file_lc->allowInlineDisplay() && $file_lc->isSafeFile() ) { |
647 | | - $icon = $file_lc->iconThumb(); |
648 | | - $dlink2 = '<div style="float:right" id="mw-media-icon">' . |
649 | | - $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' . $dlink . '</div>'; |
650 | | - } else { |
651 | | - $dlink2 = ''; |
652 | | - } |
653 | | - |
654 | | - $warning .= '<li>' . wfMsgExt( 'fileexists-extension', 'parsemag', $file->getName(), $dlink ) . '</li>' . $dlink2; |
655 | | - |
656 | | - } elseif ( ( substr( $partname , 3, 3 ) == 'px-' || substr( $partname , 2, 3 ) == 'px-' ) |
657 | | - && ereg( "[0-9]{2}" , substr( $partname , 0, 2 ) ) ) |
658 | | - { |
659 | | - # Check for filenames like 50px- or 180px-, these are mostly thumbnails |
660 | | - $nt_thb = Title::newFromText( substr( $partname , strpos( $partname , '-' ) + 1 ) . '.' . $rawExtension ); |
661 | | - $file_thb = wfLocalFile( $nt_thb ); |
662 | | - if ( $file_thb->exists() ) { |
663 | | - # Check if an image without leading '180px-' (or similiar) exists |
664 | | - $dlink = $sk->makeKnownLinkObj( $nt_thb ); |
665 | | - if ( $file_thb->allowInlineDisplay() ) { |
666 | | - $dlink2 = $sk->makeImageLinkObj( $nt_thb, |
667 | | - wfMsgExt( 'fileexists-thumb', 'parseinline', $dlink ), |
668 | | - $nt_thb->getText(), 'right', array(), false, true ); |
669 | | - } elseif ( !$file_thb->allowInlineDisplay() && $file_thb->isSafeFile() ) { |
670 | | - $icon = $file_thb->iconThumb(); |
671 | | - $dlink2 = '<div style="float:right" id="mw-media-icon">' . |
672 | | - $icon->toHtml( array( 'desc-link' => true ) ) . '<br />' . |
673 | | - $dlink . '</div>'; |
674 | | - } else { |
675 | | - $dlink2 = ''; |
676 | | - } |
677 | | - |
678 | | - $warning .= '<li>' . wfMsgExt( 'fileexists-thumbnail-yes', 'parsemag', $dlink ) . |
679 | | - '</li>' . $dlink2; |
680 | | - } else { |
681 | | - # Image w/o '180px-' does not exists, but we do not like these filenames |
682 | | - $warning .= '<li>' . wfMsgExt( 'file-thumbnail-no', 'parseinline' , |
683 | | - substr( $partname , 0, strpos( $partname , '-' ) + 1 ) ) . '</li>'; |
684 | | - } |
685 | | - } |
686 | | - if ( $file->wasDeleted() ) { |
687 | | - # If the file existed before and was deleted, warn the user of this |
688 | | - # Don't bother doing so if the image exists now, however |
689 | | - $ltitle = SpecialPage::getTitleFor( 'Log' ); |
690 | | - $llink = $sk->makeKnownLinkObj( $ltitle, wfMsgHtml( 'sf_deletionlog' ), |
691 | | - 'type=delete&page=' . $file->getTitle()->getPrefixedUrl() ); |
692 | | - $warning .= '<li>' . wfMsgWikiHtml( 'filewasdeleted', $llink ) . '</li>'; |
693 | | - } |
694 | | - return $warning; |
695 | | - } |
696 | | - |
697 | | - static function ajaxGetExistsWarning( $filename ) { |
698 | | - $file = wfFindFile( $filename ); |
699 | | - if ( !$file ) { |
700 | | - // Force local file so we have an object to do further checks against |
701 | | - // if there isn't an exact match... |
702 | | - $file = wfLocalFile( $filename ); |
703 | | - } |
704 | | - $s = ' '; |
705 | | - if ( $file ) { |
706 | | - $warning = self::getExistsWarning( $file ); |
707 | | - if ( $warning !== '' ) { |
708 | | - $s = "<ul>$warning</ul>"; |
709 | | - } |
710 | | - } |
711 | | - return $s; |
712 | | - } |
713 | | - |
714 | | - /** |
715 | | - * Render a preview of a given license for the AJAX preview on upload |
716 | | - * |
717 | | - * @param string $license |
718 | | - * @return string |
719 | | - */ |
720 | | - public static function ajaxGetLicensePreview( $license ) { |
721 | | - global $wgParser, $wgUser; |
722 | | - $text = '{{' . $license . '}}'; |
723 | | - $title = Title::makeTitle( NS_IMAGE, 'Sample.jpg' ); |
724 | | - $options = ParserOptions::newFromUser( $wgUser ); |
725 | | - |
726 | | - // Expand subst: first, then live templates... |
727 | | - $text = $wgParser->preSaveTransform( $text, $title, $wgUser, $options ); |
728 | | - $output = $wgParser->parse( $text, $title, $options ); |
729 | | - |
730 | | - return $output->getText(); |
731 | | - } |
732 | | - |
733 | | - /** |
734 | | - * Stash a file in a temporary directory for later processing |
735 | | - * after the user has confirmed it. |
736 | | - * |
737 | | - * If the user doesn't explicitly cancel or accept, these files |
738 | | - * can accumulate in the temp directory. |
739 | | - * |
740 | | - * @param string $saveName - the destination filename |
741 | | - * @param string $tempName - the source temporary file to save |
742 | | - * @return string - full path the stashed file, or false on failure |
743 | | - * @access private |
744 | | - */ |
745 | | - function saveTempUploadedFile( $saveName, $tempName ) { |
746 | | - $repo = RepoGroup::singleton()->getLocalRepo(); |
747 | | - $status = $repo->storeTemp( $saveName, $tempName ); |
748 | | - if ( !$status->isGood() ) { |
749 | | - $this->showError( $status->getWikiText() ); |
750 | | - return false; |
751 | | - } else { |
752 | | - return $status->value; |
753 | | - } |
754 | | - } |
755 | | - |
756 | | - /** |
757 | | - * Stash a file in a temporary directory for later processing, |
758 | | - * and save the necessary descriptive info into the session. |
759 | | - * Returns a key value which will be passed through a form |
760 | | - * to pick up the path info on a later invocation. |
761 | | - * |
762 | | - * @return int |
763 | | - * @access private |
764 | | - */ |
765 | | - function stashSession() { |
766 | | - $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
767 | | - |
768 | | - if ( !$stash ) { |
769 | | - # Couldn't save the file. |
770 | | - return false; |
771 | | - } |
772 | | - |
773 | | - $key = mt_rand( 0, 0x7fffffff ); |
774 | | - $_SESSION['wsUploadData'][$key] = array( |
775 | | - 'mTempPath' => $stash, |
776 | | - 'mFileSize' => $this->mFileSize, |
777 | | - 'mSrcName' => $this->mSrcName, |
778 | | - 'mFileProps' => $this->mFileProps, |
779 | | - 'version' => self::SESSION_VERSION, |
780 | | - ); |
781 | | - return $key; |
782 | | - } |
783 | | - |
784 | | - /** |
785 | | - * Remove a temporarily kept file stashed by saveTempUploadedFile(). |
786 | | - * @access private |
787 | | - * @return success |
788 | | - */ |
789 | | - function unsaveUploadedFile() { |
790 | | - global $wgOut; |
791 | | - $repo = RepoGroup::singleton()->getLocalRepo(); |
792 | | - $success = $repo->freeTemp( $this->mTempPath ); |
793 | | - if ( ! $success ) { |
794 | | - $wgOut->showFileDeleteError( $this->mTempPath ); |
795 | | - return false; |
796 | | - } else { |
797 | | - return true; |
798 | | - } |
799 | | - } |
800 | | - |
801 | | - /* -------------------------------------------------------------- */ |
802 | | - |
803 | | - /** |
804 | | - * @param string $error as HTML |
805 | | - * @access private |
806 | | - */ |
807 | | - function uploadError( $error ) { |
808 | | - global $wgOut; |
809 | | - $wgOut->addHTML( "<h2>" . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" ); |
810 | | - $wgOut->addHTML( "<span class='error'>{$error}</span>\n" ); |
811 | | - } |
812 | | - |
813 | | - /** |
814 | | - * There's something wrong with this file, not enough to reject it |
815 | | - * totally but we require manual intervention to save it for real. |
816 | | - * Stash it away, then present a form asking to confirm or cancel. |
817 | | - * |
818 | | - * @param string $warning as HTML |
819 | | - * @access private |
820 | | - */ |
821 | | - function uploadWarning( $warning ) { |
822 | | - global $wgOut, $wgContLang; |
823 | | - global $wgUseCopyrightUpload; |
824 | | - |
825 | | - $this->mSessionKey = $this->stashSession(); |
826 | | - if ( !$this->mSessionKey ) { |
827 | | - # Couldn't save file; an error has been displayed so let's go. |
828 | | - return; |
829 | | - } |
830 | | - |
831 | | - $wgOut->addHTML( "<h2>" . wfMsgHtml( 'uploadwarning' ) . "</h2>\n" ); |
832 | | - $wgOut->addHTML( "<ul class='warning'>{$warning}</ul><br />\n" ); |
833 | | - |
834 | | - $save = wfMsgHtml( 'savefile' ); |
835 | | - $reupload = wfMsgHtml( 'reupload' ); |
836 | | - $iw = wfMsgWikiHtml( 'ignorewarning' ); |
837 | | - $reup = wfMsgWikiHtml( 'reuploaddesc' ); |
838 | | - $titleObj = SpecialPage::getTitleFor( 'Upload' ); |
839 | | - $action = htmlspecialchars( $titleObj->getLocalURL( 'action=submit' ) ); |
840 | | - $align1 = $wgContLang->isRTL() ? 'left' : 'right'; |
841 | | - $align2 = $wgContLang->isRTL() ? 'right' : 'left'; |
842 | | - |
843 | | - if ( $wgUseCopyrightUpload ) |
844 | | - { |
845 | | - $copyright = " |
846 | | - <input type='hidden' name='wpUploadCopyStatus' value=\"" . htmlspecialchars( $this->mCopyrightStatus ) . "\" /> |
847 | | - <input type='hidden' name='wpUploadSource' value=\"" . htmlspecialchars( $this->mCopyrightSource ) . "\" /> |
848 | | - "; |
849 | | - } else { |
850 | | - $copyright = ""; |
851 | | - } |
852 | | - |
853 | | - $wgOut->addHTML( " |
854 | | - <form id='uploadwarning' method='post' enctype='multipart/form-data' action='$action'> |
855 | | - <input type='hidden' name='wpIgnoreWarning' value='1' /> |
856 | | - <input type='hidden' name='wpSessionKey' value=\"" . htmlspecialchars( $this->mSessionKey ) . "\" /> |
857 | | - <input type='hidden' name='wpUploadDescription' value=\"" . htmlspecialchars( $this->mComment ) . "\" /> |
858 | | - <input type='hidden' name='wpLicense' value=\"" . htmlspecialchars( $this->mLicense ) . "\" /> |
859 | | - <input type='hidden' name='wpDestFile' value=\"" . htmlspecialchars( $this->mDesiredDestName ) . "\" /> |
860 | | - <input type='hidden' name='wpWatchthis' value=\"" . htmlspecialchars( intval( $this->mWatchthis ) ) . "\" /> |
861 | | - <input type='hidden' name='sfInputID' value=\"" . htmlspecialchars( $this->mInputID ) . "\" /> |
862 | | - <input type='hidden' name='sfDelimiter' value=\"" . htmlspecialchars( $this->mInputID ) . "\" /> |
863 | | - {$copyright} |
864 | | - <table border='0'> |
865 | | - <tr> |
866 | | - <tr> |
867 | | - <td align='$align1'> |
868 | | - <input tabindex='2' type='submit' name='wpUpload' value=\"$save\" /> |
869 | | - </td> |
870 | | - <td align='$align2'>$iw</td> |
871 | | - </tr> |
872 | | - <tr> |
873 | | - <td align='$align1'> |
874 | | - <input tabindex='2' type='submit' name='wpReUpload' value=\"{$reupload}\" /> |
875 | | - </td> |
876 | | - <td align='$align2'>$reup</td> |
877 | | - </tr> |
878 | | - </tr> |
879 | | - </table></form>\n" ); |
880 | | - } |
881 | | - |
882 | | - /** |
883 | | - * Displays the main upload form, optionally with a highlighted |
884 | | - * error message up at the top. |
885 | | - * |
886 | | - * @param string $msg as HTML |
887 | | - * @access private |
888 | | - */ |
889 | | - function mainUploadWindowForm( $msg = '' ) { |
890 | | - global $wgOut, $wgUser, $wgContLang; |
891 | | - global $wgUseCopyrightUpload, $wgUseAjax, $wgAjaxUploadDestCheck, $wgAjaxLicensePreview; |
892 | | - global $wgAllowCopyUploads; |
893 | | - global $wgStylePath, $wgStyleVersion; |
894 | | - |
895 | | - $useAjaxDestCheck = $wgUseAjax && $wgAjaxUploadDestCheck; |
896 | | - $useAjaxLicensePreview = $wgUseAjax && $wgAjaxLicensePreview; |
897 | | - |
898 | | - $adc = wfBoolToStr( $useAjaxDestCheck ); |
899 | | - $alp = wfBoolToStr( $useAjaxLicensePreview ); |
900 | | - |
901 | | - $wgOut->addScript( "<script type=\"text/javascript\"> |
902 | | -wgAjaxUploadDestCheck = {$adc}; |
903 | | -wgAjaxLicensePreview = {$alp}; |
904 | | -</script> |
905 | | -<script type=\"text/javascript\" src=\"{$wgStylePath}/common/upload.js?{$wgStyleVersion}\"></script> |
906 | | - " ); |
907 | | - |
908 | | - if ( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) |
909 | | - { |
910 | | - wfDebug( "Hook 'UploadForm:initial' broke output of the upload form" ); |
911 | | - return false; |
912 | | - } |
913 | | - |
914 | | - if ( $this->mDesiredDestName && $wgUser->isAllowed( 'deletedhistory' ) ) { |
915 | | - $title = Title::makeTitleSafe( NS_IMAGE, $this->mDesiredDestName ); |
916 | | - if ( $title instanceof Title && ( $count = $title->isDeleted() ) > 0 ) { |
917 | | - $link = wfMsgExt( |
918 | | - $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted', |
919 | | - array( 'parse', 'replaceafter' ), |
920 | | - $wgUser->getSkin()->makeKnownLinkObj( |
921 | | - SpecialPage::getTitleFor( 'Undelete', $title->getPrefixedText() ), |
922 | | - wfMsgHtml( 'restorelink', $count ) |
923 | | - ) |
924 | | - ); |
925 | | - $wgOut->addHTML( "<div id=\"contentSub2\">{$link}</div>" ); |
926 | | - } |
927 | | - } |
928 | | - |
929 | | - // ignore user's settings to get a smaller form |
930 | | - $cols = 45; // intval($wgUser->getOption( 'cols' )); |
931 | | - $ew = $wgUser->getOption( 'editwidth' ); |
932 | | - if ( $ew ) $ew = " style=\"width:100%\""; |
933 | | - else $ew = ''; |
934 | | - |
935 | | - if ( '' != $msg ) { |
936 | | - $sub = wfMsgHtml( 'uploaderror' ); |
937 | | - $wgOut->addHTML( "<h2>{$sub}</h2>\n" . |
938 | | - "<span class='error'>{$msg}</span>\n" ); |
939 | | - } |
940 | | - // the 'uploadtext' message is not displayed in this window, |
941 | | - // because most of it is irrelevant to a form-based upload |
942 | | - // $wgOut->addHTML( '<div id="uploadtext">' ); |
943 | | - // $wgOut->addWikiText( wfMsgNoTrans( 'uploadtext', $this->mDesiredDestName ) ); |
944 | | - // $wgOut->addHTML( '</div>' ); |
945 | | - |
946 | | - $sourcefilename = wfMsgHtml( 'sourcefilename' ); |
947 | | - $destfilename = wfMsgHtml( 'destfilename' ); |
948 | | - $summary = wfMsgExt( 'fileuploadsummary', 'parseinline' ); |
949 | | - |
950 | | - $license = wfMsgExt( 'license', array( 'parseinline' ) ); |
951 | | - $nolicense = wfMsgHtml( 'nolicense' ); |
952 | | - // class changed in MW 1.16 |
953 | | - /* |
954 | | - $realFunction = array( 'Licenses', 'getInputHtml' ); |
955 | | - if ( is_callable( $realFunction ) ) { |
956 | | - $licenses = new Licenses( array() ); |
957 | | - $licenseshtml = $licenses->getInputHtml( null ); |
958 | | - } else { |
959 | | - $licenses = new Licenses(); |
960 | | - $licenseshtml = $licenses->getHtml(); |
961 | | - } |
962 | | - */ |
963 | | - $licenseshtml = ''; |
964 | | - |
965 | | - $ulb = wfMsgHtml( 'uploadbtn' ); |
966 | | - |
967 | | - |
968 | | - $titleObj = SpecialPage::getTitleFor( 'UploadWindow' ); |
969 | | - $action = htmlspecialchars( $titleObj->getLocalURL() ); |
970 | | - |
971 | | - $encDestName = htmlspecialchars( $this->mDesiredDestName ); |
972 | | - |
973 | | - $watchChecked = |
974 | | - ( $wgUser->getOption( 'watchdefault' ) || |
975 | | - ( $wgUser->getOption( 'watchcreations' ) && $this->mDesiredDestName === '' ) ) |
976 | | - ? 'checked="checked"' |
977 | | - : ''; |
978 | | - $warningChecked = $this->mIgnoreWarning ? 'checked' : ''; |
979 | | - |
980 | | - // Prepare form for upload or upload/copy |
981 | | - if ( $wgAllowCopyUploads && $wgUser->isAllowed( 'upload_by_url' ) ) { |
982 | | - $filename_form = |
983 | | - "<input type='radio' id='wpSourceTypeFile' name='wpSourceType' value='file' " . |
984 | | - "onchange='toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\")' checked />" . |
985 | | - "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " . |
986 | | - "onfocus='" . |
987 | | - "toggle_element_activation(\"wpUploadFileURL\",\"wpUploadFile\");" . |
988 | | - "toggle_element_check(\"wpSourceTypeFile\",\"wpSourceTypeURL\")'" . |
989 | | - ( $this->mDesiredDestName ? "":"onchange='fillDestFilename(\"wpUploadFile\")' " ) . "size='40' />" . |
990 | | - wfMsgHTML( 'upload_source_file' ) . "<br />" . |
991 | | - "<input type='radio' id='wpSourceTypeURL' name='wpSourceType' value='web' " . |
992 | | - "onchange='toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\")' />" . |
993 | | - "<input tabindex='1' type='text' name='wpUploadFileURL' id='wpUploadFileURL' " . |
994 | | - "onfocus='" . |
995 | | - "toggle_element_activation(\"wpUploadFile\",\"wpUploadFileURL\");" . |
996 | | - "toggle_element_check(\"wpSourceTypeURL\",\"wpSourceTypeFile\")'" . |
997 | | - ( $this->mDesiredDestName ? "":"onchange='fillDestFilename(\"wpUploadFileURL\")' " ) . "size='40' DISABLED />" . |
998 | | - wfMsgHtml( 'upload_source_url' ) ; |
999 | | - } else { |
1000 | | - $filename_form = |
1001 | | - "<input tabindex='1' type='file' name='wpUploadFile' id='wpUploadFile' " . |
1002 | | - ( $this->mDesiredDestName ? "":"onchange='fillDestFilename(\"wpUploadFile\")' " ) . |
1003 | | - "size='40' />" . |
1004 | | - "<input type='hidden' name='wpSourceType' value='file' />" ; |
1005 | | - } |
1006 | | - if ( $useAjaxDestCheck ) { |
1007 | | - $warningRow = "<tr><td colspan='2' id='wpDestFile-warning'> </td></tr>"; |
1008 | | - $destOnkeyup = 'onkeyup="wgUploadWarningObj.keypress();"'; |
1009 | | - } else { |
1010 | | - $warningRow = ''; |
1011 | | - $destOnkeyup = ''; |
1012 | | - } |
1013 | | - |
1014 | | - $encComment = htmlspecialchars( $this->mComment ); |
1015 | | - $align1 = $wgContLang->isRTL() ? 'left' : 'right'; |
1016 | | - $align2 = $wgContLang->isRTL() ? 'right' : 'left'; |
1017 | | - |
1018 | | - $wgOut->addHTML( <<<EOT |
1019 | | - <form id='upload' method='post' enctype='multipart/form-data' action="$action"> |
1020 | | - <table border='0'> |
1021 | | - <tr> |
1022 | | - {$this->uploadFormTextTop} |
1023 | | - <td align='$align1' valign='top'><label for='wpUploadFile'>{$sourcefilename}</label></td> |
1024 | | - <td align='$align2'> |
1025 | | - {$filename_form} |
1026 | | - </td> |
1027 | | - </tr> |
1028 | | - <tr> |
1029 | | - <td align='$align1'><label for='wpDestFile'>{$destfilename}</label></td> |
1030 | | - <td align='$align2'> |
1031 | | - <input tabindex='2' type='text' name='wpDestFile' id='wpDestFile' size='40' |
1032 | | - value="$encDestName" $destOnkeyup /> |
1033 | | - </td> |
1034 | | - </tr> |
1035 | | - <tr> |
1036 | | - <td align='$align1'><label for='wpUploadDescription'>{$summary}</label></td> |
1037 | | - <td align='$align2'> |
1038 | | - <textarea tabindex='3' name='wpUploadDescription' id='wpUploadDescription' rows='4' |
1039 | | - cols='{$cols}'{$ew}>$encComment</textarea> |
1040 | | - {$this->uploadFormTextAfterSummary} |
1041 | | - </td> |
1042 | | - </tr> |
1043 | | - <tr> |
1044 | | -EOT |
1045 | | - ); |
1046 | | - |
1047 | | - if ( $licenseshtml !== '' ) { |
1048 | | - $wgOut->addHTML( " |
1049 | | - <td align='$align1'><label for='wpLicense'>$license</label></td> |
1050 | | - <td align='$align2'> |
1051 | | - <select name='wpLicense' id='wpLicense' tabindex='4' |
1052 | | - onchange='licenseSelectorCheck()'> |
1053 | | - <option value=''>$nolicense</option> |
1054 | | - $licenseshtml |
1055 | | - </select> |
1056 | | - </td> |
1057 | | - </tr> |
1058 | | - <tr>" ); |
1059 | | - if ( $useAjaxLicensePreview ) { |
1060 | | - $wgOut->addHTML( " |
1061 | | - <td></td> |
1062 | | - <td id=\"mw-license-preview\"></td> |
1063 | | - </tr> |
1064 | | - <tr>" ); |
1065 | | - } |
1066 | | - } |
1067 | | - |
1068 | | - if ( $wgUseCopyrightUpload ) { |
1069 | | - $filestatus = wfMsgHtml ( 'filestatus' ); |
1070 | | - $copystatus = htmlspecialchars( $this->mCopyrightStatus ); |
1071 | | - $filesource = wfMsgHtml ( 'filesource' ); |
1072 | | - $uploadsource = htmlspecialchars( $this->mCopyrightSource ); |
1073 | | - |
1074 | | - $wgOut->addHTML( " |
1075 | | - <td align='$align1' nowrap='nowrap'><label for='wpUploadCopyStatus'>$filestatus:</label></td> |
1076 | | - <td><input tabindex='5' type='text' name='wpUploadCopyStatus' id='wpUploadCopyStatus' |
1077 | | - value=\"$copystatus\" size='40' /></td> |
1078 | | - </tr> |
1079 | | - <tr> |
1080 | | - <td align='$align1'><label for='wpUploadCopyStatus'>$filesource:</label></td> |
1081 | | - <td><input tabindex='6' type='text' name='wpUploadSource' id='wpUploadCopyStatus' |
1082 | | - value=\"$uploadsource\" size='40' /></td> |
1083 | | - </tr> |
1084 | | - <tr> |
1085 | | - " ); |
1086 | | - } |
1087 | | - |
1088 | | - $wgOut->addHTML( " |
1089 | | - <td></td> |
1090 | | - <td> |
1091 | | - <input tabindex='7' type='checkbox' name='wpWatchthis' id='wpWatchthis' $watchChecked value='true' /> |
1092 | | - <label for='wpWatchthis'>" . wfMsgHtml( 'watchthisupload' ) . "</label> |
1093 | | - <input tabindex='8' type='checkbox' name='wpIgnoreWarning' id='wpIgnoreWarning' value='true' $warningChecked/> |
1094 | | - <label for='wpIgnoreWarning'>" . wfMsgHtml( 'ignorewarnings' ) . "</label> |
1095 | | - </td> |
1096 | | - </tr> |
1097 | | - $warningRow |
1098 | | - <tr> |
1099 | | - <td></td> |
1100 | | - <td align='$align2'><input tabindex='9' type='submit' name='wpUpload' value=\"{$ulb}\"" . $wgUser->getSkin()->tooltipAndAccesskey( 'upload' ) . " /></td> |
1101 | | - </tr> |
1102 | | - <tr> |
1103 | | - <td></td> |
1104 | | - <td align='$align2'> |
1105 | | - " ); |
1106 | | - $wgOut->addWikiText( wfMsgForContent( 'edittools' ) ); |
1107 | | - $wgOut->addHTML( " |
1108 | | - </td> |
1109 | | - </tr> |
1110 | | - |
1111 | | - </table> |
1112 | | - <input type='hidden' name='wpDestFileWarningAck' id='wpDestFileWarningAck' value=''/> |
1113 | | - <input type='hidden' name='sfInputID' value=\"" . htmlspecialchars( $this->mInputID ) . "\" /> |
1114 | | - <input type='hidden' name='sfDelimiter' value=\"" . htmlspecialchars( $this->mDelimiter ) . "\" /> |
1115 | | - </form>" ); |
1116 | | - } |
1117 | | - |
1118 | | - /* -------------------------------------------------------------- */ |
1119 | | - |
1120 | | - /** |
1121 | | - * Split a file into a base name and all dot-delimited 'extensions' |
1122 | | - * on the end. Some web server configurations will fall back to |
1123 | | - * earlier pseudo-'extensions' to determine type and execute |
1124 | | - * scripts, so the blacklist needs to check them all. |
1125 | | - * |
1126 | | - * @return array |
1127 | | - */ |
1128 | | - function splitExtensions( $filename ) { |
1129 | | - $bits = explode( '.', $filename ); |
1130 | | - $basename = array_shift( $bits ); |
1131 | | - return array( $basename, $bits ); |
1132 | | - } |
1133 | | - |
1134 | | - /** |
1135 | | - * Perform case-insensitive match against a list of file extensions. |
1136 | | - * Returns true if the extension is in the list. |
1137 | | - * |
1138 | | - * @param string $ext |
1139 | | - * @param array $list |
1140 | | - * @return bool |
1141 | | - */ |
1142 | | - function checkFileExtension( $ext, $list ) { |
1143 | | - return in_array( strtolower( $ext ), $list ); |
1144 | | - } |
1145 | | - |
1146 | | - /** |
1147 | | - * Perform case-insensitive match against a list of file extensions. |
1148 | | - * Returns true if any of the extensions are in the list. |
1149 | | - * |
1150 | | - * @param array $ext |
1151 | | - * @param array $list |
1152 | | - * @return bool |
1153 | | - */ |
1154 | | - function checkFileExtensionList( $ext, $list ) { |
1155 | | - foreach ( $ext as $e ) { |
1156 | | - if ( in_array( strtolower( $e ), $list ) ) { |
1157 | | - return true; |
1158 | | - } |
1159 | | - } |
1160 | | - return false; |
1161 | | - } |
1162 | | - |
1163 | | - /** |
1164 | | - * Verifies that it's ok to include the uploaded file |
1165 | | - * |
1166 | | - * @param string $tmpfile the full path of the temporary file to verify |
1167 | | - * @param string $extension The filename extension that the file is to be served with |
1168 | | - * @return mixed true of the file is verified, a WikiError object otherwise. |
1169 | | - */ |
1170 | | - function verify( $tmpfile, $extension ) { |
1171 | | - # magically determine mime type |
1172 | | - $magic =& MimeMagic::singleton(); |
1173 | | - $mime = $magic->guessMimeType( $tmpfile, false ); |
1174 | | - |
1175 | | - # check mime type, if desired |
1176 | | - global $wgVerifyMimeType; |
1177 | | - if ( $wgVerifyMimeType ) { |
1178 | | - |
1179 | | - wfDebug ( "\n\nmime: <$mime> extension: <$extension>\n\n" ); |
1180 | | - # check mime type against file extension |
1181 | | - if ( !$this->verifyExtension( $mime, $extension ) ) { |
1182 | | - return new WikiErrorMsg( 'uploadcorrupt' ); |
1183 | | - } |
1184 | | - |
1185 | | - # check mime type blacklist |
1186 | | - global $wgMimeTypeBlacklist; |
1187 | | - if ( isset( $wgMimeTypeBlacklist ) && !is_null( $wgMimeTypeBlacklist ) |
1188 | | - && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) { |
1189 | | - return new WikiErrorMsg( 'filetype-badmime', htmlspecialchars( $mime ) ); |
1190 | | - } |
1191 | | - } |
1192 | | - |
1193 | | - # check for htmlish code and javascript |
1194 | | - if ( $this->detectScript ( $tmpfile, $mime, $extension ) ) { |
1195 | | - return new WikiErrorMsg( 'uploadscripted' ); |
1196 | | - } |
1197 | | - |
1198 | | - /** |
1199 | | - * Scan the uploaded file for viruses |
1200 | | - */ |
1201 | | - $virus = $this->detectVirus( $tmpfile ); |
1202 | | - if ( $virus ) { |
1203 | | - return new WikiErrorMsg( 'uploadvirus', htmlspecialchars( $virus ) ); |
1204 | | - } |
1205 | | - |
1206 | | - wfDebug( __METHOD__ . ": all clear; passing.\n" ); |
1207 | | - return true; |
1208 | | - } |
1209 | | - |
1210 | | - /** |
1211 | | - * Checks if the mime type of the uploaded file matches the file extension. |
1212 | | - * |
1213 | | - * @param string $mime the mime type of the uploaded file |
1214 | | - * @param string $extension The filename extension that the file is to be served with |
1215 | | - * @return bool |
1216 | | - */ |
1217 | | - function verifyExtension( $mime, $extension ) { |
1218 | | - $magic =& MimeMagic::singleton(); |
1219 | | - |
1220 | | - if ( ! $mime || $mime == 'unknown' || $mime == 'unknown/unknown' ) |
1221 | | - if ( ! $magic->isRecognizableExtension( $extension ) ) { |
1222 | | - wfDebug( __METHOD__ . ": passing file with unknown detected mime type; " . |
1223 | | - "unrecognized extension '$extension', can't verify\n" ); |
1224 | | - return true; |
1225 | | - } else { |
1226 | | - wfDebug( __METHOD__ . ": rejecting file with unknown detected mime type; " . |
1227 | | - "recognized extension '$extension', so probably invalid file\n" ); |
1228 | | - return false; |
1229 | | - } |
1230 | | - |
1231 | | - $match = $magic->isMatchingExtension( $extension, $mime ); |
1232 | | - |
1233 | | - if ( $match === null ) { |
1234 | | - wfDebug( __METHOD__ . ": no file extension known for mime type $mime, passing file\n" ); |
1235 | | - return true; |
1236 | | - } elseif ( $match === true ) { |
1237 | | - wfDebug( __METHOD__ . ": mime type $mime matches extension $extension, passing file\n" ); |
1238 | | - |
1239 | | - # TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it! |
1240 | | - return true; |
1241 | | - |
1242 | | - } else { |
1243 | | - wfDebug( __METHOD__ . ": mime type $mime mismatches file extension $extension, rejecting file\n" ); |
1244 | | - return false; |
1245 | | - } |
1246 | | - } |
1247 | | - |
1248 | | - /** |
1249 | | - * Heuristic for detecting files that *could* contain JavaScript instructions or |
1250 | | - * things that may look like HTML to a browser and are thus |
1251 | | - * potentially harmful. The present implementation will produce false positives in some situations. |
1252 | | - * |
1253 | | - * @param string $file Pathname to the temporary upload file |
1254 | | - * @param string $mime The mime type of the file |
1255 | | - * @param string $extension The extension of the file |
1256 | | - * @return bool true if the file contains something looking like embedded scripts |
1257 | | - */ |
1258 | | - function detectScript( $file, $mime, $extension ) { |
1259 | | - global $wgAllowTitlesInSVG; |
1260 | | - |
1261 | | - # ugly hack: for text files, always look at the entire file. |
1262 | | - # For binarie field, just check the first K. |
1263 | | - |
1264 | | - if ( strpos( $mime, 'text/' ) === 0 ) $chunk = file_get_contents( $file ); |
1265 | | - else { |
1266 | | - $fp = fopen( $file, 'rb' ); |
1267 | | - $chunk = fread( $fp, 1024 ); |
1268 | | - fclose( $fp ); |
1269 | | - } |
1270 | | - |
1271 | | - $chunk = strtolower( $chunk ); |
1272 | | - |
1273 | | - if ( !$chunk ) return false; |
1274 | | - |
1275 | | - # decode from UTF-16 if needed (could be used for obfuscation). |
1276 | | - if ( substr( $chunk, 0, 2 ) == "\xfe\xff" ) $enc = "UTF-16BE"; |
1277 | | - elseif ( substr( $chunk, 0, 2 ) == "\xff\xfe" ) $enc = "UTF-16LE"; |
1278 | | - else $enc = null; |
1279 | | - |
1280 | | - if ( $enc ) $chunk = iconv( $enc, "ASCII//IGNORE", $chunk ); |
1281 | | - |
1282 | | - $chunk = trim( $chunk ); |
1283 | | - |
1284 | | - # FIXME: convert from UTF-16 if necessarry! |
1285 | | - |
1286 | | - wfDebug( "SpecialUpload::detectScript: checking for embedded scripts and HTML stuff\n" ); |
1287 | | - |
1288 | | - # check for HTML doctype |
1289 | | - if ( preg_match( "/<!DOCTYPE *X?HTML/", $chunk ) ) return true; |
1290 | | - |
1291 | | - /** |
1292 | | - * Internet Explorer for Windows performs some really stupid file type |
1293 | | - * autodetection which can cause it to interpret valid image files as HTML |
1294 | | - * and potentially execute JavaScript, creating a cross-site scripting |
1295 | | - * attack vectors. |
1296 | | - * |
1297 | | - * Apple's Safari browser also performs some unsafe file type autodetection |
1298 | | - * which can cause legitimate files to be interpreted as HTML if the |
1299 | | - * web server is not correctly configured to send the right content-type |
1300 | | - * (or if you're really uploading plain text and octet streams!) |
1301 | | - * |
1302 | | - * Returns true if IE is likely to mistake the given file for HTML. |
1303 | | - * Also returns true if Safari would mistake the given file for HTML |
1304 | | - * when served with a generic content-type. |
1305 | | - */ |
1306 | | - |
1307 | | - $tags = array( |
1308 | | - '<body', |
1309 | | - '<head', |
1310 | | - '<html', # also in safari |
1311 | | - '<img', |
1312 | | - '<pre', |
1313 | | - '<script', # also in safari |
1314 | | - '<table' |
1315 | | - ); |
1316 | | - if ( ! $wgAllowTitlesInSVG && $extension !== 'svg' && $mime !== 'image/svg' ) { |
1317 | | - $tags[] = '<title'; |
1318 | | - } |
1319 | | - |
1320 | | - foreach ( $tags as $tag ) { |
1321 | | - if ( false !== strpos( $chunk, $tag ) ) { |
1322 | | - return true; |
1323 | | - } |
1324 | | - } |
1325 | | - |
1326 | | - /* |
1327 | | - * look for javascript |
1328 | | - */ |
1329 | | - |
1330 | | - # resolve entity-refs to look at attributes. may be harsh on big files... cache result? |
1331 | | - $chunk = Sanitizer::decodeCharReferences( $chunk ); |
1332 | | - |
1333 | | - # look for script-types |
1334 | | - if ( preg_match( '!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim', $chunk ) ) return true; |
1335 | | - |
1336 | | - # look for html-style script-urls |
1337 | | - if ( preg_match( '!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) return true; |
1338 | | - |
1339 | | - # look for css-style script-urls |
1340 | | - if ( preg_match( '!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) return true; |
1341 | | - |
1342 | | - wfDebug( "SpecialUpload::detectScript: no scripts found\n" ); |
1343 | | - return false; |
1344 | | - } |
1345 | | - |
1346 | | - /** |
1347 | | - * Generic wrapper function for a virus scanner program. |
1348 | | - * This relies on the $wgAntivirus and $wgAntivirusSetup variables. |
1349 | | - * $wgAntivirusRequired may be used to deny upload if the scan fails. |
1350 | | - * |
1351 | | - * @param string $file Pathname to the temporary upload file |
1352 | | - * @return mixed false if not virus is found, NULL if the scan fails or is disabled, |
1353 | | - * or a string containing feedback from the virus scanner if a virus was found. |
1354 | | - * If textual feedback is missing but a virus was found, this function returns true. |
1355 | | - */ |
1356 | | - function detectVirus( $file ) { |
1357 | | - global $wgAntivirus, $wgAntivirusSetup, $wgAntivirusRequired, $wgOut; |
1358 | | - |
1359 | | - if ( !$wgAntivirus ) { |
1360 | | - wfDebug( __METHOD__ . ": virus scanner disabled\n" ); |
1361 | | - return null; |
1362 | | - } |
1363 | | - |
1364 | | - if ( !$wgAntivirusSetup[$wgAntivirus] ) { |
1365 | | - wfDebug( __METHOD__ . ": unknown virus scanner: $wgAntivirus\n" ); |
1366 | | - # @TODO: localise |
1367 | | - $wgOut->addHTML( "<div class='error'>Bad configuration: unknown virus scanner: <i>$wgAntivirus</i></div>\n" ); |
1368 | | - return "unknown antivirus: $wgAntivirus"; |
1369 | | - } |
1370 | | - |
1371 | | - # look up scanner configuration |
1372 | | - $command = $wgAntivirusSetup[$wgAntivirus]["command"]; |
1373 | | - $exitCodeMap = $wgAntivirusSetup[$wgAntivirus]["codemap"]; |
1374 | | - $msgPattern = isset( $wgAntivirusSetup[$wgAntivirus]["messagepattern"] ) ? |
1375 | | - $wgAntivirusSetup[$wgAntivirus]["messagepattern"] : null; |
1376 | | - |
1377 | | - if ( strpos( $command, "%f" ) === false ) { |
1378 | | - # simple pattern: append file to scan |
1379 | | - $command .= " " . wfEscapeShellArg( $file ); |
1380 | | - } else { |
1381 | | - # complex pattern: replace "%f" with file to scan |
1382 | | - $command = str_replace( "%f", wfEscapeShellArg( $file ), $command ); |
1383 | | - } |
1384 | | - |
1385 | | - wfDebug( __METHOD__ . ": running virus scan: $command \n" ); |
1386 | | - |
1387 | | - # execute virus scanner |
1388 | | - $exitCode = false; |
1389 | | - |
1390 | | - # NOTE: there's a 50 line workaround to make stderr redirection work on windows, too. |
1391 | | - # that does not seem to be worth the pain. |
1392 | | - # Ask me (Duesentrieb) about it if it's ever needed. |
1393 | | - $output = array(); |
1394 | | - if ( wfIsWindows() ) { |
1395 | | - exec( "$command", $output, $exitCode ); |
1396 | | - } else { |
1397 | | - exec( "$command 2>&1", $output, $exitCode ); |
1398 | | - } |
1399 | | - |
1400 | | - # map exit code to AV_xxx constants. |
1401 | | - $mappedCode = $exitCode; |
1402 | | - if ( $exitCodeMap ) { |
1403 | | - if ( isset( $exitCodeMap[$exitCode] ) ) { |
1404 | | - $mappedCode = $exitCodeMap[$exitCode]; |
1405 | | - } elseif ( isset( $exitCodeMap["*"] ) ) { |
1406 | | - $mappedCode = $exitCodeMap["*"]; |
1407 | | - } |
1408 | | - } |
1409 | | - |
1410 | | - if ( $mappedCode === AV_SCAN_FAILED ) { |
1411 | | - # scan failed (code was mapped to false by $exitCodeMap) |
1412 | | - wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" ); |
1413 | | - |
1414 | | - if ( $wgAntivirusRequired ) { |
1415 | | - return "scan failed (code $exitCode)"; |
1416 | | - } else { |
1417 | | - return null; |
1418 | | - } |
1419 | | - } elseif ( $mappedCode === AV_SCAN_ABORTED ) { |
1420 | | - # scan failed because filetype is unknown (probably imune) |
1421 | | - wfDebug( __METHOD__ . ": unsupported file type $file (code $exitCode).\n" ); |
1422 | | - return null; |
1423 | | - } elseif ( $mappedCode === AV_NO_VIRUS ) { |
1424 | | - # no virus found |
1425 | | - wfDebug( __METHOD__ . ": file passed virus scan.\n" ); |
1426 | | - return false; |
1427 | | - } else { |
1428 | | - $output = join( "\n", $output ); |
1429 | | - $output = trim( $output ); |
1430 | | - |
1431 | | - if ( !$output ) { |
1432 | | - $output = true; # if there's no output, return true |
1433 | | - } elseif ( $msgPattern ) { |
1434 | | - $groups = array(); |
1435 | | - if ( preg_match( $msgPattern, $output, $groups ) ) { |
1436 | | - if ( $groups[1] ) { |
1437 | | - $output = $groups[1]; |
1438 | | - } |
1439 | | - } |
1440 | | - } |
1441 | | - |
1442 | | - wfDebug( __METHOD__ . ": FOUND VIRUS! scanner feedback: $output" ); |
1443 | | - return $output; |
1444 | | - } |
1445 | | - } |
1446 | | - |
1447 | | - /** |
1448 | | - * If we've modified the upload file we need to manually remove it |
1449 | | - * on exit to clean up. |
1450 | | - * @access private |
1451 | | - */ |
1452 | | - function cleanupTempFile() { |
1453 | | - if ( $this->mRemoveTempFile && file_exists( $this->mTempPath ) ) { |
1454 | | - wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file {$this->mTempPath}\n" ); |
1455 | | - unlink( $this->mTempPath ); |
1456 | | - } |
1457 | | - } |
1458 | | - |
1459 | | - /** |
1460 | | - * Check if there's an overwrite conflict and, if so, if restrictions |
1461 | | - * forbid this user from performing the upload. |
1462 | | - * |
1463 | | - * @return mixed true on success, WikiError on failure |
1464 | | - * @access private |
1465 | | - */ |
1466 | | - function checkOverwrite( $name ) { |
1467 | | - $img = wfFindFile( $name ); |
1468 | | - |
1469 | | - $error = ''; |
1470 | | - if ( $img ) { |
1471 | | - global $wgUser, $wgOut; |
1472 | | - if ( $img->isLocal() ) { |
1473 | | - if ( !self::userCanReUpload( $wgUser, $img->name ) ) { |
1474 | | - $error = 'fileexists-forbidden'; |
1475 | | - } |
1476 | | - } else { |
1477 | | - if ( !$wgUser->isAllowed( 'reupload' ) || |
1478 | | - !$wgUser->isAllowed( 'reupload-shared' ) ) { |
1479 | | - $error = "fileexists-shared-forbidden"; |
1480 | | - } |
1481 | | - } |
1482 | | - } |
1483 | | - |
1484 | | - if ( $error ) { |
1485 | | - $errorText = wfMsg( $error, wfEscapeWikiText( $img->getName() ) ); |
1486 | | - return new WikiError( $wgOut->parse( $errorText ) ); |
1487 | | - } |
1488 | | - |
1489 | | - // Rockin', go ahead and upload |
1490 | | - return true; |
1491 | | - } |
1492 | | - |
1493 | | - /** |
1494 | | - * Check if a user is the last uploader |
1495 | | - * |
1496 | | - * @param User $user |
1497 | | - * @param string $img, image name |
1498 | | - * @return bool |
1499 | | - */ |
1500 | | - public static function userCanReUpload( User $user, $img ) { |
1501 | | - if ( $user->isAllowed( 'reupload' ) ) |
1502 | | - return true; // non-conditional |
1503 | | - if ( !$user->isAllowed( 'reupload-own' ) ) |
1504 | | - return false; |
1505 | | - |
1506 | | - $dbr = wfGetDB( DB_SLAVE ); |
1507 | | - $row = $dbr->selectRow( 'image', |
1508 | | - /* SELECT */ 'img_user', |
1509 | | - /* WHERE */ array( 'img_name' => $img ) |
1510 | | - ); |
1511 | | - if ( !$row ) |
1512 | | - return false; |
1513 | | - |
1514 | | - return $user->getID() == $row->img_user; |
1515 | | - } |
1516 | | - |
1517 | | - /** |
1518 | | - * Display an error with a wikitext description |
1519 | | - */ |
1520 | | - function showError( $description ) { |
1521 | | - global $wgOut; |
1522 | | - $wgOut->setPageTitle( wfMsg( "internalerror" ) ); |
1523 | | - $wgOut->setRobotPolicy( "noindex,nofollow" ); |
1524 | | - $wgOut->setArticleRelated( false ); |
1525 | | - $wgOut->enableClientCache( false ); |
1526 | | - $wgOut->addWikiText( $description ); |
1527 | | - } |
1528 | | - |
1529 | | - /** |
1530 | | - * Get the initial image page text based on a comment and optional file status information |
1531 | | - */ |
1532 | | - static function getInitialPageText( $comment, $license, $copyStatus, $source ) { |
1533 | | - global $wgUseCopyrightUpload; |
1534 | | - if ( $wgUseCopyrightUpload ) { |
1535 | | - if ( $license !== '' ) { |
1536 | | - $licensetxt = '== ' . wfMsgForContent( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
1537 | | - } |
1538 | | - $pageText = '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n" . |
1539 | | - '== ' . wfMsgForContent ( 'filestatus' ) . " ==\n" . $copyStatus . "\n" . |
1540 | | - "$licensetxt" . |
1541 | | - '== ' . wfMsgForContent ( 'filesource' ) . " ==\n" . $source ; |
1542 | | - } else { |
1543 | | - if ( $license !== '' ) { |
1544 | | - $filedesc = $comment === '' ? '' : '== ' . wfMsg ( 'filedesc' ) . " ==\n" . $comment . "\n"; |
1545 | | - $pageText = $filedesc . |
1546 | | - '== ' . wfMsgForContent ( 'license' ) . " ==\n" . '{{' . $license . '}}' . "\n"; |
1547 | | - } else { |
1548 | | - $pageText = $comment; |
1549 | | - } |
1550 | | - } |
1551 | | - return $pageText; |
1552 | | - } |
1553 | | -} |