External Libraries: Update getID3 to version 1.9.20.

A full list of changes in this update can be found on GitHub: https://github.com/JamesHeinrich/getID3/compare/v1.9.19...v1.9.20.

Props hareesh-pillai, desrosj.
Previously [47601-47604].
Fixes #49945.
Built from https://develop.svn.wordpress.org/trunk@48278


git-svn-id: http://core.svn.wordpress.org/trunk@48047 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
desrosj 2020-07-02 15:48:04 +00:00
parent bbe8f41de2
commit f7d2a408d9
17 changed files with 468 additions and 193 deletions

View File

@ -684,7 +684,7 @@ class getid3_lib
$maxkey = false; $maxkey = false;
foreach ($arraydata as $key => $value) { foreach ($arraydata as $key => $value) {
if (!is_array($value)) { if (!is_array($value)) {
if ($value > $maxvalue) { if (($maxvalue === false) || ($value > $maxvalue)) {
$maxvalue = $value; $maxvalue = $value;
$maxkey = $key; $maxkey = $key;
} }
@ -704,7 +704,7 @@ class getid3_lib
$minkey = false; $minkey = false;
foreach ($arraydata as $key => $value) { foreach ($arraydata as $key => $value) {
if (!is_array($value)) { if (!is_array($value)) {
if ($value > $minvalue) { if (($minvalue === false) || ($value < $minvalue)) {
$minvalue = $value; $minvalue = $value;
$minkey = $key; $minkey = $key;
} }
@ -1529,13 +1529,20 @@ class getid3_lib
/** /**
* @param array $ThisFileInfo * @param array $ThisFileInfo
* @param bool $option_tags_html default true (just as in the main getID3 class)
* *
* @return bool * @return bool
*/ */
public static function CopyTagsToComments(&$ThisFileInfo) { public static function CopyTagsToComments(&$ThisFileInfo, $option_tags_html=true) {
// Copy all entries from ['tags'] into common ['comments'] // Copy all entries from ['tags'] into common ['comments']
if (!empty($ThisFileInfo['tags'])) { if (!empty($ThisFileInfo['tags'])) {
if (isset($ThisFileInfo['tags']['id3v1'])) {
// bubble ID3v1 to the end, if present to aid in detecting bad ID3v1 encodings
$ID3v1 = $ThisFileInfo['tags']['id3v1'];
unset($ThisFileInfo['tags']['id3v1']);
$ThisFileInfo['tags']['id3v1'] = $ID3v1;
unset($ID3v1);
}
foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) { foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
foreach ($tagarray as $tagname => $tagdata) { foreach ($tagarray as $tagname => $tagdata) {
foreach ($tagdata as $key => $value) { foreach ($tagdata as $key => $value) {
@ -1554,6 +1561,13 @@ class getid3_lib
break 2; break 2;
} }
} }
if (function_exists('mb_convert_encoding')) {
if (trim($value) == trim(substr(mb_convert_encoding($existingvalue, $ThisFileInfo['id3v1']['encoding'], $ThisFileInfo['encoding']), 0, 30))) {
// value stored in ID3v1 appears to be probably the multibyte value transliterated (badly) into ISO-8859-1 in ID3v1.
// As an example, Foobar2000 will do this if you tag a file with Chinese or Arabic or Cyrillic or something that doesn't fit into ISO-8859-1 the ID3v1 will consist of mostly "?" characters, one per multibyte unrepresentable character
break 2;
}
}
} elseif (!is_array($value)) { } elseif (!is_array($value)) {
@ -1562,7 +1576,6 @@ class getid3_lib
$oldvaluelength = strlen(trim($existingvalue)); $oldvaluelength = strlen(trim($existingvalue));
if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) { if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
$ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
//break 2;
break; break;
} }
} }
@ -1597,7 +1610,8 @@ class getid3_lib
} }
} }
// Copy to ['comments_html'] if ($option_tags_html) {
// Copy ['comments'] to ['comments_html']
if (!empty($ThisFileInfo['comments'])) { if (!empty($ThisFileInfo['comments'])) {
foreach ($ThisFileInfo['comments'] as $field => $values) { foreach ($ThisFileInfo['comments'] as $field => $values) {
if ($field == 'picture') { if ($field == 'picture') {
@ -1614,6 +1628,7 @@ class getid3_lib
} }
} }
} }
}
} }
return true; return true;

View File

@ -99,6 +99,13 @@ class getID3
*/ */
public $encoding_id3v1 = 'ISO-8859-1'; public $encoding_id3v1 = 'ISO-8859-1';
/**
* ID3v1 should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'Windows-1251' or 'KOI8-R'. If true attempt to detect these encodings, but may return incorrect values for some tags actually in ISO-8859-1 encoding
*
* @var bool
*/
public $encoding_id3v1_autodetect = false;
/* /*
* Optional tag checks - disable for speed. * Optional tag checks - disable for speed.
*/ */
@ -250,7 +257,7 @@ class getID3
*/ */
protected $startup_warning = ''; protected $startup_warning = '';
const VERSION = '1.9.19-201912211559'; const VERSION = '1.9.20-202006061653';
const FREAD_BUFFER_SIZE = 32768; const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false; const ATTACHMENTS_NONE = false;
@ -289,24 +296,28 @@ class getID3
$this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.'); $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
} }
if (($mbstring_func_overload = (int) ini_get('mbstring.func_overload')) && ($mbstring_func_overload & 0x02)) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
if (($mbstring_func_overload = (int) ini_get('mbstring.func_overload')) && ($mbstring_func_overload & 0x02)) {
// http://php.net/manual/en/mbstring.overload.php // http://php.net/manual/en/mbstring.overload.php
// "mbstring.func_overload in php.ini is a positive value that represents a combination of bitmasks specifying the categories of functions to be overloaded. It should be set to 1 to overload the mail() function. 2 for string functions, 4 for regular expression functions" // "mbstring.func_overload in php.ini is a positive value that represents a combination of bitmasks specifying the categories of functions to be overloaded. It should be set to 1 to overload the mail() function. 2 for string functions, 4 for regular expression functions"
// getID3 cannot run when string functions are overloaded. It doesn't matter if mail() or ereg* functions are overloaded since getID3 does not use those. // getID3 cannot run when string functions are overloaded. It doesn't matter if mail() or ereg* functions are overloaded since getID3 does not use those.
$this->startup_error .= 'WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", getID3 cannot run with this setting (bitmask 2 (string functions) cannot be set). Recommended to disable entirely.'."\n"; // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated
$this->startup_error .= 'WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", getID3 cannot run with this setting (bitmask 2 (string functions) cannot be set). Recommended to disable entirely.'."\n";
} }
// check for magic quotes in PHP < 7.4.0 (when these functions became deprecated) // check for magic quotes in PHP < 7.4.0 (when these functions became deprecated)
if (version_compare(PHP_VERSION, '7.4.0', '<')) { if (version_compare(PHP_VERSION, '7.4.0', '<')) {
// Check for magic_quotes_runtime // Check for magic_quotes_runtime
if (function_exists('get_magic_quotes_runtime')) { if (function_exists('get_magic_quotes_runtime')) {
if (get_magic_quotes_runtime()) { // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.get_magic_quotes_runtimeDeprecated // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.get_magic_quotes_runtimeDeprecated
if (get_magic_quotes_runtime()) {
$this->startup_error .= 'magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'."\n"; $this->startup_error .= 'magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'."\n";
} }
} }
// Check for magic_quotes_gpc // Check for magic_quotes_gpc
if (function_exists('get_magic_quotes_gpc')) { if (function_exists('get_magic_quotes_gpc')) {
if (get_magic_quotes_gpc()) { // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.get_magic_quotes_gpcDeprecated // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.get_magic_quotes_gpcDeprecated
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"; $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";
} }
} }
@ -846,6 +857,14 @@ class getID3
'mime_type' => 'application/octet-stream', 'mime_type' => 'application/octet-stream',
), ),
// DSDIFF - audio - Direct Stream Digital Interchange File Format
'dsdiff' => array(
'pattern' => '^FRM8',
'group' => 'audio',
'module' => 'dsdiff',
'mime_type' => 'audio/dsd',
),
// DTS - audio - Dolby Theatre System // DTS - audio - Dolby Theatre System
'dts' => array( 'dts' => array(
'pattern' => '^\\x7F\\xFE\\x80\\x01', 'pattern' => '^\\x7F\\xFE\\x80\\x01',
@ -973,6 +992,14 @@ class getID3
'fail_ape' => 'ERROR', 'fail_ape' => 'ERROR',
), ),
// TAK - audio - Tom's lossless Audio Kompressor
'tak' => array(
'pattern' => '^tBaK',
'group' => 'audio',
'module' => 'tak',
'mime_type' => 'application/octet-stream',
),
// TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
'tta' => array( 'tta' => array(
'pattern' => '^TTA', // could also be '^TTA(\\x01|\\x02|\\x03|2|1)' 'pattern' => '^TTA', // could also be '^TTA(\\x01|\\x02|\\x03|2|1)'
@ -1033,6 +1060,14 @@ class getID3
'mime_type' => 'video/x-flv', 'mime_type' => 'video/x-flv',
), ),
// IVF - audio/video - IVF
'ivf' => array(
'pattern' => '^DKIF',
'group' => 'audio-video',
'module' => 'ivf',
'mime_type' => 'video/x-ivf',
),
// MKAV - audio/video - Mastroka // MKAV - audio/video - Mastroka
'matroska' => array( 'matroska' => array(
'pattern' => '^\\x1A\\x45\\xDF\\xA3', 'pattern' => '^\\x1A\\x45\\xDF\\xA3',
@ -1217,12 +1252,22 @@ class getID3
'iconv_req' => false, 'iconv_req' => false,
), ),
// HPK - data - HPK compressed data
'hpk' => array(
'pattern' => '^BPUL',
'group' => 'archive',
'module' => 'hpk',
'mime_type' => 'application/octet-stream',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
),
// RAR - data - RAR compressed data // RAR - data - RAR compressed data
'rar' => array( 'rar' => array(
'pattern' => '^Rar\\!', 'pattern' => '^Rar\\!',
'group' => 'archive', 'group' => 'archive',
'module' => 'rar', 'module' => 'rar',
'mime_type' => 'application/octet-stream', 'mime_type' => 'application/vnd.rar',
'fail_id3' => 'ERROR', 'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR', 'fail_ape' => 'ERROR',
), ),
@ -1424,6 +1469,7 @@ class getID3
'flac' => array('vorbiscomment' , 'UTF-8'), 'flac' => array('vorbiscomment' , 'UTF-8'),
'divxtag' => array('divx' , 'ISO-8859-1'), 'divxtag' => array('divx' , 'ISO-8859-1'),
'iptc' => array('iptc' , 'ISO-8859-1'), 'iptc' => array('iptc' , 'ISO-8859-1'),
'dsdiff' => array('dsdiff' , 'ISO-8859-1'),
); );
} }
@ -1525,6 +1571,17 @@ class getID3
return true; return true;
} }
/**
* Calls getid3_lib::CopyTagsToComments() but passes in the option_tags_html setting from this instance of getID3
*
* @param array $ThisFileInfo
*
* @return bool
*/
public function CopyTagsToComments(&$ThisFileInfo) {
return getid3_lib::CopyTagsToComments($ThisFileInfo, $this->option_tags_html);
}
/** /**
* @param string $algorithm * @param string $algorithm
* *
@ -1560,7 +1617,8 @@ class getID3
// page sequence numbers likely happens for OggSpeex and OggFLAC as well, but // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
// currently vorbiscomment only works on OggVorbis files. // currently vorbiscomment only works on OggVorbis files.
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
$this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)'); $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)');
$this->info[$algorithm.'_data'] = false; $this->info[$algorithm.'_data'] = false;
@ -2053,6 +2111,61 @@ abstract class getid3_handler
return fseek($this->getid3->fp, $bytes, $whence); return fseek($this->getid3->fp, $bytes, $whence);
} }
/**
* @return string|false
*
* @throws getid3_exception
*/
protected function fgets() {
// must be able to handle CR/LF/CRLF but not read more than one lineend
$buffer = ''; // final string we will return
$prevchar = ''; // save previously-read character for end-of-line checking
if ($this->data_string_flag) {
while (true) {
$thischar = substr($this->data_string, $this->data_string_position++, 1);
if (($prevchar == "\r") && ($thischar != "\n")) {
// read one byte too many, back up
$this->data_string_position--;
break;
}
$buffer .= $thischar;
if ($thischar == "\n") {
break;
}
if ($this->data_string_position >= $this->data_string_length) {
// EOF
break;
}
$prevchar = $thischar;
}
} else {
// Ideally we would just use PHP's fgets() function, however...
// it does not behave consistently with regards to mixed line endings, may be system-dependent
// and breaks entirely when given a file with mixed \r vs \n vs \r\n line endings (e.g. some PDFs)
//return fgets($this->getid3->fp);
while (true) {
$thischar = fgetc($this->getid3->fp);
if (($prevchar == "\r") && ($thischar != "\n")) {
// read one byte too many, back up
fseek($this->getid3->fp, -1, SEEK_CUR);
break;
}
$buffer .= $thischar;
if ($thischar == "\n") {
break;
}
if (feof($this->getid3->fp)) {
break;
}
$prevchar = $thischar;
}
}
return $buffer;
}
/** /**
* @return bool * @return bool
*/ */

