Scott Taylor 2015-06-28 00:17:25 +00:00
parent f077b3c511
commit a59b2ffcae
18 changed files with 465 additions and 120 deletions

View File

@ -519,11 +519,13 @@ class getid3_lib
} }
public static function XML2array($XMLstring) { public static function XML2array($XMLstring) {
if ( function_exists( 'simplexml_load_string' ) && function_exists( 'libxml_disable_entity_loader' ) ) { if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) {
$loader = libxml_disable_entity_loader( true ); // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html
$XMLobject = simplexml_load_string( $XMLstring, 'SimpleXMLElement', LIBXML_NOENT ); // https://core.trac.wordpress.org/changeset/29378
$return = self::SimpleXMLelement2array( $XMLobject ); $loader = libxml_disable_entity_loader(true);
libxml_disable_entity_loader( $loader ); $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
$return = self::SimpleXMLelement2array($XMLobject);
libxml_disable_entity_loader($loader);
return $return; return $return;
} }
return false; return false;
@ -1163,6 +1165,8 @@ class getid3_lib
fwrite($tmp, $imgData); fwrite($tmp, $imgData);
fclose($tmp); fclose($tmp);
$GetDataImageSize = @getimagesize($tempfilename, $imageinfo); $GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
$GetDataImageSize['height'] = $GetDataImageSize[0];
$GetDataImageSize['width'] = $GetDataImageSize[1];
} }
unlink($tempfilename); unlink($tempfilename);
} }
@ -1373,4 +1377,4 @@ class getid3_lib
return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1); return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1);
} }
} }

View File

@ -28,7 +28,7 @@ $temp_dir = ini_get('upload_tmp_dir');
if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) { if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) {
$temp_dir = ''; $temp_dir = '';
} }
if (!$temp_dir) { if (!$temp_dir && function_exists('sys_get_temp_dir')) { // sys_get_temp_dir added in PHP v5.2.1
// sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts
$temp_dir = sys_get_temp_dir(); $temp_dir = sys_get_temp_dir();
} }
@ -109,7 +109,7 @@ class getID3
protected $startup_error = ''; protected $startup_error = '';
protected $startup_warning = ''; protected $startup_warning = '';
const VERSION = '1.9.8-20140511'; const VERSION = '1.9.9-20141121';
const FREAD_BUFFER_SIZE = 32768; const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false; const ATTACHMENTS_NONE = false;
@ -249,7 +249,7 @@ class getID3
$this->filename = $filename; $this->filename = $filename;
$this->info = array(); $this->info = array();
$this->info['GETID3_VERSION'] = $this->version(); $this->info['GETID3_VERSION'] = $this->version();
$this->info['php_memory_limit'] = $this->memory_limit; $this->info['php_memory_limit'] = (($this->memory_limit > 0) ? $this->memory_limit : false);
// remote files not supported // remote files not supported
if (preg_match('/^(ht|f)tp:\/\//', $filename)) { if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
@ -1793,4 +1793,4 @@ abstract class getid3_handler {
class getid3_exception extends Exception class getid3_exception extends Exception
{ {
public $message; public $message;
} }

View File

@ -24,4 +24,4 @@ made publicly available to all getID3() users.
The licensee may not sub-license getID3() itself, meaning that any The licensee may not sub-license getID3() itself, meaning that any
commercially released product containing all or parts of getID3() must commercially released product containing all or parts of getID3() must
have added functionality beyond what is available in getID3(); have added functionality beyond what is available in getID3();
getID3() itself may not be re-licensed by the licensee. getID3() itself may not be re-licensed by the licensee.

View File

@ -26,4 +26,4 @@ getID3 Commercial License: http://getid3.org/#gCL (payment required)
***************************************************************** *****************************************************************
Copies of each of the above licenses are included in the 'licenses' Copies of each of the above licenses are included in the 'licenses'
directory of the getID3 distribution. directory of the getID3 distribution.

View File

@ -2010,4 +2010,4 @@ class getid3_asf extends getid3_handler {
return $string; return $string;
} }
} }

View File

@ -742,4 +742,4 @@ class AVCSequenceParameterSetReader {
public function getHeight() { public function getHeight() {
return $this->height; return $this->height;
} }
} }

View File

@ -1748,4 +1748,4 @@ class getid3_matroska extends getid3_handler
return $info; return $info;
} }
} }

View File

