<?php
/**
* PNG frame counter and metadata extractor.
+ *
* Slightly derived from GIFMetadataExtractor.php
* Deliberately not using MWExceptions to avoid external dependencies, encouraging
* redistribution.
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
* @ingroup Media
*/
throw new Exception( __METHOD__ . ": File $filename does not exist" );
}
- $fh = fopen( $filename, 'r' );
+ $fh = fopen( $filename, 'rb' );
if ( !$fh ) {
throw new Exception( __METHOD__ . ": Unable to open file $filename" );
// Read chunks
while ( !feof( $fh ) ) {
$buf = fread( $fh, 4 );
- if ( !$buf ) {
+ if ( !$buf || strlen( $buf ) < 4 ) {
throw new Exception( __METHOD__ . ": Read error" );
}
$chunk_size = unpack( "N", $buf );
$chunk_size = $chunk_size[1];
+ if ( $chunk_size < 0 ) {
+ throw new Exception( __METHOD__ . ": Chunk size too big for unpack" );
+ }
+
$chunk_type = fread( $fh, 4 );
- if ( !$chunk_type ) {
+ if ( !$chunk_type || strlen( $chunk_type ) < 4 ) {
throw new Exception( __METHOD__ . ": Read error" );
}
if ( $chunk_type == "IHDR" ) {
$buf = self::read( $fh, $chunk_size );
- if ( !$buf ) {
+ if ( !$buf || strlen( $buf ) < $chunk_size ) {
throw new Exception( __METHOD__ . ": Read error" );
}
$bitDepth = ord( substr( $buf, 8, 1 ) );
case 0:
$colorType = 'greyscale';
break;
- case 2:
+ case 2:
$colorType = 'truecolour';
break;
case 3:
}
} elseif ( $chunk_type == "acTL" ) {
$buf = fread( $fh, $chunk_size );
- if( !$buf ) {
+ if( !$buf || strlen( $buf ) < $chunk_size || $chunk_size < 4 ) {
throw new Exception( __METHOD__ . ": Read error" );
}
$loopCount = $actl['plays'];
} elseif ( $chunk_type == "fcTL" ) {
$buf = self::read( $fh, $chunk_size );
- if ( !$buf ) {
+ if ( !$buf || strlen( $buf ) < $chunk_size ) {
throw new Exception( __METHOD__ . ": Read error" );
}
$buf = substr( $buf, 20 );
+ if ( strlen( $buf ) < 4 ) {
+ throw new Exception( __METHOD__ . ": Read error" );
+ }
$fctldur = unpack( "ndelay_num/ndelay_den", $buf );
if ( $fctldur['delay_den'] == 0 ) {
} elseif ( $chunk_type == 'tEXt' ) {
$buf = self::read( $fh, $chunk_size );
+ // In case there is no \x00 which will make explode fail.
+ if ( strpos( $buf, "\x00" ) === false ) {
+ throw new Exception( __METHOD__ . ": Read error on tEXt chunk" );
+ }
+
list( $keyword, $content ) = explode( "\x00", $buf, 2 );
if ( $keyword === '' || $content === '' ) {
throw new Exception( __METHOD__ . ": Read error on tEXt chunk" );
if ( function_exists( 'gzuncompress' ) ) {
$buf = self::read( $fh, $chunk_size );
+ // In case there is no \x00 which will make explode fail.
+ if ( strpos( $buf, "\x00" ) === false ) {
+ throw new Exception( __METHOD__ . ": Read error on zTXt chunk" );
+ }
+
list( $keyword, $postKeyword ) = explode( "\x00", $buf, 2 );
if ( $keyword === '' || $postKeyword === '' ) {
throw new Exception( __METHOD__ . ": Read error on zTXt chunk" );
throw new Exception( __METHOD__ . ": tIME wrong size" );
}
$buf = self::read( $fh, $chunk_size );
- if ( !$buf ) {
+ if ( !$buf || strlen( $buf ) < $chunk_size ) {
throw new Exception( __METHOD__ . ": Read error" );
}
}
$buf = self::read( $fh, $chunk_size );
- if ( !$buf ) {
+ if ( !$buf || strlen( $buf ) < $chunk_size ) {
throw new Exception( __METHOD__ . ": Read error" );
}
$dim = unpack( "Nwidth/Nheight/Cunit", $buf );
if ( $dim['unit'] == 1 ) {
- // unit is meters
- // (as opposed to 0 = undefined )
- $text['XResolution'] = $dim['width']
- . '/100';
- $text['YResolution'] = $dim['height']
- . '/100';
- $text['ResolutionUnit'] = 3;
- // 3 = dots per cm (from Exif).
+ // Need to check for negative because php
+ // doesn't deal with super-large unsigned 32-bit ints well
+ if ( $dim['width'] > 0 && $dim['height'] > 0 ) {
+ // unit is meters
+ // (as opposed to 0 = undefined )
+ $text['XResolution'] = $dim['width']
+ . '/100';
+ $text['YResolution'] = $dim['height']
+ . '/100';
+ $text['ResolutionUnit'] = 3;
+ // 3 = dots per cm (from Exif).
+ }
}
} elseif ( $chunk_type == "IEND" ) {
continue;
}
- // fixme: currently timezones are ignored.
+ // @todo FIXME: Currently timezones are ignored.
// possibly should be wfTimestamp's
// responsibility. (at least for numeric TZ)
$formatted = wfTimestamp( TS_EXIF, $value );