View File

@ -13,6 +13,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_asf extends getid3_handler class getid3_asf extends getid3_handler

View File

@ -53,6 +53,10 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
define('GETID3_FLV_TAG_AUDIO', 8); define('GETID3_FLV_TAG_AUDIO', 8);
define('GETID3_FLV_TAG_VIDEO', 9); define('GETID3_FLV_TAG_VIDEO', 9);
define('GETID3_FLV_TAG_META', 18); define('GETID3_FLV_TAG_META', 18);

View File

@ -14,6 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation. define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements. define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
@ -329,7 +332,7 @@ class getid3_matroska extends getid3_handler
break;*/ break;*/
} }
$info['video']['streams'][] = $track_info; $info['video']['streams'][$trackarray['TrackUID']] = $track_info;
break; break;
case 2: // Audio case 2: // Audio
@ -362,7 +365,7 @@ class getid3_matroska extends getid3_handler
// create temp instance // create temp instance
$getid3_temp = new getID3(); $getid3_temp = new getID3();
if ($track_info['dataformat'] != 'flac') { if ($track_info['dataformat'] != 'flac') {
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
} }
$getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') { if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
@ -478,7 +481,7 @@ class getid3_matroska extends getid3_handler
break; break;
} }
$info['audio']['streams'][] = $track_info; $info['audio']['streams'][$trackarray['TrackUID']] = $track_info;
break; break;
} }
} }
@ -509,6 +512,30 @@ class getid3_matroska extends getid3_handler
unset($info['mime_type']); unset($info['mime_type']);
} }
// use _STATISTICS_TAGS if available to set audio/video bitrates
if (!empty($info['matroska']['tags'])) {
$_STATISTICS_byTrackUID = array();
foreach ($info['matroska']['tags'] as $key1 => $value1) {
if (!empty($value1['Targets']['TagTrackUID'][0]) && !empty($value1['SimpleTag'])) {
foreach ($value1['SimpleTag'] as $key2 => $value2) {
if (!empty($value2['TagName']) && isset($value2['TagString'])) {
$_STATISTICS_byTrackUID[$value1['Targets']['TagTrackUID'][0]][$value2['TagName']] = $value2['TagString'];
}
}
}
}
foreach (array('audio','video') as $avtype) {
if (!empty($info[$avtype]['streams'])) {
foreach ($info[$avtype]['streams'] as $trackUID => $trackdata) {
if (!isset($trackdata['bitrate']) && !empty($_STATISTICS_byTrackUID[$trackUID]['BPS'])) {
$info[$avtype]['streams'][$trackUID]['bitrate'] = (int) $_STATISTICS_byTrackUID[$trackUID]['BPS'];
@$info[$avtype]['bitrate'] += $info[$avtype]['streams'][$trackUID]['bitrate'];
}
}
}
}
}
return true; return true;
} }
@ -614,8 +641,10 @@ class getid3_matroska extends getid3_handler
while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) { while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
switch ($subelement['id']) { switch ($subelement['id']) {
case EBML_ID_TRACKNUMBER:
case EBML_ID_TRACKUID: case EBML_ID_TRACKUID:
$track_entry[$subelement['id_name']] = getid3_lib::PrintHexBytes($subelement['data'], true, false);
break;
case EBML_ID_TRACKNUMBER:
case EBML_ID_TRACKTYPE: case EBML_ID_TRACKTYPE:
case EBML_ID_MINCACHE: case EBML_ID_MINCACHE:
case EBML_ID_MAXCACHE: case EBML_ID_MAXCACHE:
@ -963,7 +992,7 @@ class getid3_matroska extends getid3_handler
case EBML_ID_TAGEDITIONUID: case EBML_ID_TAGEDITIONUID:
case EBML_ID_TAGCHAPTERUID: case EBML_ID_TAGCHAPTERUID:
case EBML_ID_TAGATTACHMENTUID: case EBML_ID_TAGATTACHMENTUID:
$targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::PrintHexBytes($sub_sub_subelement['data'], true, false);
break; break;
default: default:

View File

@ -15,6 +15,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
@ -55,23 +58,33 @@ class getid3_quicktime extends getid3_handler
$atomsize = getid3_lib::BigEndian2Int($this->fread(8)); $atomsize = getid3_lib::BigEndian2Int($this->fread(8));
} }
if (($offset + $atomsize) > $info['avdataend']) {
$info['quicktime'][$atomname]['name'] = $atomname; $info['quicktime'][$atomname]['name'] = $atomname;
$info['quicktime'][$atomname]['size'] = $atomsize; $info['quicktime'][$atomname]['size'] = $atomsize;
$info['quicktime'][$atomname]['offset'] = $offset; $info['quicktime'][$atomname]['offset'] = $offset;
if (($offset + $atomsize) > $info['avdataend']) {
$this->error('Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)'); $this->error('Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)');
return false; return false;
} }
if ($atomsize == 0) { if ($atomsize == 0) {
// Furthermore, for historical reasons the list of atoms is optionally // Furthermore, for historical reasons the list of atoms is optionally
// terminated by a 32-bit integer set to 0. If you are writing a program // terminated by a 32-bit integer set to 0. If you are writing a program
// to read user data atoms, you should allow for the terminating 0. // to read user data atoms, you should allow for the terminating 0.
$info['quicktime'][$atomname]['name'] = $atomname;
$info['quicktime'][$atomname]['size'] = $atomsize;
$info['quicktime'][$atomname]['offset'] = $offset;
break; break;
} }
$atomHierarchy = array(); $atomHierarchy = array();
$info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms); $parsedAtomData = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
$parsedAtomData['name'] = $atomname;
$parsedAtomData['size'] = $atomsize;
$parsedAtomData['offset'] = $offset;
if (in_array($atomname, array('uuid'))) {
@$info['quicktime'][$atomname][] = $parsedAtomData;
} else {
$info['quicktime'][$atomname] = $parsedAtomData;
}
$offset += $atomsize; $offset += $atomsize;
$atomcounter++; $atomcounter++;
@ -114,7 +127,8 @@ class getid3_quicktime extends getid3_handler
foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) { foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) {
$ISO6709parsed = array('latitude'=>false, 'longitude'=>false, 'altitude'=>false); $ISO6709parsed = array('latitude'=>false, 'longitude'=>false, 'altitude'=>false);
if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) { if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) {
@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; // phpcs:ignore PHPCompatibility.Lists.AssignmentOrder.Affected // phpcs:ignore PHPCompatibility.Lists.AssignmentOrder.Affected
@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 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) * floatval(ltrim($lat_deg, '0').$lat_deg_dec);
@ -143,8 +157,8 @@ class getid3_quicktime extends getid3_handler
foreach (array('latitude', 'longitude', 'altitude') as $key) { foreach (array('latitude', 'longitude', 'altitude') as $key) {
if ($ISO6709parsed[$key] !== false) { if ($ISO6709parsed[$key] !== false) {
$value = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]); $value = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
if (!in_array($value, $info['quicktime']['comments']['gps_'.$key])) { if (!isset($info['quicktime']['comments']['gps_'.$key]) || !in_array($value, $info['quicktime']['comments']['gps_'.$key])) {
$info['quicktime']['comments']['gps_'.$key][] = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]); @$info['quicktime']['comments']['gps_'.$key][] = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
} }
} }
} }
@ -527,6 +541,7 @@ class getid3_quicktime extends getid3_handler
} elseif (preg_match('#^GIF#', $atom_structure['data'])) { } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
$atom_structure['image_mime'] = 'image/gif'; $atom_structure['image_mime'] = 'image/gif';
} }
$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
break; break;
case 'atID': case 'atID':
@ -553,6 +568,7 @@ class getid3_quicktime extends getid3_handler
} elseif (preg_match('#^GIF#', $atom_structure['data'])) { } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
$atom_structure['image_mime'] = 'image/gif'; $atom_structure['image_mime'] = 'image/gif';
} }
$info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
} }
break; break;
@ -756,6 +772,15 @@ class getid3_quicktime extends getid3_handler
$atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
$stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
if (substr($atom_structure['sample_description_table'][$i]['data'], 1, 54) == 'application/octet-stream;type=com.parrot.videometadata') {
// special handling for apparently-malformed (TextMetaDataSampleEntry?) data for some version of Parrot drones
$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['mime_type'] = substr($atom_structure['sample_description_table'][$i]['data'], 1, 55);
$atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['metadata_version'] = (int) substr($atom_structure['sample_description_table'][$i]['data'], 55, 1);
unset($atom_structure['sample_description_table'][$i]['data']);
$this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in this version of getID3() ['.$this->getid3->version().']');
continue;
}
$atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2)); $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
$atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2)); $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
$atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4); $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
@ -1133,7 +1158,7 @@ class getid3_quicktime extends getid3_handler
$atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
$atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
$atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
$atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24)); $atom_structure['component_name'] = $this->MaybePascal2String(substr($atom_data, 24));
if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) { if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
$info['video']['dataformat'] = 'quicktimevr'; $info['video']['dataformat'] = 'quicktimevr';
@ -1164,6 +1189,8 @@ class getid3_quicktime extends getid3_handler
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
$info['comments']['language'][] = $atom_structure['language']; $info['comments']['language'][] = $atom_structure['language'];
} }
$info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
break; break;
@ -1174,6 +1201,7 @@ class getid3_quicktime extends getid3_handler
$atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01 $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
$atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']); $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modification_date_unix'];
break; break;
@ -1271,6 +1299,8 @@ class getid3_quicktime extends getid3_handler
} }
$atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
$atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
$info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
$info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
$info['quicktime']['display_scale'] = $atom_structure['matrix_a']; $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
$info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
@ -1309,6 +1339,8 @@ class getid3_quicktime extends getid3_handler
$atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008); $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
$atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
$atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
$info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
$info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
// https://www.getid3.org/phpBB3/viewtopic.php?t=1908 // https://www.getid3.org/phpBB3/viewtopic.php?t=1908
// attempt to compute rotation from matrix values // attempt to compute rotation from matrix values
@ -1450,7 +1482,7 @@ class getid3_quicktime extends getid3_handler
$info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size']; $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp); $getid3_mp3 = new getid3_mp3($getid3_temp);
@ -1639,18 +1671,36 @@ class getid3_quicktime extends getid3_handler
} }
break; break;
case 'uuid': // Atom holding 360fly spatial data?? case 'uuid': // user-defined atom often seen containing XML data, also used for potentially many other purposes, only a few specifically handled by getID3 (e.g. 360fly spatial data)
/* code in this block by Paul Lewis 2019-Oct-31 */ //Get the UUID ID in first 16 bytes
$uuid_bytes_read = unpack('H8time_low/H4time_mid/H4time_hi/H4clock_seq_hi/H12clock_seq_low', substr($atom_data, 0, 16));
$atom_structure['uuid_field_id'] = implode('-', $uuid_bytes_read);
switch ($atom_structure['uuid_field_id']) { // http://fileformats.archiveteam.org/wiki/Boxes/atoms_format#UUID_boxes
case '0537cdab-9d0c-4431-a72a-fa561f2a113e': // Exif - http://fileformats.archiveteam.org/wiki/Exif
case '2c4c0100-8504-40b9-a03e-562148d6dfeb': // Photoshop Image Resources - http://fileformats.archiveteam.org/wiki/Photoshop_Image_Resources
case '33c7a4d2-b81d-4723-a0ba-f1a3e097ad38': // IPTC-IIM - http://fileformats.archiveteam.org/wiki/IPTC-IIM
case '8974dbce-7be7-4c51-84f9-7148f9882554': // PIFF Track Encryption Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
case '96a9f1f1-dc98-402d-a7ae-d68e34451809': // GeoJP2 World File Box - http://fileformats.archiveteam.org/wiki/GeoJP2
case 'a2394f52-5a9b-4f14-a244-6c427c648df4': // PIFF Sample Encryption Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
case 'b14bf8bd-083d-4b43-a5ae-8cd7d5a6ce03': // GeoJP2 GeoTIFF Box - http://fileformats.archiveteam.org/wiki/GeoJP2
case 'd08a4f18-10f3-4a82-b6c8-32d8aba183d3': // PIFF Protection System Specific Header Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
$this->warning('Unhandled (but recognized) "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)');
break;
case 'be7acfcb-97a9-42e8-9c71-999491e3afac': // XMP data (in XML format)
$atom_structure['xml'] = substr($atom_data, 16, strlen($atom_data) - 16 - 8); // 16 bytes for UUID, 8 bytes header(?)
break;
case 'efe1589a-bb77-49ef-8095-27759eb1dc6f': // 360fly data
/* 360fly code in this block by Paul Lewis 2019-Oct-31 */
/* Sensor Timestamps need to be calculated using the recordings base time at ['quicktime']['moov']['subatoms'][0]['creation_time_unix']. */ /* Sensor Timestamps need to be calculated using the recordings base time at ['quicktime']['moov']['subatoms'][0]['creation_time_unix']. */
$atom_structure['title'] = '360Fly Sensor Data'; $atom_structure['title'] = '360Fly Sensor Data';
//Get the UUID ID in first 16 bytes
$uuid_bytes_read = unpack('H8time_low/H4time_mid/H4time_hi/H4clock_seq_hi/H12clock_seq_low', substr($atom_data, 0, 16));
$atom_structure['uuid_field_id'] = print_r(implode('-', $uuid_bytes_read), true);
//Get the UUID HEADER data //Get the UUID HEADER data
$uuid_bytes_read = unpack('Sheader_size/Sheader_version/Stimescale/Shardware_version/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/', substr($atom_data, 16, 32)); $uuid_bytes_read = unpack('vheader_size/vheader_version/vtimescale/vhardware_version/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/', substr($atom_data, 16, 32));
$atom_structure['uuid_header'] = json_encode($uuid_bytes_read, true); $atom_structure['uuid_header'] = $uuid_bytes_read;
$start_byte = 48; $start_byte = 48;
$atom_SENSOR_data = substr($atom_data, $start_byte); $atom_SENSOR_data = substr($atom_data, $start_byte);
@ -1731,7 +1781,6 @@ class getid3_quicktime extends getid3_handler
$sensor_data_array['speed'] = $uuid_bytes_read['speed']; $sensor_data_array['speed'] = $uuid_bytes_read['speed'];
$sensor_data_array['bearing'] = $uuid_bytes_read['bearing']; $sensor_data_array['bearing'] = $uuid_bytes_read['bearing'];
$sensor_data_array['acc'] = $uuid_bytes_read['acc']; $sensor_data_array['acc'] = $uuid_bytes_read['acc'];
//$sensor_data_array = print_r($uuid_bytes_read, true);
array_push($atom_structure['sensor_data']['data_type']['gps_data'], $sensor_data_array); array_push($atom_structure['sensor_data']['data_type']['gps_data'], $sensor_data_array);
//array_push($debug_structure['debug_items'], $uuid_bytes_read['timestamp']); //array_push($debug_structure['debug_items'], $uuid_bytes_read['timestamp']);
break; break;
@ -1750,11 +1799,16 @@ class getid3_quicktime extends getid3_handler
break; break;
} }
} }
// if (isset($debug_structure['debug_items']) && count($debug_structure['debug_items']) > 0) { //if (isset($debug_structure['debug_items']) && count($debug_structure['debug_items']) > 0) {
// $atom_structure['sensor_data']['data_type']['debug_list'] = implode(',', $debug_structure['debug_items']); // $atom_structure['sensor_data']['data_type']['debug_list'] = implode(',', $debug_structure['debug_items']);
// } else { //} else {
$atom_structure['sensor_data']['data_type']['debug_list'] = 'No debug items in list!'; $atom_structure['sensor_data']['data_type']['debug_list'] = 'No debug items in list!';
// } //}
break;
default:
$this->warning('Unhandled "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)');
}
break; break;
case 'gps ': case 'gps ':
@ -1949,17 +2003,22 @@ class getid3_quicktime extends getid3_handler
case 'thma': // subatom to "frea" -- "ThumbnailImage" case 'thma': // subatom to "frea" -- "ThumbnailImage"
// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
if (strlen($atom_data) > 0) { if (strlen($atom_data) > 0) {
$info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg'); $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'ThumbnailImage');
} }
break; break;
case 'scra': // subatom to "frea" -- "PreviewImage" case 'scra': // subatom to "frea" -- "PreviewImage"
// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
// but the only sample file I've seen has no useful data here // but the only sample file I've seen has no useful data here
if (strlen($atom_data) > 0) { if (strlen($atom_data) > 0) {
$info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg'); $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'PreviewImage');
} }
break; break;
case 'cdsc': // timed metadata reference
// A QuickTime movie can contain none, one, or several timed metadata tracks. Timed metadata tracks can refer to multiple tracks.
// Metadata tracks are linked to the tracks they describe using a track-reference of type 'cdsc'. The metadata track holds the 'cdsc' track reference.
$atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data);
break;
default: default:
$this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset); $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset);
@ -2000,6 +2059,12 @@ class getid3_quicktime extends getid3_handler
} }
return $atom_structure; return $atom_structure;
} }
if (strlen($subatomdata) < ($subatomsize - 8)) {
// we don't have enough data to decode the subatom.
// this may be because we are refusing to parse large subatoms, or it may be because this atom had its size set too large
// so we passed in the start of a following atom incorrectly?
return $atom_structure;
}
$atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); $atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
$subatomoffset += $subatomsize; $subatomoffset += $subatomsize;
} }
@ -2840,19 +2905,8 @@ class getid3_quicktime extends getid3_handler
} }
if ($comment_key) { if ($comment_key) {
if ($comment_key == 'picture') { if ($comment_key == 'picture') {
if (!is_array($data)) { // already copied directly into [comments][picture] elsewhere, do not re-copy here
$image_mime = ''; return true;
if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) {
$image_mime = 'image/png';
} elseif (preg_match('#^\xFF\xD8\xFF#', $data)) {
$image_mime = 'image/jpeg';
} elseif (preg_match('#^GIF#', $data)) {
$image_mime = 'image/gif';
} elseif (preg_match('#^BM#', $data)) {
$image_mime = 'image/bmp';
}
$data = array('data'=>$data, 'image_mime'=>$image_mime);
}
} }
$gooddata = array($data); $gooddata = array($data);
if ($comment_key == 'genre') { if ($comment_key == 'genre') {
@ -2860,7 +2914,7 @@ class getid3_quicktime extends getid3_handler
$gooddata = explode(';', $data); $gooddata = explode(';', $data);
} }
foreach ($gooddata as $data) { foreach ($gooddata as $data) {
if (is_array($data) || (!empty($info['quicktime']['comments'][$comment_key]) && in_array($data, $info['quicktime']['comments'][$comment_key]))) { if (!empty($info['quicktime']['comments'][$comment_key]) && in_array($data, $info['quicktime']['comments'][$comment_key], true)) {
// avoid duplicate copies of identical data // avoid duplicate copies of identical data
continue; continue;
} }
@ -2929,6 +2983,23 @@ class getid3_quicktime extends getid3_handler
return substr($pascalstring, 1); return substr($pascalstring, 1);
} }
/**
* @param string $pascalstring
*
* @return string
*/
public function MaybePascal2String($pascalstring) {
// Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
// Check if string actually is in this format or written incorrectly, straight string, or null-terminated string
if (ord(substr($pascalstring, 0, 1)) == (strlen($pascalstring) - 1)) {
return substr($pascalstring, 1);
} elseif (substr($pascalstring, -1, 1) == "\x00") {
// appears to be null-terminated instead of Pascal-style
return substr($pascalstring, 0, -1);
}
return $pascalstring;
}
/** /**
* Helper functions for m4b audiobook chapters * Helper functions for m4b audiobook chapters

View File

@ -23,6 +23,9 @@
* @todo Rewrite RIFF parser totally * @todo Rewrite RIFF parser totally
*/ */
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);