@ -35,7 +35,7 @@ class getid3_quicktime extends getid3_handler
$offset = 0; $offset = 0;
$atomcounter = 0; $atomcounter = 0;
$atom_data_read_buffer_size = ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 2) : $this->getid3->option_fread_buffer_size * 1024); // allow [default: 32MB] if PHP configured with no memory_limit
while ($offset < $info['avdataend']) { while ($offset < $info['avdataend']) {
if (!getid3_lib::intValueSupported($offset)) { if (!getid3_lib::intValueSupported($offset)) {
$info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions';
@ -68,7 +68,7 @@ class getid3_quicktime extends getid3_handler
break; break;
} }
$atomHierarchy = array(); $atomHierarchy = array();
$info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, round($this->getid3->memory_limit / 2))), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms); $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
$offset += $atomsize; $offset += $atomsize;
$atomcounter++; $atomcounter++;
@ -799,9 +799,9 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
//$FrameRateCalculatorArray = array(); //$FrameRateCalculatorArray = array();
$frames_count = 0; $frames_count = 0;
$max_stts_entries_to_scan = min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']); $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
if ($max_stts_entries_to_scan < $atom_structure['number_entries']) { if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
$info['warning'][] = 'QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($this->getid3->memory_limit / 1048576).'MB).'; $info['warning'][] = 'QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($atom_structure['number_entries'] / 1048576).'MB).';
} }
for ($i = 0; $i < $max_stts_entries_to_scan; $i++) { for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
$atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
@ -1399,7 +1399,7 @@ if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($
case "\x00\x00\x00\x00": case "\x00\x00\x00\x00":
case 'meta': // METAdata atom case 'meta': // METAdata atom
// some kind of metacontainer, may contain a big data dump such as: // some kind of metacontainer, may contain a big data dump such as:
// mdta keys  mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst   data DEApple 0  (data DE2011-05-11T17:54:04+0200 2  *data DE+52.4936+013.3897+040.247/   data DE4.3.1  data DEiPhone 4 // mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4
// http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
@ -2243,4 +2243,4 @@ echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br
return substr($pascalstring, 1); return substr($pascalstring, 1);
} }
} }

View File

@ -471,4 +471,4 @@ class getid3_ac3 extends getid3_handler
} }
} }

View File

@ -288,4 +288,4 @@ class getid3_dts extends getid3_handler
return false; return false;
} }
} }

View File

@ -135,7 +135,17 @@ class getid3_flac extends getid3_handler
if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) { if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) {
foreach ($info['flac']['PICTURE'] as $entry) { foreach ($info['flac']['PICTURE'] as $entry) {
if (!empty($entry['data'])) { if (!empty($entry['data'])) {
$info['flac']['comments']['picture'][] = array('image_mime'=>$entry['image_mime'], 'data'=>$entry['data']); if (!isset($info['flac']['comments']['picture'])) {
$info['flac']['comments']['picture'] = array();
}
$comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($entry[$picture_key])) {
$comments_picture_data[$picture_key] = $entry[$picture_key];
}
}
$info['flac']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
} }
} }
} }
@ -343,25 +353,25 @@ class getid3_flac extends getid3_handler
$info = &$this->getid3->info; $info = &$this->getid3->info;
$picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4)); $picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['type'] = self::pictureTypeLookup($picture['typeid']); $picture['picturetype'] = self::pictureTypeLookup($picture['typeid']);
$picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4))); $picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4)));
$descr_length = getid3_lib::BigEndian2Int($this->fread(4)); $descr_length = getid3_lib::BigEndian2Int($this->fread(4));
if ($descr_length) { if ($descr_length) {
$picture['description'] = $this->fread($descr_length); $picture['description'] = $this->fread($descr_length);
} }
$picture['width'] = getid3_lib::BigEndian2Int($this->fread(4)); $picture['image_width'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['height'] = getid3_lib::BigEndian2Int($this->fread(4)); $picture['image_height'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4)); $picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4));
$picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4)); $picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4));
$data_length = getid3_lib::BigEndian2Int($this->fread(4)); $picture['datalength'] = getid3_lib::BigEndian2Int($this->fread(4));
if ($picture['image_mime'] == '-->') { if ($picture['image_mime'] == '-->') {
$picture['data'] = $this->fread($data_length); $picture['data'] = $this->fread($picture['datalength']);
} else { } else {
$picture['data'] = $this->saveAttachment( $picture['data'] = $this->saveAttachment(
str_replace('/', '_', $picture['type']).'_'.$this->ftell(), str_replace('/', '_', $picture['picturetype']).'_'.$this->ftell(),
$this->ftell(), $this->ftell(),
$data_length, $picture['datalength'],
$picture['image_mime']); $picture['image_mime']);
} }
@ -440,4 +450,4 @@ class getid3_flac extends getid3_handler
return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved'); return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
} }
} }

