r107643 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107642‎ | r107643 | r107644 >
Date:18:59, 30 December 2011
Author:yaron
Status:ok
Tags:
Comment:
Removing file - no longer needed, now that SF requires MW 1.16
Modified paths:
  • /trunk/extensions/SemanticForms/specials/SF_UploadWindow.php (deleted) (history)

Diff [purge]

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 = '&#160;';
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'>&#160;</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 -}

Status & tagging log