r10453 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r10452‎ | r10453 | r10454 >
Date:12:59, 12 August 2005
Author:vibber
Status:old
Tags:
Comment:
* (bug 3076) Support MacBinary-encoded uploads from IE/Mac
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/MacBinary.php (added) (history)
  • /trunk/phase3/includes/SpecialUpload.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/SpecialUpload.php
@@ -9,6 +9,7 @@
1010 *
1111 */
1212 require_once( 'Image.php' );
 13+require_once( 'MacBinary.php' );
1314
1415 /**
1516 * Entry point
@@ -31,7 +32,7 @@
3233 var $mUploadFile, $mUploadDescription, $mIgnoreWarning;
3334 var $mUploadSaveName, $mUploadTempName, $mUploadSize, $mUploadOldVersion;
3435 var $mUploadCopyStatus, $mUploadSource, $mReUpload, $mAction, $mUpload;
35 - var $mOname, $mSessionKey, $mStashed, $mDestFile;
 36+ var $mOname, $mSessionKey, $mStashed, $mDestFile, $mRemoveTempFile;
3637 /**#@-*/
3738
3839 /**
@@ -70,7 +71,8 @@
7172 $this->mUploadTempName = $data['mUploadTempName'];
7273 $this->mUploadSize = $data['mUploadSize'];
7374 $this->mOname = $data['mOname'];
74 - $this->mStashed = true;
 75+ $this->mStashed = true;
 76+ $this->mRemoveTempFile = false;
7577 } else {
7678 /**
7779 *Check for a newly uploaded file.
@@ -80,6 +82,7 @@
8183 $this->mOname = $request->getFileName( 'wpUploadFile' );
8284 $this->mSessionKey = false;
8385 $this->mStashed = false;
 86+ $this->mRemoveTempFile = false; // PHP will handle this
8487 }
8588 }
8689
@@ -121,6 +124,8 @@
122125 } else {
123126 $this->mainUploadForm();
124127 }
 128+
 129+ $this->cleanupTempFile();
125130 }
126131
127132 /* -------------------------------------------------------------- */
@@ -201,7 +206,8 @@
202207 * probably not accept it.
203208 */
204209 if( !$this->mStashed ) {
205 - $veri= $this->verify($this->mUploadTempName, $finalExt);
 210+ $this->checkMacBinary();
 211+ $veri = $this->verify( $this->mUploadTempName, $finalExt );
206212
207213 if( $veri !== true ) { //it's a wiki error...
208214 return $this->uploadError( $veri->toString() );
@@ -254,9 +260,10 @@
255261 * Try actually saving the thing...
256262 * It will show an error form on failure.
257263 */
 264+ $hasBeenMunged = !empty( $this->mSessionKey ) || $this->mRemoveTempFile;
258265 if( $this->saveUploadedFile( $this->mUploadSaveName,
259266 $this->mUploadTempName,
260 - !empty( $this->mSessionKey ) ) ) {
 267+ $hasBeenMunged ) ) {
261268 /**
262269 * Update the upload log and create the description page
263270 * if it's a new file.
@@ -315,27 +322,17 @@
316323 $this->mUploadOldVersion = '';
317324 }
318325
319 - if( $useRename ) {
320 - wfSuppressWarnings();
321 - $success = rename( $tempName, $this->mSavedFile );
322 - wfRestoreWarnings();
 326+ wfSuppressWarnings();
 327+ $success = $useRename
 328+ ? rename( $tempName, $this->mSavedFile )
 329+ : move_uploaded_file( $tempName, $this->mSavedFile );
 330+ wfRestoreWarnings();
323331
324 - if( ! $success ) {
325 - $wgOut->fileCopyError( $tempName, $this->mSavedFile );
326 - return false;
327 - } else {
328 - wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
329 - }
 332+ if( ! $success ) {
 333+ $wgOut->fileCopyError( $tempName, $this->mSavedFile );
 334+ return false;
330335 } else {
331 - wfSuppressWarnings();
332 - $success = move_uploaded_file( $tempName, $this->mSavedFile );
333 - wfRestoreWarnings();
334 -
335 - if( ! $success ) {
336 - $wgOut->fileCopyError( $tempName, $this->mSavedFile );
337 - return false;
338 - }
339 - else wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
 336+ wfDebug("$fname: wrote tempfile $tempName to ".$this->mSavedFile."\n");
340337 }
341338
342339 chmod( $this->mSavedFile, 0644 );
@@ -359,7 +356,10 @@
360357 $archive = wfImageArchiveDir( $saveName, 'temp' );
361358 $stash = $archive . '/' . gmdate( "YmdHis" ) . '!' . $saveName;
362359
363 - if ( !move_uploaded_file( $tempName, $stash ) ) {
 360+ $success = $this->mRemoveTempFile
 361+ ? rename( $tempName, $stash )
 362+ : move_uploaded_file( $tempName, $stash );
 363+ if ( !$success ) {
364364 $wgOut->fileCopyError( $tempName, $stash );
365365 return false;
366366 }
@@ -625,7 +625,7 @@
626626 /**
627627 * Verifies that it's ok to include the uploaded file
628628 *
629 - * @param string $tmpfile the full path opf the temporary file to verify
 629+ * @param string $tmpfile the full path of the temporary file to verify
630630 * @param string $extension The filename extension that the file is to be served with
631631 * @return mixed true of the file is verified, a WikiError object otherwise.
632632 */
@@ -883,7 +883,44 @@
884884 return $output;
885885 }
886886 }
 887+
 888+ /**
 889+ * Check if the temporary file is MacBinary-encoded, as some uploads
 890+ * from Internet Explorer on Mac OS Classic and Mac OS X will be.
 891+ * If so, the data fork will be extracted to a second temporary file,
 892+ * which will then be checked for validity and either kept or discarded.
 893+ *
 894+ * @access private
 895+ */
 896+ function checkMacBinary() {
 897+ $macbin = new MacBinary( $this->mUploadTempName );
 898+ if( $macbin->isValid() ) {
 899+ $dataFile = tempnam( wfTempDir(), "WikiMacBinary" );
 900+ $dataHandle = fopen( $dataFile, 'wb' );
 901+
 902+ wfDebug( "SpecialUpload::checkMacBinary: Extracting MacBinary data fork to $dataFile\n" );
 903+ $macbin->extractData( $dataHandle );
 904+
 905+ $this->mUploadTempName = $dataFile;
 906+ $this->mUploadSize = $macbin->dataForkLength();
 907+
 908+ // We'll have to manually remove the new file if it's not kept.
 909+ $this->mRemoveTempFile = true;
 910+ }
 911+ $macbin->close();
 912+ }