View File

@ -2009,4 +2009,4 @@ class getid3_mp3 extends getid3_handler
return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org'); return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
} }
} }

View File

@ -63,6 +63,12 @@ class getid3_ogg extends getid3_handler
$this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo); $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo);
} elseif (substr($filedata, 0, 8) == 'OpusHead') {
if( $this->ParseOpusPageHeader($filedata, $filedataoffset, $oggpageinfo) == false ) {
return false;
}
} elseif (substr($filedata, 0, 8) == 'Speex ') { } elseif (substr($filedata, 0, 8) == 'Speex ') {
// http://www.speex.org/manual/node10.html // http://www.speex.org/manual/node10.html
@ -255,7 +261,7 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
} else { } else {
$info['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"'; $info['error'][] = 'Expecting either "Speex ", "OpusHead" or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"';
unset($info['ogg']); unset($info['ogg']);
unset($info['mime_type']); unset($info['mime_type']);
return false; return false;
@ -288,8 +294,19 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
$this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR); $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
$this->ParseVorbisComments(); $this->ParseVorbisComments();
break; break;
}
case 'opus':
$filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
$info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 0, 8); // hard-coded to 'OpusTags'
if(substr($filedata, 0, 8) != 'OpusTags') {
$info['error'][] = 'Expected "OpusTags" as header but got "'.substr($filedata, 0, 8).'"';
return false;
}
$this->ParseVorbisComments();
break;
}
// Last Page - Number of Samples // Last Page - Number of Samples
if (!getid3_lib::intValueSupported($info['avdataend'])) { if (!getid3_lib::intValueSupported($info['avdataend'])) {
@ -409,6 +426,57 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
return true; return true;
} }
// http://tools.ietf.org/html/draft-ietf-codec-oggopus-03
public function ParseOpusPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) {
$info = &$this->getid3->info;
$info['audio']['dataformat'] = 'opus';
$info['mime_type'] = 'audio/ogg; codecs=opus';
/** @todo find a usable way to detect abr (vbr that is padded to be abr) */
$info['audio']['bitrate_mode'] = 'vbr';
$info['audio']['lossless'] = false;
$info['ogg']['pageheader']['opus']['opus_magic'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'OpusHead'
$filedataoffset += 8;
$info['ogg']['pageheader']['opus']['version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
if ($info['ogg']['pageheader']['opus']['version'] < 1 || $info['ogg']['pageheader']['opus']['version'] > 15) {
$info['error'][] = 'Unknown opus version number (only accepting 1-15)';
return false;
}
$info['ogg']['pageheader']['opus']['out_channel_count'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
$filedataoffset += 1;
if ($info['ogg']['pageheader']['opus']['out_channel_count'] == 0) {
$info['error'][] = 'Invalid channel count in opus header (must not be zero)';
return false;
}
$info['ogg']['pageheader']['opus']['pre_skip'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
$filedataoffset += 2;
$info['ogg']['pageheader']['opus']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
$filedataoffset += 4;
//$info['ogg']['pageheader']['opus']['output_gain'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2));
//$filedataoffset += 2;
//$info['ogg']['pageheader']['opus']['channel_mapping_family'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
//$filedataoffset += 1;
$info['opus']['opus_version'] = $info['ogg']['pageheader']['opus']['version'];
$info['opus']['sample_rate'] = $info['ogg']['pageheader']['opus']['sample_rate'];
$info['opus']['out_channel_count'] = $info['ogg']['pageheader']['opus']['out_channel_count'];
$info['audio']['channels'] = $info['opus']['out_channel_count'];
$info['audio']['sample_rate'] = $info['opus']['sample_rate'];
return true;
}
public function ParseOggPageHeader() { public function ParseOggPageHeader() {
// http://xiph.org/ogg/vorbis/doc/framing.html // http://xiph.org/ogg/vorbis/doc/framing.html
$oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file
@ -471,6 +539,7 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
switch ($info['audio']['dataformat']) { switch ($info['audio']['dataformat']) {
case 'vorbis': case 'vorbis':
case 'speex': case 'speex':
case 'opus':
$CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block
$this->fseek($CommentStartOffset); $this->fseek($CommentStartOffset);
$commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
@ -479,6 +548,10 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
if ($info['audio']['dataformat'] == 'vorbis') { if ($info['audio']['dataformat'] == 'vorbis') {
$commentdataoffset += (strlen('vorbis') + 1); $commentdataoffset += (strlen('vorbis') + 1);
} }
else if ($info['audio']['dataformat'] == 'opus') {
$commentdataoffset += strlen('OpusTags');
}
break; break;
case 'flac': case 'flac':
@ -505,6 +578,12 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
$ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw']; $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw'];
for ($i = 0; $i < $CommentsCount; $i++) { for ($i = 0; $i < $CommentsCount; $i++) {
if ($i >= 10000) {
// https://github.com/owncloud/music/issues/212#issuecomment-43082336
$info['warning'][] = 'Unexpectedly large number ('.$CommentsCount.') of Ogg comments - breaking after reading '.$i.' comments';
break;
}
$ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) { if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) {
@ -615,8 +694,12 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
$ogg = new self($this->getid3); $ogg = new self($this->getid3);
$ogg->setStringMode($data); $ogg->setStringMode($data);
$info['ogg']['comments']['picture'][] = array( $info['ogg']['comments']['picture'][] = array(
'image_mime' => $imageinfo['mime'], 'image_mime' => $imageinfo['mime'],
'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']), 'datalength' => strlen($data),
'picturetype' => 'cover art',
'image_height' => $imageinfo['height'],
'image_width' => $imageinfo['width'],
'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']),
); );
unset($ogg); unset($ogg);
@ -753,4 +836,4 @@ $info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of get
return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null); return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null);
} }
} }