View File

@ -14,6 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
class getid3_ac3 extends getid3_handler class getid3_ac3 extends getid3_handler
{ {

View File

@ -14,6 +14,9 @@
// // // //
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
/** /**
* @tutorial http://wiki.multimedia.cx/index.php?title=DTS * @tutorial http://wiki.multimedia.cx/index.php?title=DTS

View File

@ -14,7 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
/** /**

View File

@ -14,6 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
// number of frames to scan to determine if MPEG-audio sequence is valid // number of frames to scan to determine if MPEG-audio sequence is valid
// Lower this number to 5-20 for faster scanning // Lower this number to 5-20 for faster scanning

View File

@ -14,6 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
class getid3_ogg extends getid3_handler class getid3_ogg extends getid3_handler

View File

@ -14,6 +14,10 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
class getid3_apetag extends getid3_handler class getid3_apetag extends getid3_handler
{ {
/** /**

View File

@ -14,6 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
class getid3_id3v1 extends getid3_handler class getid3_id3v1 extends getid3_handler
{ {
@ -62,19 +65,22 @@ class getid3_id3v1 extends getid3_handler
foreach ($ParsedID3v1 as $key => $value) { foreach ($ParsedID3v1 as $key => $value) {
$ParsedID3v1['comments'][$key][0] = $value; $ParsedID3v1['comments'][$key][0] = $value;
} }
$ID3v1encoding = $this->getid3->encoding_id3v1;
if ($this->getid3->encoding_id3v1_autodetect) {
// ID3v1 encoding detection hack START // ID3v1 encoding detection hack START
// ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets // ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
// Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess // Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
$ID3v1encoding = 'ISO-8859-1';
foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) { foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
foreach ($valuearray as $key => $value) { foreach ($valuearray as $key => $value) {
if (preg_match('#^[\\x00-\\x40\\xA8\\xB8\\x80-\\xFF]+$#', $value)) { if (preg_match('#^[\\x00-\\x40\\x80-\\xFF]+$#', $value) && !ctype_digit((string) $value)) { // check for strings with only characters above chr(128) and punctuation/numbers, but not just numeric strings (e.g. track numbers or years)
foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) { foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) { if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
$ID3v1encoding = $id3v1_bad_encoding; $ID3v1encoding = $id3v1_bad_encoding;
$this->warning('ID3v1 detected as '.$id3v1_bad_encoding.' text encoding in '.$tag_key);
break 3; break 3;
} elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) { } elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
$ID3v1encoding = $id3v1_bad_encoding; $ID3v1encoding = $id3v1_bad_encoding;
$this->warning('ID3v1 detected as '.$id3v1_bad_encoding.' text encoding in '.$tag_key);
break 3; break 3;
} }
} }
@ -82,6 +88,7 @@ class getid3_id3v1 extends getid3_handler
} }
} }
// ID3v1 encoding detection hack END // ID3v1 encoding detection hack END
}
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
$GoodFormatID3v1tag = $this->GenerateID3v1Tag( $GoodFormatID3v1tag = $this->GenerateID3v1Tag(

View File

@ -14,6 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
class getid3_id3v2 extends getid3_handler class getid3_id3v2 extends getid3_handler
@ -520,14 +523,21 @@ class getid3_id3v2 extends getid3_handler
if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) { if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) {
// note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here: // note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
// replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name // replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
if (preg_match('#/#', $genrestring)) { if (strpos($genrestring, '/') !== false) {
$LegitimateSlashedGenreList = array( // https://github.com/JamesHeinrich/getID3/issues/223
'Pop/Funk', // ID3v1 genre #62 - https://en.wikipedia.org/wiki/ID3#standard
'Cut-up/DJ', // Discogs - https://www.discogs.com/style/cut-up/dj
'RnB/Swing', // Discogs - https://www.discogs.com/style/rnb/swing
'Funk / Soul', // Discogs (note spaces) - https://www.discogs.com/genre/funk+%2F+soul
);
$genrestring = str_replace('/', "\x00", $genrestring); $genrestring = str_replace('/', "\x00", $genrestring);
$genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring); foreach ($LegitimateSlashedGenreList as $SlashedGenre) {
$genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring); $genrestring = str_ireplace(str_replace('/', "\x00", $SlashedGenre), $SlashedGenre, $genrestring);
}
} }
// some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal" // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
if (preg_match('#;#', $genrestring)) { if (strpos($genrestring, ';') !== false) {
$genrestring = str_replace(';', "\x00", $genrestring); $genrestring = str_replace(';', "\x00", $genrestring);
} }
} }

View File

@ -14,7 +14,9 @@
// /// // ///
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
exit;
}
class getid3_lyrics3 extends getid3_handler class getid3_lyrics3 extends getid3_handler
{ {
/** /**
@ -107,7 +109,7 @@ class getid3_lyrics3 extends getid3_handler
$GETID3_ERRORARRAY = &$info['warning']; $GETID3_ERRORARRAY = &$info['warning'];
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
$getid3_temp = new getID3(); $getid3_temp = new getID3();
$getid3_temp->openfile($this->getid3->filename); $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_apetag = new getid3_apetag($getid3_temp); $getid3_apetag = new getid3_apetag($getid3_temp);
$getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
$getid3_apetag->Analyze(); $getid3_apetag->Analyze();

View File

@ -13,7 +13,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '5.5-alpha-48277'; $wp_version = '5.5-alpha-48278';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.