887913
888 -
 914+ /**
 915+ * If we've modified the upload file we need to manually remove it
 916+ * on exit to clean up.
 917+ * @access private
 918+ */
 919+ function cleanupTempFile() {
 920+ if( $this->mRemoveTempFile && file_exists( $this->mUploadTempName ) ) {
 921+ wfDebug( "SpecialUpload::cleanupTempFile: Removing temporary file $this->mUploadTempName\n" );
 922+ unlink( $this->mUploadTempName );
 923+ }
 924+ }
 925+
889926 }
890927 ?>
Index: trunk/phase3/includes/MacBinary.php
@@ -0,0 +1,269 @@
 2+<?php
 3+/**
 4+ * MacBinary signature checker and data fork extractor, for files
 5+ * uploaded from Internet Explorer for Mac.
 6+ *
 7+ * Copyright (C) 2005 Brion Vibber <brion@pobox.com>
 8+ * Portions based on Convert::BinHex by Eryq et al
 9+ * http://www.mediawiki.org/
 10+ *
 11+ * This program is free software; you can redistribute it and/or modify
 12+ * it under the terms of the GNU General Public License as published by
 13+ * the Free Software Foundation; either version 2 of the License, or
 14+ * (at your option) any later version.
 15+ *
 16+ * This program is distributed in the hope that it will be useful,
 17+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 19+ * GNU General Public License for more details.
 20+ *
 21+ * You should have received a copy of the GNU General Public License along
 22+ * with this program; if not, write to the Free Software Foundation, Inc.,
 23+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 24+ * http://www.gnu.org/copyleft/gpl.html
 25+ *
 26+ * @package MediaWiki
 27+ * @subpackage SpecialPage
 28+ */
 29+
 30+class MacBinary {
 31+ function MacBinary( $filename ) {
 32+ $this->open( $filename );
 33+ $this->loadHeader();
 34+ }
 35+
 36+ /**
 37+ * The file must be seekable, such as local filesystem.
 38+ * Remote URLs probably won't work.
 39+ *
 40+ * @param string $filename
 41+ */
 42+ function open( $filename ) {
 43+ $this->valid = false;
 44+ $this->version = 0;
 45+ $this->filename = '';
 46+ $this->dataLength = 0;
 47+ $this->resourceLength = 0;
 48+ $this->handle = fopen( $filename, 'rb' );
 49+ }
 50+
 51+ /**
 52+ * Does this appear to be a valid MacBinary archive?
 53+ * @return bool
 54+ */
 55+ function isValid() {
 56+ return $this->valid;
 57+ }
 58+
 59+ /**
 60+ * Get length of data fork
 61+ * @return int
 62+ */
 63+ function dataForkLength() {
 64+ return $this->dataLength;
 65+ }
 66+
 67+ /**
 68+ * Copy the data fork to an external file or resource.
 69+ * @param resource $destination
 70+ * @return bool
 71+ */
 72+ function extractData( $destination ) {
 73+ if( !$this->isValid() ) {
 74+ return false;
 75+ }
 76+
 77+ // Data fork appears immediately after header
 78+ fseek( $this->handle, 128 );
 79+ return $this->copyBytesTo( $destination, $this->dataLength );
 80+ }
 81+
 82+ /**
 83+ *
 84+ */
 85+ function close() {
 86+ fclose( $this->handle );
 87+ }
 88+
 89+ // --------------------------------------------------------------
 90+
 91+ /**
 92+ * Check if the given file appears to be MacBinary-encoded,
 93+ * as Internet Explorer on Mac OS may provide for unknown types.
 94+ * http://www.lazerware.com/formats/macbinary/macbinary_iii.html
 95+ * If ok, load header data.
 96+ *
 97+ * @return bool
 98+ * @access private
 99+ */
 100+ function loadHeader() {
 101+ $fname = 'MacBinary::loadHeader';
 102+
 103+ fseek( $this->handle, 0 );
 104+ $head = fread( $this->handle, 128 );
 105+ $this->hexdump( $head );
 106+
 107+ if( strlen( $head ) < 128 ) {
 108+ wfDebug( "$fname: couldn't read full MacBinary header\n" );
 109+ return false;
 110+ }
 111+
 112+ if( $head{0} != "\x00" || $head{74} != "\x00" ) {
 113+ wfDebug( "$fname: header bytes 0 and 74 not null\n" );
 114+ return false;
 115+ }
 116+
 117+ $signature = substr( $head, 102, 4 );
 118+ $a = unpack( "ncrc", substr( $head, 124, 2 ) );
 119+ $storedCRC = $a['crc'];
 120+ $calculatedCRC = $this->calcCRC( substr( $head, 0, 124 ) );
 121+ if( $storedCRC == $calculatedCRC ) {
 122+ if( $signature == 'mBIN' ) {
 123+ $this->version = 3;
 124+ } else {
 125+ $this->version = 2;
 126+ }
 127+ } else {
 128+ $crc = sprintf( "%x != %x", $storedCRC, $calculatedCRC );
 129+ if( $storedCRC == 0 && $head{82} == "\x00" &&
 130+ substr( $head, 101, 24 ) == str_repeat( "\x00", 24 ) ) {
 131+ wfDebug( "$fname: no CRC, looks like MacBinary I\n" );
 132+ $this->version = 1;
 133+ } elseif( $signature == 'mBIN' && $storedCRC == 0x185 ) {
 134+ // Mac IE 5.0 seems to insert this value in the CRC field.
 135+ // 5.2.3 works correctly; don't know about other versions.
 136+ wfDebug( "$fname: CRC doesn't match ($crc), looks like Mac IE 5.0\n" );
 137+ $this->version = 3;
 138+ } else {
 139+ wfDebug( "$fname: CRC doesn't match ($crc) and not MacBinary I\n" );
 140+ return false;
 141+ }
 142+ }
 143+
 144+ $nameLength = ord( $head{1} );
 145+ if( $nameLength < 1 || $nameLength > 63 ) {
 146+ wfDebug( "$fname: invalid filename size $nameLength\n" );
 147+ return false;
 148+ }
 149+ $this->filename = substr( $head, 2, $nameLength );
 150+
 151+ $forks = unpack( "Ndata/Nresource", substr( $head, 83, 8 ) );
 152+ $this->dataLength = $forks['data'];
 153+ $this->resourceLength = $forks['resource'];
 154+ $maxForkLength = 0x7fffff;
 155+
 156+ if( $this->dataLength < 0 || $this->dataLength > $maxForkLength ) {
 157+ wfDebug( "$fname: invalid data fork length $this->dataLength\n" );
 158+ return false;
 159+ }
 160+
 161+ if( $this->resourceLength < 0 || $this->resourceLength > $maxForkLength ) {
 162+ wfDebug( "$fname: invalid resource fork size $this->resourceLength\n" );
 163+ return false;
 164+ }
 165+
 166+ wfDebug( "$fname: appears to be MacBinary $this->version, data length $this->dataLength\n" );
 167+ $this->valid = true;
 168+ return true;
 169+ }
 170+
 171+ /**
 172+ * Calculate a 16-bit CRC value as for MacBinary headers.
 173+ * Adapted from perl5 Convert::BinHex by Eryq,
 174+ * based on the mcvert utility (Doug Moore, April '87),
 175+ * with magic array thingy by Jim Van Verth.
 176+ * http://search.cpan.org/~eryq/Convert-BinHex-1.119/lib/Convert/BinHex.pm
 177+ *
 178+ * @param string $data
 179+ * @param int $seed
 180+ * @return int
 181+ * @access private
 182+ */
 183+ function calcCRC( $data, $seed = 0 ) {
 184+ # An array useful for CRC calculations that use 0x1021 as the "seed":
 185+ $MAGIC = array(
 186+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
 187+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
 188+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
 189+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
 190+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
 191+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
 192+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
 193+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
 194+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
 195+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
 196+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
 197+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
 198+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
 199+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
 200+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
 201+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
 202+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
 203+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
 204+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
 205+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
 206+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
 207+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
 208+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
 209+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
 210+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
 211+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
 212+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
 213+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
 214+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
 215+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
 216+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
 217+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
 218+ );
 219+ $len = strlen( $data );
 220+ $crc = $seed;
 221+ for( $i = 0; $i < $len; $i++ ) {
 222+ $crc ^= ord( $data{$i} ) << 8;
 223+ $crc &= 0xFFFF;
 224+ $crc = ($crc << 8) ^ $MAGIC[$crc >> 8];
 225+ $crc &= 0xFFFF;
 226+ }
 227+ return $crc;
 228+ }
 229+
 230+ /**
 231+ * @param resource $destination
 232+ * @param int $bytesToCopy
 233+ * @return bool
 234+ * @access private
 235+ */
 236+ function copyBytesTo( $destination, $bytesToCopy ) {
 237+ $bufferSize = 65536;
 238+ for( $remaining = $bytesToCopy; $remaining > 0; $remaining -= $bufferSize ) {
 239+ $thisChunkSize = min( $remaining, $bufferSize );
 240+ $buffer = fread( $this->handle, $thisChunkSize );
 241+ fwrite( $destination, $buffer );
 242+ }
 243+ }
 244+
 245+ /**
 246+ * Hex dump of the header for debugging
 247+ * @access private
 248+ */
 249+ function hexdump( $data ) {
 250+ global $wgDebugLogFile;
 251+ if( !$wgDebugLogFile ) return;
 252+
 253+ $width = 16;
 254+ $at = 0;
 255+ for( $remaining = strlen( $data ); $remaining > 0; $remaining -= $width ) {
 256+ $line = sprintf( "%04x:", $at );
 257+ $printable = '';
 258+ for( $i = 0; $i < $width; $i++ ) {
 259+ $byte = ord( $data{$at++} );
 260+ $line .= sprintf( " %02x", $byte );
 261+ $printable .= ($byte >= 32 && $byte <= 126 )
 262+ ? chr( $byte )
 263+ : '.';
 264+ }
 265+ wfDebug( "MacBinary: $line $printable\n" );
 266+ }
 267+ }
 268+}
 269+
 270+?>
\ No newline at end of file
Property changes on: trunk/phase3/includes/MacBinary.php
___________________________________________________________________
Added: svn:eol-style
1271 + native
Added: svn:keywords
2272 + Author Date Id Revision
Index: trunk/phase3/RELEASE-NOTES
@@ -17,6 +17,7 @@
1818 * PHP 4.1 compatibility fix: don't use new_link parameter to mysql_connect
1919 if running prior to 4.2.0 as it causes the call to fail
2020 * (bug 3117) Fix display of upload size and type with tidy on
 21+* (bug 3076) Support MacBinary-encoded uploads from IE/Mac
2122
2223
2324 === Caveats ===

Follow-up revisions

RevisionCommit summaryAuthorDate
r89534Fix copypaste error (Not a special page), follow-up (ancient) r10453krinkle21:09, 5 June 2011

Status & tagging log