diff --git a/wp-includes/ID3/getid3.lib.php b/wp-includes/ID3/getid3.lib.php index e8d5433672..6cc2bc928a 100644 --- a/wp-includes/ID3/getid3.lib.php +++ b/wp-includes/ID3/getid3.lib.php @@ -11,8 +11,8 @@ // /// ///////////////////////////////////////////////////////////////// -if(!defined('GETID3_LIBXML_OPTIONS') && defined('LIBXML_VERSION')) { - if(LIBXML_VERSION >= 20621) { +if (!defined('GETID3_LIBXML_OPTIONS') && defined('LIBXML_VERSION')) { + if (LIBXML_VERSION >= 20621) { define('GETID3_LIBXML_OPTIONS', LIBXML_NOENT | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_COMPACT); } else { define('GETID3_LIBXML_OPTIONS', LIBXML_NOENT | LIBXML_NONET | LIBXML_NOWARNING); @@ -73,7 +73,8 @@ class getid3_lib /** * @param int|null $variable - * @param int $increment + * @param-out int $variable + * @param int $increment * * @return bool */ @@ -115,7 +116,9 @@ class getid3_lib // check if integers are 64-bit static $hasINT64 = null; if ($hasINT64 === null) { // 10x faster than is_null() - $hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1 + /** @var int|float|object $bigInt */ + $bigInt = pow(2, 31); + $hasINT64 = is_int($bigInt); // 32-bit int are limited to (2^31)-1 if (!$hasINT64 && !defined('PHP_INT_MIN')) { define('PHP_INT_MIN', ~PHP_INT_MAX); } @@ -440,7 +443,7 @@ class getid3_lib } /** - * @param int $number + * @param int|string $number * * @return string */ @@ -744,16 +747,36 @@ class getid3_lib * @return array|false */ public static function XML2array($XMLstring) { - if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) { - // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html - // https://core.trac.wordpress.org/changeset/29378 - // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is - // disabled by default, but is still needed when LIBXML_NOENT is used. - $loader = @libxml_disable_entity_loader(true); - $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', GETID3_LIBXML_OPTIONS); - $return = self::SimpleXMLelement2array($XMLobject); - @libxml_disable_entity_loader($loader); - return $return; + if (function_exists('simplexml_load_string')) { + if (PHP_VERSION_ID < 80000) { + if (function_exists('libxml_disable_entity_loader')) { + // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html + // https://core.trac.wordpress.org/changeset/29378 + // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is + // disabled by default, but is still needed when LIBXML_NOENT is used. + $loader = @libxml_disable_entity_loader(true); + $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', GETID3_LIBXML_OPTIONS); + $return = self::SimpleXMLelement2array($XMLobject); + @libxml_disable_entity_loader($loader); + return $return; + } + } else { + $allow = false; + if (defined('LIBXML_VERSION') && (LIBXML_VERSION >= 20900)) { + // https://www.php.net/manual/en/function.libxml-disable-entity-loader.php + // "as of libxml 2.9.0 entity substitution is disabled by default, so there is no need to disable the loading + // of external entities, unless there is the need to resolve internal entity references with LIBXML_NOENT." + $allow = true; + } elseif (function_exists('libxml_set_external_entity_loader')) { + libxml_set_external_entity_loader(function () { return null; }); // https://www.zend.com/blog/cve-2023-3823 + $allow = true; + } + if ($allow) { + $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', GETID3_LIBXML_OPTIONS); + $return = self::SimpleXMLelement2array($XMLobject); + return $return; + } + } } return false; } @@ -1497,7 +1520,7 @@ class getid3_lib public static function GetDataImageSize($imgData, &$imageinfo=array()) { if (PHP_VERSION_ID >= 50400) { $GetDataImageSize = @getimagesizefromstring($imgData, $imageinfo); - if ($GetDataImageSize === false || !isset($GetDataImageSize[0], $GetDataImageSize[1])) { + if ($GetDataImageSize === false) { return false; } $GetDataImageSize['height'] = $GetDataImageSize[0]; @@ -1525,7 +1548,7 @@ class getid3_lib fwrite($tmp, $imgData); fclose($tmp); $GetDataImageSize = @getimagesize($tempfilename, $imageinfo); - if (($GetDataImageSize === false) || !isset($GetDataImageSize[0]) || !isset($GetDataImageSize[1])) { + if ($GetDataImageSize === false) { return false; } $GetDataImageSize['height'] = $GetDataImageSize[0]; @@ -1719,7 +1742,7 @@ class getid3_lib // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1)); $explodedLine = explode("\t", $line, 2); - $ThisKey = (isset($explodedLine[0]) ? $explodedLine[0] : ''); + $ThisKey = $explodedLine[0]; $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : ''); $cache[$file][$name][$ThisKey] = trim($ThisValue); } diff --git a/wp-includes/ID3/getid3.php b/wp-includes/ID3/getid3.php index 13461c6910..2f827bc775 100644 --- a/wp-includes/ID3/getid3.php +++ b/wp-includes/ID3/getid3.php @@ -387,7 +387,7 @@ class getID3 */ protected $startup_warning = ''; - const VERSION = '1.9.23-202310190849'; + const VERSION = '1.9.24-202509040923'; const FREAD_BUFFER_SIZE = 32768; const ATTACHMENTS_NONE = false; @@ -409,10 +409,10 @@ class getID3 $memoryLimit = ini_get('memory_limit'); if (preg_match('#([0-9]+) ?M#i', $memoryLimit, $matches)) { // could be stored as "16M" rather than 16777216 for example - $memoryLimit = $matches[1] * 1048576; + $memoryLimit = (int) $matches[1] * 1048576; } elseif (preg_match('#([0-9]+) ?G#i', $memoryLimit, $matches)) { // The 'G' modifier is available since PHP 5.1.0 // could be stored as "2G" rather than 2147483648 for example - $memoryLimit = $matches[1] * 1073741824; + $memoryLimit = (int) $matches[1] * 1073741824; } $this->memory_limit = $memoryLimit; @@ -446,7 +446,7 @@ class getID3 } // Check for magic_quotes_gpc if (function_exists('get_magic_quotes_gpc')) { - if (get_magic_quotes_gpc()) { // @phpstan-ignore-line + if (get_magic_quotes_gpc()) { $this->startup_error .= 'magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).'."\n"; } } @@ -529,7 +529,7 @@ class getID3 * @return bool */ public function setOption($optArray) { - if (!is_array($optArray) || empty($optArray)) { + if (empty($optArray)) { return false; } foreach ($optArray as $opt => $val) { @@ -680,6 +680,8 @@ class getID3 catch (getid3_exception $e) { throw $e; } + } else { + $this->warning('skipping check for '.$tag_name.' tags since option_tag_'.$tag_name.'=FALSE'); } } if (isset($this->info['id3v2']['tag_offset_start'])) { @@ -1477,6 +1479,16 @@ class getID3 // Misc other formats + // GPX - data - GPS Exchange Format + 'gpx' => array ( + 'pattern' => '^<\\?xml [^>]+>[\s]* 'misc', + 'module' => 'gpx', + 'mime_type' => 'application/gpx+xml', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + // PAR2 - data - Parity Volume Set Specification 2.0 'par2' => array ( 'pattern' => '^PAR2\\x00PKT', @@ -1890,8 +1902,8 @@ class getID3 // Calculate combined bitrate - audio + video $CombinedBitrate = 0; - $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); - $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); + $CombinedBitrate += (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] != 'free') ? $this->info['audio']['bitrate'] : 0); + $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) { $this->info['bitrate'] = $CombinedBitrate; } @@ -1998,7 +2010,9 @@ class getID3 if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) { return false; } - $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16)); + if ($this->info['audio']['bitrate'] != 'free') { + $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16)); + } if (!empty($this->info['audio']['streams'])) { foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) { diff --git a/wp-includes/ID3/module.audio-video.asf.php b/wp-includes/ID3/module.audio-video.asf.php index 4d3d377ddb..0cda358987 100644 --- a/wp-includes/ID3/module.audio-video.asf.php +++ b/wp-includes/ID3/module.audio-video.asf.php @@ -336,7 +336,7 @@ class getid3_asf extends getid3_handler // shortcut $thisfile_asf['codec_list_object'] = array(); /** @var mixed[] $thisfile_asf_codeclistobject */ - $thisfile_asf_codeclistobject = &$thisfile_asf['codec_list_object']; + $thisfile_asf_codeclistobject = &$thisfile_asf['codec_list_object']; // @phpstan-ignore-line $thisfile_asf_codeclistobject['offset'] = $NextObjectOffset + $offset; $thisfile_asf_codeclistobject['objectid'] = $NextObjectGUID; @@ -499,13 +499,17 @@ class getid3_asf extends getid3_handler $offset += 2; $thisfile_asf_scriptcommandobject['command_types_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; - for ($CommandTypesCounter = 0; $CommandTypesCounter < $thisfile_asf_scriptcommandobject['command_types_count']; $CommandTypesCounter++) { - $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character - $offset += 2; - $thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); - $offset += $CommandTypeNameLength; + if ($thisfile_asf_scriptcommandobject['command_types_count'] > 0) { + $thisfile_asf_scriptcommandobject['command_types'] = array(); + for ($CommandTypesCounter = 0; $CommandTypesCounter < (int) $thisfile_asf_scriptcommandobject['command_types_count']; $CommandTypesCounter++) { + $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter] = array(); + $thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); + $offset += $CommandTypeNameLength; + } } - for ($CommandsCounter = 0; $CommandsCounter < $thisfile_asf_scriptcommandobject['commands_count']; $CommandsCounter++) { + for ($CommandsCounter = 0; $CommandsCounter < (int) $thisfile_asf_scriptcommandobject['commands_count']; $CommandsCounter++) { $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); $offset += 4; $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['type_index'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); @@ -554,6 +558,8 @@ class getid3_asf extends getid3_handler break; } $thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + /** @var int|float|false $totalMakersCount */ + $totalMakersCount = $thisfile_asf_markerobject['markers_count']; $offset += 4; $thisfile_asf_markerobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; @@ -565,7 +571,8 @@ class getid3_asf extends getid3_handler $offset += 2; $thisfile_asf_markerobject['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['name_length']); $offset += $thisfile_asf_markerobject['name_length']; - for ($MarkersCounter = 0; $MarkersCounter < $thisfile_asf_markerobject['markers_count']; $MarkersCounter++) { + for ($MarkersCounter = 0; $MarkersCounter < $totalMakersCount; $MarkersCounter++) { + $thisfile_asf_markerobject['markers'][$MarkersCounter] = array(); $thisfile_asf_markerobject['markers'][$MarkersCounter]['offset'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); $offset += 8; $thisfile_asf_markerobject['markers'][$MarkersCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); @@ -615,7 +622,7 @@ class getid3_asf extends getid3_handler } $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; - for ($StreamNumberCounter = 0; $StreamNumberCounter < $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count']; $StreamNumberCounter++) { + for ($StreamNumberCounter = 0; $StreamNumberCounter < (int) $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count']; $StreamNumberCounter++) { $thisfile_asf_bitratemutualexclusionobject['stream_numbers'][$StreamNumberCounter] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; } @@ -759,7 +766,7 @@ class getid3_asf extends getid3_handler $thisfile_asf_extendedcontentdescriptionobject['objectsize'] = $NextObjectSize; $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; - for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) { + for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < (int) $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) { // shortcut $thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter] = array(); $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter]; @@ -957,7 +964,8 @@ class getid3_asf extends getid3_handler $thisfile_asf_streambitratepropertiesobject['objectsize'] = $NextObjectSize; $thisfile_asf_streambitratepropertiesobject['bitrate_records_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; - for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { + for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < (int) $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { + $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter] = array(); $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); $offset += 2; $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags']['stream_number'] = $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] & 0x007F; @@ -1006,7 +1014,7 @@ class getid3_asf extends getid3_handler if (isset($thisfile_asf_streambitratepropertiesobject['bitrate_records_count'])) { $ASFbitrateAudio = 0; $ASFbitrateVideo = 0; - for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { + for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < (int) $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) { switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) { case 1: @@ -1030,7 +1038,7 @@ class getid3_asf extends getid3_handler $thisfile_video['bitrate'] = $ASFbitrateVideo; } } - if (isset($thisfile_asf['stream_properties_object']) && is_array($thisfile_asf['stream_properties_object'])) { + if (isset($thisfile_asf['stream_properties_object'])) { $thisfile_audio['bitrate'] = 0; $thisfile_video['bitrate'] = 0; @@ -1067,7 +1075,7 @@ class getid3_asf extends getid3_handler } if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { // @phpstan-ignore-line - foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { + foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { // @phpstan-ignore-line if (isset($dataarray['flags']['stream_number']) && ($dataarray['flags']['stream_number'] == $streamnumber)) { $thisfile_asf_audiomedia_currentstream['bitrate'] = $dataarray['bitrate']; $thisfile_audio['bitrate'] += $dataarray['bitrate']; @@ -1153,7 +1161,7 @@ class getid3_asf extends getid3_handler $thisfile_asf_videomedia_currentstream['format_data']['codec_data'] = substr($streamdata['type_specific_data'], $videomediaoffset); if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { // @phpstan-ignore-line - foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { + foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { // @phpstan-ignore-line if (isset($dataarray['flags']['stream_number']) && ($dataarray['flags']['stream_number'] == $streamnumber)) { $thisfile_asf_videomedia_currentstream['bitrate'] = $dataarray['bitrate']; $thisfile_video['streams'][$streamnumber]['bitrate'] = $dataarray['bitrate']; @@ -1266,10 +1274,13 @@ class getid3_asf extends getid3_handler $thisfile_asf_simpleindexobject['maximum_packet_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); $offset += 4; $thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); + /** @var int|float|false $totalIndexEntriesCount */ + $totalIndexEntriesCount = $thisfile_asf_simpleindexobject['index_entries_count']; $offset += 4; - $IndexEntriesData = $SimpleIndexObjectData.$this->fread(6 * $thisfile_asf_simpleindexobject['index_entries_count']); - for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) { + $IndexEntriesData = $SimpleIndexObjectData.$this->fread(6 * $totalIndexEntriesCount); + for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $totalIndexEntriesCount; $IndexEntriesCounter++) { + $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter] = array(); $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); $offset += 4; $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_count'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); @@ -1320,9 +1331,10 @@ class getid3_asf extends getid3_handler $offset += 4; $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count']); - for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < (int) $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { $IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); $offset += 2; + $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter] = array(); $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['stream_number'] = $IndexSpecifierStreamNumber; $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); $offset += 2; @@ -1331,17 +1343,19 @@ class getid3_asf extends getid3_handler $ASFIndexObjectData .= $this->fread(4); $thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + /** @var int|float|false $totalIndexEntryCount */ + $totalIndexEntryCount = $thisfile_asf_asfindexobject['index_entry_count']; $offset += 4; $ASFIndexObjectData .= $this->fread(8 * $thisfile_asf_asfindexobject['index_specifiers_count']); - for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < (int) $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { $thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8)); $offset += 8; } $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); - for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) { - for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + for ($IndexEntryCounter = 0; $IndexEntryCounter < $totalIndexEntryCount; $IndexEntryCounter++) { + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < (int) $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { $thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); $offset += 4; } diff --git a/wp-includes/ID3/module.audio-video.quicktime.php b/wp-includes/ID3/module.audio-video.quicktime.php index c058bbc1c3..301501f703 100644 --- a/wp-includes/ID3/module.audio-video.quicktime.php +++ b/wp-includes/ID3/module.audio-video.quicktime.php @@ -38,12 +38,21 @@ class getid3_quicktime extends getid3_handler */ public $ParseAllPossibleAtoms = false; + /** + * real ugly, but so is the QuickTime structure that stores keys and values in different multi-nested locations that are hard to relate to each other + * https://github.com/JamesHeinrich/getID3/issues/214 + * + * @var int + */ + private $metaDATAkey = 1; + /** * @return bool */ public function Analyze() { $info = &$this->getid3->info; + $this->metaDATAkey = 1; $info['fileformat'] = 'quicktime'; $info['quicktime']['hinting'] = false; $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present @@ -147,27 +156,27 @@ class getid3_quicktime extends getid3_handler @list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = $matches; if (strlen($lat_deg) == 2) { // [+-]DD.D - $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim($lat_deg, '0').$lat_deg_dec); + $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * (float) (ltrim($lat_deg, '0').$lat_deg_dec); } elseif (strlen($lat_deg) == 4) { // [+-]DDMM.M - $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60); + $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * (int) ltrim(substr($lat_deg, 0, 2), '0') + ((float) (ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec) / 60); } elseif (strlen($lat_deg) == 6) { // [+-]DDMMSS.S - $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval((int) ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600); + $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * (int) ltrim(substr($lat_deg, 0, 2), '0') + ((int) ltrim(substr($lat_deg, 2, 2), '0') / 60) + ((float) (ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec) / 3600); } if (strlen($lon_deg) == 3) { // [+-]DDD.D - $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim($lon_deg, '0').$lon_deg_dec); + $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * (float) (ltrim($lon_deg, '0').$lon_deg_dec); } elseif (strlen($lon_deg) == 5) { // [+-]DDDMM.M - $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60); + $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * (int) ltrim(substr($lon_deg, 0, 2), '0') + ((float) (ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec) / 60); } elseif (strlen($lon_deg) == 7) { // [+-]DDDMMSS.S - $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval((int) ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600); + $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * (int) ltrim(substr($lon_deg, 0, 2), '0') + ((int) ltrim(substr($lon_deg, 2, 2), '0') / 60) + ((float) (ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec) / 3600); } if (strlen($alt_deg) == 3) { // [+-]DDD.D - $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim($alt_deg, '0').$alt_deg_dec); + $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * (float) (ltrim($alt_deg, '0').$alt_deg_dec); } elseif (strlen($alt_deg) == 5) { // [+-]DDDMM.M - $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60); + $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * (int) ltrim(substr($alt_deg, 0, 2), '0') + ((float) (ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec) / 60); } elseif (strlen($alt_deg) == 7) { // [+-]DDDMMSS.S - $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval((int) ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600); + $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * (int) ltrim(substr($alt_deg, 0, 2), '0') + ((int) ltrim(substr($alt_deg, 2, 2), '0') / 60) + ((float) (ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec) / 3600); } foreach (array('latitude', 'longitude', 'altitude') as $key) { @@ -215,6 +224,26 @@ class getid3_quicktime extends getid3_handler $info['mime_type'] = 'video/mp4'; } } + if (!empty($info['quicktime']['ftyp']['signature']) && in_array($info['quicktime']['ftyp']['signature'], array('heic','heix','hevc','hevx','heim','heis','hevm','hevs'))) { + if ($info['mime_type'] == 'video/quicktime') { // default value, as we + // https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format +$this->error('HEIF files not currently supported'); + switch ($info['quicktime']['ftyp']['signature']) { + // https://github.com/strukturag/libheif/issues/83 (comment by Dirk Farin 2018-09-14) + case 'heic': // the usual HEIF images + case 'heix': // 10bit images, or anything that uses h265 with range extension + case 'hevc': // brands for image sequences + case 'hevx': // brands for image sequences + case 'heim': // multiview + case 'heis': // scalable + case 'hevm': // multiview sequence + case 'hevs': // scalable sequence + $info['fileformat'] = 'heif'; + $info['mime_type'] = 'image/heif'; + break; + } + } + } if (!$this->ReturnAtomData) { unset($info['quicktime']['moov']); @@ -793,7 +822,7 @@ class getid3_quicktime extends getid3_handler } $stsdEntriesDataOffset = 8; - for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + for ($i = 0; $i < (int) $atom_structure['number_entries']; $i++) { $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4)); $stsdEntriesDataOffset += 4; $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4); @@ -829,17 +858,20 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in // video tracks // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html - $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); - $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); - $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); - $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); - $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); - $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); - $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4)); - $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2)); - $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4); - $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2)); - $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2)); + // https://developer.apple.com/documentation/quicktime-file-format + $STSDvOffset = 8; + $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 4)); $STSDvOffset += 4; + $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 4)); $STSDvOffset += 4; + $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 2)); $STSDvOffset += 2; + $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 2)); $STSDvOffset += 2; + $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 4)); $STSDvOffset += 4; + $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 4)); $STSDvOffset += 4; + $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 4)); $STSDvOffset += 4; + $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 2)); $STSDvOffset += 2; + $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 32) ; $STSDvOffset += 32; + $atom_structure['sample_description_table'][$i]['compressor_name'] = $this->MaybePascal2String(rtrim($atom_structure['sample_description_table'][$i]['compressor_name'], "\x00")); // https://github.com/JamesHeinrich/getID3/issues/452 + $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 2)); $STSDvOffset += 2; + $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], $STSDvOffset, 2)); $STSDvOffset += 2; switch ($atom_structure['sample_description_table'][$i]['data_format']) { case '2vuY': @@ -1641,7 +1673,7 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in @list($all, $latitude, $longitude, $altitude) = $matches; $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude); $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude); - if (!empty($altitude)) { + if (!empty($altitude)) { // @phpstan-ignore-line $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude); } } else { @@ -1721,16 +1753,21 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in break; case 'data': // metaDATA atom - static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data $atom_structure['language'] = substr($atom_data, 4 + 0, 2); $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); $atom_structure['data'] = substr($atom_data, 4 + 4); - $atom_structure['key_name'] = (isset($info['quicktime']['temp_meta_key_names'][$metaDATAkey]) ? $info['quicktime']['temp_meta_key_names'][$metaDATAkey] : ''); - $metaDATAkey++; + $atom_structure['key_name'] = (isset($info['quicktime']['temp_meta_key_names'][$this->metaDATAkey]) ? $info['quicktime']['temp_meta_key_names'][$this->metaDATAkey] : ''); + $this->metaDATAkey++; + + switch ($atom_structure['key_name']) { + case 'com.android.capture.fps': + $atom_structure['data'] = getid3_lib::BigEndian2Float($atom_structure['data']); + break; + } if ($atom_structure['key_name'] && $atom_structure['data']) { - @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data']; + @$info['quicktime']['comments'][str_replace('com.android.', '', str_replace('com.apple.quicktime.', '', $atom_structure['key_name']))][] = $atom_structure['data']; } break; @@ -1971,16 +2008,16 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in foreach (array('latitude','longitude') as $latlon) { preg_match('#^([0-9]{1,3})([0-9]{2}\\.[0-9]+)$#', $GPS_this_GPRMC['raw'][$latlon], $matches); list($dummy, $deg, $min) = $matches; - $GPS_this_GPRMC[$latlon] = $deg + ($min / 60); + $GPS_this_GPRMC[$latlon] = (int) $deg + ((float) $min / 60); } $GPS_this_GPRMC['latitude'] *= (($GPS_this_GPRMC['raw']['latitude_direction'] == 'S') ? -1 : 1); $GPS_this_GPRMC['longitude'] *= (($GPS_this_GPRMC['raw']['longitude_direction'] == 'W') ? -1 : 1); $GPS_this_GPRMC['heading'] = $GPS_this_GPRMC['raw']['angle']; $GPS_this_GPRMC['speed_knot'] = $GPS_this_GPRMC['raw']['knots']; - $GPS_this_GPRMC['speed_kmh'] = $GPS_this_GPRMC['raw']['knots'] * 1.852; + $GPS_this_GPRMC['speed_kmh'] = (float) $GPS_this_GPRMC['raw']['knots'] * 1.852; if ($GPS_this_GPRMC['raw']['variation']) { - $GPS_this_GPRMC['variation'] = $GPS_this_GPRMC['raw']['variation']; + $GPS_this_GPRMC['variation'] = (float) $GPS_this_GPRMC['raw']['variation']; $GPS_this_GPRMC['variation'] *= (($GPS_this_GPRMC['raw']['variation_direction'] == 'W') ? -1 : 1); } @@ -2114,7 +2151,7 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in $atom_structure['ES_DescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1)); $esds_offset += 1; if ($atom_structure['ES_DescrTag'] != 0x03) { - $this->warning('expecting esds.ES_DescrTag = 0x03, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DescrTag']).'), at offset '.$atom_structure['offset']); + $this->warning('expecting esds.ES_DescrTag = 0x03, found 0x'.sprintf('%02X', $atom_structure['ES_DescrTag']).', at offset '.$atom_structure['offset']); break; } $atom_structure['ES_DescrSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset); @@ -2143,7 +2180,7 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in $atom_structure['ES_DecoderConfigDescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1)); $esds_offset += 1; if ($atom_structure['ES_DecoderConfigDescrTag'] != 0x04) { - $this->warning('expecting esds.ES_DecoderConfigDescrTag = 0x04, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DecoderConfigDescrTag']).'), at offset '.$atom_structure['offset']); + $this->warning('expecting esds.ES_DecoderConfigDescrTag = 0x04, found 0x'.sprintf('%02X', $atom_structure['ES_DecoderConfigDescrTag']).', at offset '.$atom_structure['offset']); break; } $atom_structure['ES_DecoderConfigDescrTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset); @@ -2174,7 +2211,7 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in $atom_structure['ES_DecSpecificInfoTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1)); $esds_offset += 1; if ($atom_structure['ES_DecSpecificInfoTag'] != 0x05) { - $this->warning('expecting esds.ES_DecSpecificInfoTag = 0x05, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_DecSpecificInfoTag']).'), at offset '.$atom_structure['offset']); + $this->warning('expecting esds.ES_DecSpecificInfoTag = 0x05, found 0x'.sprintf('%02X', $atom_structure['ES_DecSpecificInfoTag']).', at offset '.$atom_structure['offset']); break; } $atom_structure['ES_DecSpecificInfoTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset); @@ -2185,7 +2222,7 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in $atom_structure['ES_SLConfigDescrTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $esds_offset, 1)); $esds_offset += 1; if ($atom_structure['ES_SLConfigDescrTag'] != 0x06) { - $this->warning('expecting esds.ES_SLConfigDescrTag = 0x05, found 0x'.getid3_lib::PrintHexBytes($atom_structure['ES_SLConfigDescrTag']).'), at offset '.$atom_structure['offset']); + $this->warning('expecting esds.ES_SLConfigDescrTag = 0x05, found 0x'.sprintf('%02X', $atom_structure['ES_SLConfigDescrTag']).', at offset '.$atom_structure['offset']); break; } $atom_structure['ES_SLConfigDescrTagSize'] = $this->quicktime_read_mp4_descr_length($atom_data, $esds_offset); diff --git a/wp-includes/ID3/module.audio-video.riff.php b/wp-includes/ID3/module.audio-video.riff.php index a94ae24d0c..b911e20e1e 100644 --- a/wp-includes/ID3/module.audio-video.riff.php +++ b/wp-includes/ID3/module.audio-video.riff.php @@ -67,12 +67,19 @@ class getid3_riff extends getid3_handler $RIFFsize = substr($RIFFheader, 4, 4); $RIFFsubtype = substr($RIFFheader, 8, 4); - switch ($RIFFtype) { + if ($RIFFsize == "\x00\x00\x00\x00") { + // https://github.com/JamesHeinrich/getID3/issues/468 + // may occur in streaming files where the data size is unknown + $thisfile_riff['header_size'] = $info['avdataend'] - 8; + $this->warning('RIFF size field is empty, assuming the correct value is filesize-8 ('.$thisfile_riff['header_size'].')'); + } else { + $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); + } + switch ($RIFFtype) { case 'FORM': // AIFF, AIFC //$info['fileformat'] = 'aiff'; $this->container = 'aiff'; - $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4)); break; @@ -81,7 +88,6 @@ class getid3_riff extends getid3_handler case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s //$info['fileformat'] = 'riff'; $this->container = 'riff'; - $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); if ($RIFFsubtype == 'RMP3') { // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s $RIFFsubtype = 'WAVE'; @@ -98,7 +104,7 @@ class getid3_riff extends getid3_handler $info['avdataend'] = $info['filesize']; } - $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset + $nextRIFFoffset = (int) $Original['avdataoffset'] + 8 + (int) $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) { try { $this->fseek($nextRIFFoffset); @@ -305,8 +311,9 @@ class getid3_riff extends getid3_handler // assigned for text fields, resulting in a null-terminated string (or possibly just a single null) followed by garbage // Keep only string as far as first null byte, discard rest of fixed-width data // https://github.com/JamesHeinrich/getID3/issues/263 - $null_terminator_offset = strpos($thisfile_riff_WAVE_bext_0[$bext_key], "\x00"); - $thisfile_riff_WAVE_bext_0[$bext_key] = substr($thisfile_riff_WAVE_bext_0[$bext_key], 0, $null_terminator_offset); + // https://github.com/JamesHeinrich/getID3/issues/430 + $null_terminator_rows = explode("\x00", $thisfile_riff_WAVE_bext_0[$bext_key]); + $thisfile_riff_WAVE_bext_0[$bext_key] = $null_terminator_rows[0]; } $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10); @@ -472,7 +479,7 @@ class getid3_riff extends getid3_handler @list($key, $value) = explode(':', $line, 2); if (substr($value, 0, 3) == '[{"') { if ($decoded = @json_decode($value, true)) { - if (!empty($decoded) && (count($decoded) == 1)) { + if (count($decoded) === 1) { $value = $decoded[0]; } else { $value = $decoded; @@ -1132,7 +1139,9 @@ class getid3_riff extends getid3_handler $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); foreach ($CommentsChunkNames as $key => $value) { if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { - $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; + // https://github.com/JamesHeinrich/getID3/issues/430 + $null_terminator_rows = explode("\x00", $thisfile_riff[$RIFFsubtype][$key][0]['data']); + $thisfile_riff['comments'][$value][] = $null_terminator_rows[0]; } } /* @@ -1224,7 +1233,9 @@ class getid3_riff extends getid3_handler $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); foreach ($CommentsChunkNames as $key => $value) { if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { - $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; + // https://github.com/JamesHeinrich/getID3/issues/430 + $null_terminator_rows = explode("\x00", $thisfile_riff[$RIFFsubtype][$key][0]['data']); + $thisfile_riff['comments'][$value][] = $null_terminator_rows[0]; } } @@ -1364,19 +1375,19 @@ class getid3_riff extends getid3_handler } if ($info['playtime_seconds'] > 0) { - if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { + if ($thisfile_riff_audio !== null && $thisfile_riff_video !== null) { if (!isset($info['bitrate'])) { $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); } - } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) { + } elseif ($thisfile_riff_audio !== null && $thisfile_riff_video === null) { // @phpstan-ignore-line if (!isset($thisfile_audio['bitrate'])) { $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); } - } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { + } elseif ($thisfile_riff_audio === null && $thisfile_riff_video !== null) { if (!isset($thisfile_video['bitrate'])) { $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); @@ -1601,9 +1612,18 @@ class getid3_riff extends getid3_handler $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.'); break; } - if (($chunksize == 0) && ($chunkname != 'JUNK')) { - $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.'); - break; + if ($chunksize == 0) { + if ($chunkname == 'JUNK') { + // this is allowed + } elseif ($chunkname == 'data') { + // https://github.com/JamesHeinrich/getID3/issues/468 + // may occur in streaming files where the data size is unknown + $chunksize = $info['avdataend'] - $this->ftell(); + $this->warning('RIFF.data size field is empty, assuming the correct value is filesize-offset ('.$chunksize.')'); + } else { + $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.'); + break; + } } if (($chunksize % 2) != 0) { // all structures are packed on word boundaries @@ -1693,7 +1713,7 @@ class getid3_riff extends getid3_handler break; } $thisindex = 0; - if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { + if (isset($RIFFchunk[$chunkname])) { $thisindex = count($RIFFchunk[$chunkname]); } $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8; diff --git a/wp-includes/ID3/module.audio.mp3.php b/wp-includes/ID3/module.audio.mp3.php index 0d8fee3e5d..11fbbde2cc 100644 --- a/wp-includes/ID3/module.audio.mp3.php +++ b/wp-includes/ID3/module.audio.mp3.php @@ -305,7 +305,11 @@ class getid3_mp3 extends getid3_handler } elseif (!empty($info['audio']['bitrate'])) { if ($info['audio']['bitrate_mode'] == 'cbr') { - $encoder_options = strtoupper($info['audio']['bitrate_mode']).round($info['audio']['bitrate'] / 1000); + if ($info['audio']['bitrate'] == 'free') { + $encoder_options = strtoupper($info['audio']['bitrate_mode']); + } else { + $encoder_options = strtoupper($info['audio']['bitrate_mode']).round($info['audio']['bitrate'] / 1000); + } } else { $encoder_options = strtoupper($info['audio']['bitrate_mode']); } @@ -315,7 +319,7 @@ class getid3_mp3 extends getid3_handler $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min']; } - if (isset($thisfile_mpeg_audio['bitrate']) && $thisfile_mpeg_audio['bitrate'] === 'free') { + if (isset($thisfile_mpeg_audio['bitrate']) && ($thisfile_mpeg_audio['bitrate'] === 'free')) { $encoder_options .= ' --freeformat'; } @@ -712,7 +716,7 @@ class getid3_mp3 extends getid3_handler //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144; } - $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat); + $thisfile_mpeg_audio['framelength'] = (int) floor($framelengthfloat); } if ($thisfile_mpeg_audio['xing_flags']['toc']) { @@ -919,7 +923,7 @@ class getid3_mp3 extends getid3_handler // LAME CBR - if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1 && $thisfile_mpeg_audio['bitrate'] !== 'free') { + if (($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) && ($thisfile_mpeg_audio['bitrate'] !== 'free')) { $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); @@ -1174,7 +1178,6 @@ class getid3_mp3 extends getid3_handler $nextframetestarray = array('error' => array(), 'warning' => array(), 'avdataend' => $info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) { - /** @phpstan-ignore-next-line */ getid3_lib::safe_inc($info['mp3_validity_check_bitrates'][$nextframetestarray['mpeg']['audio']['bitrate']]); if ($ScanAsCBR) { // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header @@ -1186,7 +1189,7 @@ class getid3_mp3 extends getid3_handler // next frame is OK, get ready to check the one after that if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { - $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; + $nextframetestoffset += (int) $nextframetestarray['mpeg']['audio']['framelength']; } else { $this->error('Frame at offset ('.$offset.') is has an invalid frame length.'); return false; @@ -1761,14 +1764,15 @@ class getid3_mp3 extends getid3_handler static $MPEGaudioBitrate; if (empty($MPEGaudioBitrate)) { $MPEGaudioBitrate = array ( - '1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000), - 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000), - 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000) - ), - - '2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000), - 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000), - ) + '1' => array( + 1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000), + 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000), + 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000) + ), + '2' => array( + 1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000), + 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000), + ), ); $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2]; $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2']; diff --git a/wp-includes/ID3/module.audio.ogg.php b/wp-includes/ID3/module.audio.ogg.php index bda166a8a9..ab1e1390d8 100644 --- a/wp-includes/ID3/module.audio.ogg.php +++ b/wp-includes/ID3/module.audio.ogg.php @@ -350,6 +350,12 @@ $this->warning('Ogg Theora (v3) not fully supported in this version of getID3 [' $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0)); $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size())); if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) { + if (substr($LastChunkOfOgg, 13, 8) === "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF") { + // https://github.com/JamesHeinrich/getID3/issues/450 + // "Sometimes, Opus encoders (WhatsApp voice registrations and others) add a special last header with a granule duration of 0xFFFFFFFFFFFFFF. + // This value indicates "this is the end," but must be ignored; otherwise, it makes calculations wrong." + $LastOggSpostion = strpos($LastChunkOfOgg, 'SggO', $LastOggSpostion + 1); + } $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO'))); $info['avdataend'] = $this->ftell(); $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader(); diff --git a/wp-includes/ID3/module.tag.apetag.php b/wp-includes/ID3/module.tag.apetag.php index 1305cfb509..f47e354eb0 100644 --- a/wp-includes/ID3/module.tag.apetag.php +++ b/wp-includes/ID3/module.tag.apetag.php @@ -42,6 +42,10 @@ class getid3_apetag extends getid3_handler $this->warning('Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'); return false; } + if (PHP_INT_MAX == 2147483647) { + // https://github.com/JamesHeinrich/getID3/issues/439 + $this->warning('APEtag flags may not be parsed correctly on 32-bit PHP'); + } $id3v1tagsize = 128; $apetagheadersize = 32; diff --git a/wp-includes/ID3/module.tag.id3v2.php b/wp-includes/ID3/module.tag.id3v2.php index ec448be87b..5748476be8 100644 --- a/wp-includes/ID3/module.tag.id3v2.php +++ b/wp-includes/ID3/module.tag.id3v2.php @@ -659,7 +659,7 @@ class getid3_id3v2 extends getid3_handler // Owner identifier $00 // Identifier $exploded = explode("\x00", $parsedFrame['data'], 2); - $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : ''); + $parsedFrame['ownerid'] = $exploded[0]; $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : ''); } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame @@ -1068,13 +1068,15 @@ class getid3_id3v2 extends getid3_handler $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator)); - if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) { - // timestamp probably omitted for first data item - } else { - $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); - $frame_remainingdata = substr($frame_remainingdata, 4); + if (strlen($frame_remainingdata)) { // https://github.com/JamesHeinrich/getID3/issues/444 + if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) { + // timestamp probably omitted for first data item + } else { + $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); + $frame_remainingdata = substr($frame_remainingdata, 4); + } + $timestampindex++; } - $timestampindex++; } } unset($parsedFrame['data']); @@ -1304,7 +1306,7 @@ class getid3_id3v2 extends getid3_handler // Adjustment $xx (xx ...) $frame_offset = 0; - $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); + $parsedFrame['adjustmentbits'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); diff --git a/wp-includes/ID3/module.tag.lyrics3.php b/wp-includes/ID3/module.tag.lyrics3.php index c8b2cf6305..01f942d005 100644 --- a/wp-includes/ID3/module.tag.lyrics3.php +++ b/wp-includes/ID3/module.tag.lyrics3.php @@ -110,19 +110,23 @@ class getid3_lyrics3 extends getid3_handler if (!isset($info['ape'])) { if (isset($info['lyrics3']['tag_offset_start'])) { $GETID3_ERRORARRAY = &$info['warning']; - getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); - $getid3_temp = new getID3(); - $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp); - $getid3_apetag = new getid3_apetag($getid3_temp); - $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; - $getid3_apetag->Analyze(); - if (!empty($getid3_temp->info['ape'])) { - $info['ape'] = $getid3_temp->info['ape']; + if ($this->getid3->option_tag_apetag) { + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp); + $getid3_apetag = new getid3_apetag($getid3_temp); + $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; + $getid3_apetag->Analyze(); + if (!empty($getid3_temp->info['ape'])) { + $info['ape'] = $getid3_temp->info['ape']; + } + if (!empty($getid3_temp->info['replay_gain'])) { + $info['replay_gain'] = $getid3_temp->info['replay_gain']; + } + unset($getid3_temp, $getid3_apetag); + } else { + $this->warning('Unable to check for Lyrics3 and APE tags interaction since option_tag_apetag=FALSE'); } - if (!empty($getid3_temp->info['replay_gain'])) { - $info['replay_gain'] = $getid3_temp->info['replay_gain']; - } - unset($getid3_temp, $getid3_apetag); } else { $this->warning('Lyrics3 and APE tags appear to have become entangled (most likely due to updating the APE tags with a non-Lyrics3-aware tagger)'); } @@ -227,7 +231,7 @@ class getid3_lyrics3 extends getid3_handler foreach ($imagestrings as $key => $imagestring) { if (strpos($imagestring, '||') !== false) { $imagearray = explode('||', $imagestring); - $ParsedLyrics3['images'][$key]['filename'] = (isset($imagearray[0]) ? $imagearray[0] : ''); + $ParsedLyrics3['images'][$key]['filename'] = $imagearray[0]; $ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : ''); $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : ''); } @@ -272,7 +276,7 @@ class getid3_lyrics3 extends getid3_handler */ public function Lyrics3Timestamp2Seconds($rawtimestamp) { if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) { - return (int) (($regs[1] * 60) + $regs[2]); + return (int) (((int) $regs[1] * 60) + (int) $regs[2]); } return false; } @@ -287,28 +291,28 @@ class getid3_lyrics3 extends getid3_handler $notimestamplyricsarray = array(); foreach ($lyricsarray as $key => $lyricline) { $regs = array(); - unset($thislinetimestamps); + $thislinetimestamps = array(); while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) { $thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]); $lyricline = str_replace($regs[0], '', $lyricline); } $notimestamplyricsarray[$key] = $lyricline; - if (isset($thislinetimestamps) && is_array($thislinetimestamps)) { + if (count($thislinetimestamps) > 0) { sort($thislinetimestamps); foreach ($thislinetimestamps as $timestampkey => $timestamp) { - if (isset($Lyrics3data['synchedlyrics'][$timestamp])) { + if (isset($Lyrics3data['comments']['synchedlyrics'][$timestamp])) { // timestamps only have a 1-second resolution, it's possible that multiple lines // could have the same timestamp, if so, append - $Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline; + $Lyrics3data['comments']['synchedlyrics'][$timestamp] .= "\r\n".$lyricline; } else { - $Lyrics3data['synchedlyrics'][$timestamp] = $lyricline; + $Lyrics3data['comments']['synchedlyrics'][$timestamp] = $lyricline; } } } } - $Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray); - if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) { - ksort($Lyrics3data['synchedlyrics']); + $Lyrics3data['comments']['unsynchedlyrics'][0] = implode("\r\n", $notimestamplyricsarray); + if (isset($Lyrics3data['comments']['synchedlyrics']) && is_array($Lyrics3data['comments']['synchedlyrics'])) { + ksort($Lyrics3data['comments']['synchedlyrics']); } return true; } diff --git a/wp-includes/version.php b/wp-includes/version.php index 039bb23230..cf54599767 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '7.0-alpha-61250'; +$wp_version = '7.0-alpha-61253'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.