View File

@ -138,58 +138,88 @@ class getid3_apetag extends getid3_handler
$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
case 0: // UTF-8 case 0: // UTF-8
case 3: // Locator (URL, filename, etc), UTF-8 encoded case 2: // Locator (URL, filename, etc), UTF-8 encoded
$thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data'])); $thisfile_ape_items_current['data'] = explode("\x00", $thisfile_ape_items_current['data']);
break; break;
default: // binary data case 1: // binary data
default:
break; break;
} }
switch (strtolower($item_key)) { switch (strtolower($item_key)) {
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
case 'replaygain_track_gain': case 'replaygain_track_gain':
$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['originator'] = 'unspecified'; $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
} else {
$info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'replaygain_track_peak': case 'replaygain_track_peak':
$thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['track']['originator'] = 'unspecified'; $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if ($thisfile_replaygain['track']['peak'] <= 0) { $thisfile_replaygain['track']['originator'] = 'unspecified';
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; if ($thisfile_replaygain['track']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
}
} else {
$info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
} }
break; break;
case 'replaygain_album_gain': case 'replaygain_album_gain':
$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['originator'] = 'unspecified'; $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
} else {
$info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'replaygain_album_peak': case 'replaygain_album_peak':
$thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['album']['originator'] = 'unspecified'; $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
if ($thisfile_replaygain['album']['peak'] <= 0) { $thisfile_replaygain['album']['originator'] = 'unspecified';
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; if ($thisfile_replaygain['album']['peak'] <= 0) {
$info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
}
} else {
$info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
} }
break; break;
case 'mp3gain_undo': case 'mp3gain_undo':
list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
$thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false);
} else {
$info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'mp3gain_minmax': case 'mp3gain_minmax':
list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
} else {
$info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'mp3gain_album_minmax': case 'mp3gain_album_minmax':
list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
} else {
$info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"';
}
break; break;
case 'tracknumber': case 'tracknumber':
@ -222,6 +252,10 @@ class getid3_apetag extends getid3_handler
case 'cover art (recording)': case 'cover art (recording)':
case 'cover art (studio)': case 'cover art (studio)':
// list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
if (is_array($thisfile_ape_items_current['data'])) {
$info['warning'][] = 'APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8';
$thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
}
list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2); list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
$thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00"); $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
$thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
@ -269,7 +303,14 @@ class getid3_apetag extends getid3_handler
if (!isset($info['ape']['comments']['picture'])) { if (!isset($info['ape']['comments']['picture'])) {
$info['ape']['comments']['picture'] = array(); $info['ape']['comments']['picture'] = array();
} }
$info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']); $comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($thisfile_ape_items_current[$picture_key])) {
$comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key];
}
}
$info['ape']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
} }
} while (false); } while (false);
break; break;
@ -317,7 +358,7 @@ class getid3_apetag extends getid3_handler
public function parseAPEtagFlags($rawflagint) { public function parseAPEtagFlags($rawflagint) {
// "Note: APE Tags 1.0 do not use any of the APE Tag flags. // "Note: APE Tags 1.0 do not use any of the APE Tag flags.
// All are set to zero on creation and ignored on reading." // All are set to zero on creation and ignored on reading."
// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html // http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags
$flags['header'] = (bool) ($rawflagint & 0x80000000); $flags['header'] = (bool) ($rawflagint & 0x80000000);
$flags['footer'] = (bool) ($rawflagint & 0x40000000); $flags['footer'] = (bool) ($rawflagint & 0x40000000);
$flags['this_is_header'] = (bool) ($rawflagint & 0x20000000); $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000);
@ -368,4 +409,4 @@ class getid3_apetag extends getid3_handler
return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup); return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
} }
} }

View File

@ -357,4 +357,4 @@ class getid3_id3v1 extends getid3_handler
return $ID3v1Tag; return $ID3v1Tag;
} }
} }

View File

@ -625,12 +625,13 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -640,8 +641,8 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['description'] = $frame_description; $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
@ -717,11 +718,13 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -729,10 +732,10 @@ class getid3_id3v2 extends getid3_handler
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
if ($frame_terminatorpos) { if ($frame_terminatorpos) {
@ -956,20 +959,22 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
$frame_offset += 3; $frame_offset += 3;
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
@ -1002,8 +1007,10 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
$frame_offset += 3; $frame_offset += 3;
@ -1020,16 +1027,16 @@ class getid3_id3v2 extends getid3_handler
$frame_remainingdata = substr($parsedFrame['data'], $frame_offset); $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
while (strlen($frame_remainingdata)) { while (strlen($frame_remainingdata)) {
$frame_offset = 0; $frame_offset = 0;
$frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_terminatorpos = strpos($frame_remainingdata, $frame_textencoding_terminator);
if ($frame_terminatorpos === false) { if ($frame_terminatorpos === false) {
$frame_remainingdata = ''; $frame_remainingdata = '';
} else { } else {
if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) { if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
// timestamp probably omitted for first data item // timestamp probably omitted for first data item
} else { } else {
@ -1060,20 +1067,22 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_language = substr($parsedFrame['data'], $frame_offset, 3); $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
$frame_offset += 3; $frame_offset += 3;
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
@ -1330,8 +1339,10 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
@ -1367,8 +1378,8 @@ class getid3_id3v2 extends getid3_handler
if ($frame_offset >= $parsedFrame['datalength']) { if ($frame_offset >= $parsedFrame['datalength']) {
$info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset); $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset);
} else { } else {
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -1386,7 +1397,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['picturetypeid'] = $frame_picturetype; $parsedFrame['picturetypeid'] = $frame_picturetype;
$parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
$parsedFrame['description'] = $frame_description; $parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
$parsedFrame['datalength'] = strlen($parsedFrame['data']); $parsedFrame['datalength'] = strlen($parsedFrame['data']);
$parsedFrame['image_mime'] = ''; $parsedFrame['image_mime'] = '';
@ -1443,7 +1454,14 @@ class getid3_id3v2 extends getid3_handler
if (!isset($info['id3v2']['comments']['picture'])) { if (!isset($info['id3v2']['comments']['picture'])) {
$info['id3v2']['comments']['picture'] = array(); $info['id3v2']['comments']['picture'] = array();
} }
$info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']); $comments_picture_data = array();
foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
if (isset($parsedFrame[$picture_key])) {
$comments_picture_data[$picture_key] = $parsedFrame[$picture_key];
}
}
$info['id3v2']['comments']['picture'][] = $comments_picture_data;
unset($comments_picture_data);
} }
} }
} while (false); } while (false);
@ -1462,8 +1480,10 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -1472,25 +1492,25 @@ class getid3_id3v2 extends getid3_handler
} }
$frame_offset = $frame_terminatorpos + strlen("\x00"); $frame_offset = $frame_terminatorpos + strlen("\x00");
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_filename) === 0) { if (ord($frame_filename) === 0) {
$frame_filename = ''; $frame_filename = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['encodingid'] = $frame_textencoding; $parsedFrame['encodingid'] = $frame_textencoding;
@ -1607,7 +1627,7 @@ class getid3_id3v2 extends getid3_handler
} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information
(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information
// There may be more than one 'LINK' frame in a tag, // There may be more than one 'LINK' frame in a tag,
// but only one with the same contents // but only one with the same contents
// <Header for 'Linked information', ID: 'LINK'> // <Header for 'Linked information', ID: 'LINK'>
@ -1635,7 +1655,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback_iso88591_utf8($parsedFrame['url']);
} }
unset($parsedFrame['data']); unset($parsedFrame['data']);
@ -1729,8 +1749,10 @@ class getid3_id3v2 extends getid3_handler
$frame_offset = 0; $frame_offset = 0;
$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_textencoding_terminator = $this->TextEncodingTerminatorLookup($frame_textencoding);
if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
$info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
$frame_textencoding_terminator = "\x00";
} }
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
@ -1752,25 +1774,25 @@ class getid3_id3v2 extends getid3_handler
$frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_sellername) === 0) { if (ord($frame_sellername) === 0) {
$frame_sellername = ''; $frame_sellername = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator, $frame_offset);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
} }
$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
if (ord($frame_description) === 0) { if (ord($frame_description) === 0) {
$frame_description = ''; $frame_description = '';
} }
$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
@ -1944,6 +1966,186 @@ class getid3_id3v2 extends getid3_handler
unset($parsedFrame['data']); unset($parsedFrame['data']);
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CHAP')) { // CHAP Chapters frame (ID3v2.3+ only)
// http://id3.org/id3v2-chapters-1.0
// <ID3v2.3 or ID3v2.4 frame header, ID: "CHAP"> (10 bytes)
// Element ID <text string> $00
// Start time $xx xx xx xx
// End time $xx xx xx xx
// Start offset $xx xx xx xx
// End offset $xx xx xx xx
// <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
$frame_offset += strlen($parsedFrame['element_id']."\x00");
$parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
$parsedFrame['time_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
// "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
$parsedFrame['offset_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
}
$frame_offset += 4;
if (substr($parsedFrame['data'], $frame_offset, 4) != "\xFF\xFF\xFF\xFF") {
// "If these bytes are all set to 0xFF then the value should be ignored and the start time value should be utilized."
$parsedFrame['offset_end'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
}
$frame_offset += 4;
if ($frame_offset < strlen($parsedFrame['data'])) {
$parsedFrame['subframes'] = array();
while ($frame_offset < strlen($parsedFrame['data'])) {
// <Optional embedded sub-frames>
$subframe = array();
$subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
$frame_offset += 4;
$subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
$subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
$frame_offset += 2;
if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
$info['warning'][] = 'CHAP subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)';
break;
}
$subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
$frame_offset += $subframe['size'];
$subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
$subframe['text'] = substr($subframe_rawdata, 1);
$subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
switch (substr($encoding_converted_text, 0, 2)) {
case "\xFF\xFE":
case "\xFE\xFF":
switch (strtoupper($info['id3v2']['encoding'])) {
case 'ISO-8859-1':
case 'UTF-8':
$encoding_converted_text = substr($encoding_converted_text, 2);
// remove unwanted byte-order-marks
break;
default:
// ignore
break;
}
break;
default:
// do not remove BOM
break;
}
if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
if ($subframe['name'] == 'TIT2') {
$parsedFrame['chapter_name'] = $encoding_converted_text;
} elseif ($subframe['name'] == 'TIT3') {
$parsedFrame['chapter_description'] = $encoding_converted_text;
}
$parsedFrame['subframes'][] = $subframe;
} else {
$info['warning'][] = 'ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)';
}
}
unset($subframe_rawdata, $subframe, $encoding_converted_text);
}
$id3v2_chapter_entry = array();
foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
if (isset($parsedFrame[$id3v2_chapter_key])) {
$id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
}
}
if (!isset($info['id3v2']['chapters'])) {
$info['id3v2']['chapters'] = array();
}
$info['id3v2']['chapters'][] = $id3v2_chapter_entry;
unset($id3v2_chapter_entry, $id3v2_chapter_key);
} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)
// http://id3.org/id3v2-chapters-1.0
// <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes)
// Element ID <text string> $00
// CTOC flags %xx
// Entry count $xx
// Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */
// <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
$frame_offset += strlen($parsedFrame['element_id']."\x00");
$ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
$frame_offset += 1;
$parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));
$frame_offset += 1;
$terminator_position = null;
for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {
$terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);
$parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);
$frame_offset = $terminator_position + 1;
}
$parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01);
$parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);
unset($ctoc_flags_raw, $terminator_position);
if ($frame_offset < strlen($parsedFrame['data'])) {
$parsedFrame['subframes'] = array();
while ($frame_offset < strlen($parsedFrame['data'])) {
// <Optional embedded sub-frames>
$subframe = array();
$subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);
$frame_offset += 4;
$subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
$subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
$frame_offset += 2;
if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
$info['warning'][] = 'CTOS subframe "'.$subframe['name'].'" at frame offset '.$frame_offset.' claims to be "'.$subframe['size'].'" bytes, which is more than the available data ('.(strlen($parsedFrame['data']) - $frame_offset).' bytes)';
break;
}
$subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);
$frame_offset += $subframe['size'];
$subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
$subframe['text'] = substr($subframe_rawdata, 1);
$subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
$encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
switch (substr($encoding_converted_text, 0, 2)) {
case "\xFF\xFE":
case "\xFE\xFF":
switch (strtoupper($info['id3v2']['encoding'])) {
case 'ISO-8859-1':
case 'UTF-8':
$encoding_converted_text = substr($encoding_converted_text, 2);
// remove unwanted byte-order-marks
break;
default:
// ignore
break;
}
break;
default:
// do not remove BOM
break;
}
if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
if ($subframe['name'] == 'TIT2') {
$parsedFrame['toc_name'] = $encoding_converted_text;
} elseif ($subframe['name'] == 'TIT3') {
$parsedFrame['toc_description'] = $encoding_converted_text;
}
$parsedFrame['subframes'][] = $subframe;
} else {
$info['warning'][] = 'ID3v2.CTOC subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)';
}
}
unset($subframe_rawdata, $subframe, $encoding_converted_text);
}
} }
return true; return true;
@ -3344,7 +3546,7 @@ class getid3_id3v2 extends getid3_handler
3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00. 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00.
255 => "\x00\x00" 255 => "\x00\x00"
); );
return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : ''); return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : "\x00");
} }
public static function TextEncodingNameLookup($encoding) { public static function TextEncodingNameLookup($encoding) {
@ -3422,3 +3624,4 @@ class getid3_id3v2 extends getid3_handler
} }
} }

View File

@ -101,20 +101,24 @@ class getid3_lyrics3 extends getid3_handler
$this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size); $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size);
if (!isset($info['ape'])) { if (!isset($info['ape'])) {
$GETID3_ERRORARRAY = &$info['warning']; if (isset($info['lyrics3']['tag_offset_start'])) {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); $GETID3_ERRORARRAY = &$info['warning'];
$getid3_temp = new getID3(); getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
$getid3_temp->openfile($this->getid3->filename); $getid3_temp = new getID3();
$getid3_apetag = new getid3_apetag($getid3_temp); $getid3_temp->openfile($this->getid3->filename);
$getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; $getid3_apetag = new getid3_apetag($getid3_temp);
$getid3_apetag->Analyze(); $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
if (!empty($getid3_temp->info['ape'])) { $getid3_apetag->Analyze();
$info['ape'] = $getid3_temp->info['ape']; 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 {
$info['warning'][] = 'Lyrics3 and APE tags appear to have become entangled (most likely due to updating the APE tags with a non-Lyrics3-aware tagger)';
} }
if (!empty($getid3_temp->info['replay_gain'])) {
$info['replay_gain'] = $getid3_temp->info['replay_gain'];
}
unset($getid3_temp, $getid3_apetag);
} }
} }
@ -291,4 +295,4 @@ class getid3_lyrics3 extends getid3_handler
} }
return null; return null;
} }
} }

View File

@ -4,7 +4,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '4.3-alpha-32978'; $wp_version = '4.3-alpha-32979';
/** /**
* 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.