Changeset 46112 for trunk/src/wp-includes/ID3/module.tag.id3v2.php
- Timestamp:
- 09/14/2019 07:06:09 PM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/ID3/module.tag.id3v2.php
r41196 r46112 1 1 <?php 2 2 3 ///////////////////////////////////////////////////////////////// 3 4 /// getID3() by James Heinrich <info@getid3.org> // 4 // available at http://getid3.sourceforge.net // 5 // or http://www.getid3.org // 6 // also https://github.com/JamesHeinrich/getID3 // 7 ///////////////////////////////////////////////////////////////// 8 // See readme.txt for more details // 5 // available at https://github.com/JamesHeinrich/getID3 // 6 // or https://www.getid3.org // 7 // or http://getid3.sourceforge.net // 8 // see readme.txt for more details // 9 9 ///////////////////////////////////////////////////////////////// 10 10 /// // … … 21 21 public $StartingOffset = 0; 22 22 23 /** 24 * @return bool 25 */ 23 26 public function Analyze() { 24 27 $info = &$this->getid3->info; … … 57 60 if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { 58 61 59 $thisfile_id3v2['majorversion'] = ord($header {3});60 $thisfile_id3v2['minorversion'] = ord($header {4});62 $thisfile_id3v2['majorversion'] = ord($header[3]); 63 $thisfile_id3v2['minorversion'] = ord($header[4]); 61 64 62 65 // shortcut … … 77 80 } 78 81 79 $id3_flags = ord($header {5});82 $id3_flags = ord($header[5]); 80 83 switch ($id3v2_majorversion) { 81 84 case 2: … … 258 261 $thisfile_id3v2['padding']['valid'] = true; 259 262 for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { 260 if ($framedata {$i}!= "\x00") {263 if ($framedata[$i] != "\x00") { 261 264 $thisfile_id3v2['padding']['valid'] = false; 262 265 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; … … 267 270 break; // skip rest of ID3v2 header 268 271 } 272 $frame_header = null; 273 $frame_name = null; 274 $frame_size = null; 275 $frame_flags = null; 269 276 if ($id3v2_majorversion == 2) { 270 277 // Frame ID $xx xx xx (three characters) … … 320 327 $len = strlen($framedata); 321 328 for ($i = 0; $i < $len; $i++) { 322 if ($framedata {$i}!= "\x00") {329 if ($framedata[$i] != "\x00") { 323 330 $thisfile_id3v2['padding']['valid'] = false; 324 331 $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; … … 428 435 if (substr($footer, 0, 3) == '3DI') { 429 436 $thisfile_id3v2['footer'] = true; 430 $thisfile_id3v2['majorversion_footer'] = ord($footer {3});431 $thisfile_id3v2['minorversion_footer'] = ord($footer {4});437 $thisfile_id3v2['majorversion_footer'] = ord($footer[3]); 438 $thisfile_id3v2['minorversion_footer'] = ord($footer[4]); 432 439 } 433 440 if ($thisfile_id3v2['majorversion_footer'] <= 4) { 434 $id3_flags = ord( substr($footer{5}));441 $id3_flags = ord($footer[5]); 435 442 $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); 436 443 $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); … … 453 460 } 454 461 455 if (isset($thisfile_id3v2['comments']['track '])) {456 foreach ($thisfile_id3v2['comments']['track '] as $key => $value) {462 if (isset($thisfile_id3v2['comments']['track_number'])) { 463 foreach ($thisfile_id3v2['comments']['track_number'] as $key => $value) { 457 464 if (strstr($value, '/')) { 458 list($thisfile_id3v2['comments']['track num'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);465 list($thisfile_id3v2['comments']['track_number'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track_number'][$key]); 459 466 } 460 467 } … … 499 506 } 500 507 501 508 /** 509 * @param string $genrestring 510 * 511 * @return array 512 */ 502 513 public function ParseID3v2GenreString($genrestring) { 503 514 // Parse genres into arrays of genreName and genreID … … 531 542 $element = trim($element); 532 543 if ($element) { 533 if (preg_match('#^[0-9]{1,3} #', $element)) {544 if (preg_match('#^[0-9]{1,3}$#', $element)) { 534 545 $clean_genres[] = getid3_id3v1::LookupGenreName($element); 535 546 } else { … … 541 552 } 542 553 543 554 /** 555 * @param array $parsedFrame 556 * 557 * @return bool 558 */ 544 559 public function ParseID3v2Frame(&$parsedFrame) { 545 560 … … 658 673 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 659 674 } 660 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 661 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 662 // if description only contains a BOM or terminator then make it blank 663 $frame_description = ''; 664 } 675 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 676 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 665 677 $parsedFrame['encodingid'] = $frame_textencoding; 666 678 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 667 679 668 $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $ frame_description));680 $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['description'])); 669 681 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 682 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator); 670 683 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 671 684 $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); … … 679 692 680 693 681 } elseif ($parsedFrame['frame_name'] {0}== 'T') { // 4.2. T??[?] Text information frame694 } elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame 682 695 // There may only be one text information frame of its kind in an tag. 683 696 // <Header for 'Text information frame', ID: 'T000' - 'TZZZ', … … 693 706 694 707 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 708 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 695 709 696 710 $parsedFrame['encodingid'] = $frame_textencoding; 697 711 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 698 699 712 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 700 713 // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / … … 752 765 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 753 766 } 754 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);755 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {756 // if description only contains a BOM or terminator then make it blank757 $frame_description = '';758 }759 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));760 761 $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);762 if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {763 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00764 }765 if ($frame_terminatorpos) {766 // there are null bytes after the data - this is not according to spec767 // only use data up to first null byte768 $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);769 } else {770 // no null bytes following data, just use all data771 $frame_urldata = (string) $parsedFrame['data'];772 }773 774 767 $parsedFrame['encodingid'] = $frame_textencoding; 775 768 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 776 777 $parsedFrame['url'] = $frame_urldata; 778 $parsedFrame['description'] = $frame_description; 769 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); // according to the frame text encoding 770 $parsedFrame['url'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); // always ISO-8859-1 771 $parsedFrame['description'] = $this->RemoveStringTerminator($parsedFrame['description'], $frame_textencoding_terminator); 772 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 773 779 774 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 780 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback( $parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);775 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); 781 776 } 782 777 unset($parsedFrame['data']); 783 778 784 779 785 } elseif ($parsedFrame['frame_name'] {0}== 'W') { // 4.3. W??? URL link frames780 } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames 786 781 // There may only be one URL link frame of its kind in a tag, 787 782 // except when stated otherwise in the frame description … … 790 785 // URL <text string> 791 786 792 $parsedFrame['url'] = trim($parsedFrame['data']); 787 $parsedFrame['url'] = trim($parsedFrame['data']); // always ISO-8859-1 793 788 if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { 794 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];789 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']); 795 790 } 796 791 unset($parsedFrame['data']); … … 814 809 $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset); 815 810 816 // http ://www.getid3.org/phpBB3/viewtopic.php?t=1369811 // https://www.getid3.org/phpBB3/viewtopic.php?t=1369 817 812 // "this tag typically contains null terminated strings, which are associated in pairs" 818 813 // "there are users that use the tag incorrectly" … … 934 929 $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); 935 930 $parsedFrame['data'] = substr($parsedFrame['data'], 10); 931 $deviationbitstream = ''; 936 932 while ($frame_offset < strlen($parsedFrame['data'])) { 937 933 $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); … … 995 991 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 996 992 } 997 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 998 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 999 // if description only contains a BOM or terminator then make it blank 1000 $frame_description = ''; 1001 } 993 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 994 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1002 995 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 996 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator); 1003 997 1004 998 $parsedFrame['encodingid'] = $frame_textencoding; … … 1007 1001 $parsedFrame['language'] = $frame_language; 1008 1002 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1009 $parsedFrame['description'] = $frame_description;1010 1003 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1011 1004 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); … … 1062 1055 1063 1056 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1064 if (($timestampindex == 0) && (ord($frame_remainingdata {0}) != 0)) {1057 if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) { 1065 1058 // timestamp probably omitted for first data item 1066 1059 } else { … … 1103 1096 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1104 1097 } 1105 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1106 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 1107 // if description only contains a BOM or terminator then make it blank 1108 $frame_description = ''; 1109 } 1098 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1099 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1110 1100 $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1101 $frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator); 1111 1102 1112 1103 $parsedFrame['encodingid'] = $frame_textencoding; … … 1115 1106 $parsedFrame['language'] = $frame_language; 1116 1107 $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); 1117 $parsedFrame['description'] = $frame_description;1118 1108 $parsedFrame['data'] = $frame_text; 1119 1109 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { … … 1408 1398 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1409 1399 } 1410 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1411 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 1412 // if description only contains a BOM or terminator then make it blank 1413 $frame_description = ''; 1400 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1401 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1402 $parsedFrame['encodingid'] = $frame_textencoding; 1403 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1404 1405 if ($id3v2_majorversion == 2) { 1406 $parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null; 1407 } else { 1408 $parsedFrame['mime'] = isset($frame_mimetype) ? $frame_mimetype : null; 1414 1409 } 1415 $parsedFrame['encodingid'] = $frame_textencoding; 1416 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1417 1418 if ($id3v2_majorversion == 2) { 1419 $parsedFrame['imagetype'] = $frame_imagetype; 1420 } else { 1421 $parsedFrame['mime'] = $frame_mimetype; 1422 } 1423 $parsedFrame['picturetypeid'] = $frame_picturetype; 1424 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); 1425 $parsedFrame['description'] = $frame_description; 1426 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1427 $parsedFrame['datalength'] = strlen($parsedFrame['data']); 1428 1429 $parsedFrame['image_mime'] = ''; 1410 $parsedFrame['picturetypeid'] = $frame_picturetype; 1411 $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); 1412 $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); 1413 $parsedFrame['datalength'] = strlen($parsedFrame['data']); 1414 1415 $parsedFrame['image_mime'] = ''; 1430 1416 $imageinfo = array(); 1431 1417 if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) { 1432 1418 if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { 1433 $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);1419 $parsedFrame['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); 1434 1420 if ($imagechunkcheck[0]) { 1435 1421 $parsedFrame['image_width'] = $imagechunkcheck[0]; … … 1447 1433 break; 1448 1434 } 1435 $dir = ''; 1449 1436 if ($this->getid3->option_save_attachments === true) { 1450 1437 // great … … 1534 1521 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1535 1522 } 1536 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1537 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 1538 // if description only contains a BOM or terminator then make it blank 1539 $frame_description = ''; 1540 } 1523 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1524 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1541 1525 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); 1542 1526 … … 1547 1531 $parsedFrame['mime'] = $frame_mimetype; 1548 1532 $parsedFrame['filename'] = $frame_filename; 1549 $parsedFrame['description'] = $frame_description;1550 1533 unset($parsedFrame['data']); 1551 1534 … … 1617 1600 1618 1601 $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); 1619 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1620 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 1621 // if description only contains a BOM or terminator then make it blank 1622 $frame_description = ''; 1623 } 1602 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1603 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1624 1604 $frame_offset = $frame_terminatorpos + strlen("\x00"); 1625 1605 1626 1606 $parsedFrame['ownerid'] = $frame_ownerid; 1627 1607 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1628 $parsedFrame['description'] = $frame_description;1629 1608 unset($parsedFrame['data']); 1630 1609 … … 1722 1701 $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); 1723 1702 1724 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1703 $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); 1704 $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 1725 1705 if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { 1726 1706 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); … … 1760 1740 1761 1741 $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); 1742 $parsedFrame['seller'] = $this->RemoveStringTerminator($parsedFrame['seller'], $this->TextEncodingTerminatorLookup($frame_textencoding)); 1762 1743 unset($parsedFrame['data']); 1763 1744 … … 1818 1799 $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 1819 1800 } 1820 $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1821 if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 1822 // if description only contains a BOM or terminator then make it blank 1823 $frame_description = ''; 1824 } 1801 $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); 1802 $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']); 1825 1803 $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator); 1826 1804 … … 1839 1817 $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); 1840 1818 $parsedFrame['sellername'] = $frame_sellername; 1841 $parsedFrame['description'] = $frame_description;1842 1819 $parsedFrame['mime'] = $frame_mimetype; 1843 1820 $parsedFrame['logo'] = $frame_sellerlogo; … … 2003 1980 // Start time $xx xx xx xx 2004 1981 // End time $xx xx xx xx 2005 2006 2007 1982 // Start offset $xx xx xx xx 1983 // End offset $xx xx xx xx 1984 // <Optional embedded sub-frames> 2008 1985 2009 1986 $frame_offset = 0; … … 2038 2015 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { 2039 2016 $this->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)'); 2017 break; 2018 } 2019 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']); 2020 $frame_offset += $subframe['size']; 2021 2022 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1)); 2023 $subframe['text'] = substr($subframe_rawdata, 1); 2024 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']); 2025 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text'])); 2026 switch (substr($encoding_converted_text, 0, 2)) { 2027 case "\xFF\xFE": 2028 case "\xFE\xFF": 2029 switch (strtoupper($info['id3v2']['encoding'])) { 2030 case 'ISO-8859-1': 2031 case 'UTF-8': 2032 $encoding_converted_text = substr($encoding_converted_text, 2); 2033 // remove unwanted byte-order-marks 2034 break; 2035 default: 2036 // ignore 2037 break; 2038 } 2039 break; 2040 default: 2041 // do not remove BOM 2042 break; 2043 } 2044 2045 switch ($subframe['name']) { 2046 case 'TIT2': 2047 $parsedFrame['chapter_name'] = $encoding_converted_text; 2048 $parsedFrame['subframes'][] = $subframe; 2049 break; 2050 case 'TIT3': 2051 $parsedFrame['chapter_description'] = $encoding_converted_text; 2052 $parsedFrame['subframes'][] = $subframe; 2053 break; 2054 case 'WXXX': 2055 list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2); 2056 $parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url']; 2057 $parsedFrame['subframes'][] = $subframe; 2058 break; 2059 case 'APIC': 2060 if (preg_match('#^([^\\x00]+)*\\x00(.)([^\\x00]+)*\\x00(.+)$#s', $subframe['text'], $matches)) { 2061 list($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata) = $matches; 2062 $subframe['image_mime'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_mime)); 2063 $subframe['picture_type'] = $this->APICPictureTypeLookup($subframe_apic_picturetype); 2064 $subframe['description'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_description)); 2065 if (strlen($this->TextEncodingTerminatorLookup($subframe['encoding'])) == 2) { 2066 // the null terminator between "description" and "picture data" could be either 1 byte (ISO-8859-1, UTF-8) or two bytes (UTF-16) 2067 // the above regex assumes one byte, if it's actually two then strip the second one here 2068 $subframe_apic_picturedata = substr($subframe_apic_picturedata, 1); 2069 } 2070 $subframe['data'] = $subframe_apic_picturedata; 2071 unset($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata); 2072 unset($subframe['text'], $parsedFrame['text']); 2073 $parsedFrame['subframes'][] = $subframe; 2074 $parsedFrame['picture_present'] = true; 2075 } else { 2076 $this->warning('ID3v2.CHAP subframe #'.(count($parsedFrame['subframes']) + 1).' "'.$subframe['name'].'" not in expected format'); 2077 } 2078 break; 2079 default: 2080 $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (supported: TIT2, TIT3, WXXX, APIC)'); 2081 break; 2082 } 2083 } 2084 unset($subframe_rawdata, $subframe, $encoding_converted_text); 2085 unset($parsedFrame['data']); // debatable whether this this be here, without it the returned structure may contain a large amount of duplicate data if chapters contain APIC 2086 } 2087 2088 $id3v2_chapter_entry = array(); 2089 foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description', 'chapter_url', 'picture_present') as $id3v2_chapter_key) { 2090 if (isset($parsedFrame[$id3v2_chapter_key])) { 2091 $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key]; 2092 } 2093 } 2094 if (!isset($info['id3v2']['chapters'])) { 2095 $info['id3v2']['chapters'] = array(); 2096 } 2097 $info['id3v2']['chapters'][] = $id3v2_chapter_entry; 2098 unset($id3v2_chapter_entry, $id3v2_chapter_key); 2099 2100 2101 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only) 2102 // http://id3.org/id3v2-chapters-1.0 2103 // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes) 2104 // Element ID <text string> $00 2105 // CTOC flags %xx 2106 // Entry count $xx 2107 // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */ 2108 // <Optional embedded sub-frames> 2109 2110 $frame_offset = 0; 2111 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2); 2112 $frame_offset += strlen($parsedFrame['element_id']."\x00"); 2113 $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1)); 2114 $frame_offset += 1; 2115 $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1)); 2116 $frame_offset += 1; 2117 2118 $terminator_position = null; 2119 for ($i = 0; $i < $parsedFrame['entry_count']; $i++) { 2120 $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset); 2121 $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset); 2122 $frame_offset = $terminator_position + 1; 2123 } 2124 2125 $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01); 2126 $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03); 2127 2128 unset($ctoc_flags_raw, $terminator_position); 2129 2130 if ($frame_offset < strlen($parsedFrame['data'])) { 2131 $parsedFrame['subframes'] = array(); 2132 while ($frame_offset < strlen($parsedFrame['data'])) { 2133 // <Optional embedded sub-frames> 2134 $subframe = array(); 2135 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4); 2136 $frame_offset += 4; 2137 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); 2138 $frame_offset += 4; 2139 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); 2140 $frame_offset += 2; 2141 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) { 2142 $this->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)'); 2040 2143 break; 2041 2144 } … … 2068 2171 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) { 2069 2172 if ($subframe['name'] == 'TIT2') { 2070 $parsedFrame['chapter_name'] = $encoding_converted_text;2071 } elseif ($subframe['name'] == 'TIT3') {2072 $parsedFrame['chapter_description'] = $encoding_converted_text;2073 }2074 $parsedFrame['subframes'][] = $subframe;2075 } else {2076 $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');2077 }2078 }2079 unset($subframe_rawdata, $subframe, $encoding_converted_text);2080 }2081 2082 $id3v2_chapter_entry = array();2083 foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {2084 if (isset($parsedFrame[$id3v2_chapter_key])) {2085 $id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];2086 }2087 }2088 if (!isset($info['id3v2']['chapters'])) {2089 $info['id3v2']['chapters'] = array();2090 }2091 $info['id3v2']['chapters'][] = $id3v2_chapter_entry;2092 unset($id3v2_chapter_entry, $id3v2_chapter_key);2093 2094 2095 } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'CTOC')) { // CTOC Chapters Table Of Contents frame (ID3v2.3+ only)2096 // http://id3.org/id3v2-chapters-1.02097 // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC"> (10 bytes)2098 // Element ID <text string> $002099 // CTOC flags %xx2100 // Entry count $xx2101 // Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */2102 // <Optional embedded sub-frames>2103 2104 $frame_offset = 0;2105 @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);2106 $frame_offset += strlen($parsedFrame['element_id']."\x00");2107 $ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));2108 $frame_offset += 1;2109 $parsedFrame['entry_count'] = ord(substr($parsedFrame['data'], $frame_offset, 1));2110 $frame_offset += 1;2111 2112 $terminator_position = null;2113 for ($i = 0; $i < $parsedFrame['entry_count']; $i++) {2114 $terminator_position = strpos($parsedFrame['data'], "\x00", $frame_offset);2115 $parsedFrame['child_element_ids'][$i] = substr($parsedFrame['data'], $frame_offset, $terminator_position - $frame_offset);2116 $frame_offset = $terminator_position + 1;2117 }2118 2119 $parsedFrame['ctoc_flags']['ordered'] = (bool) ($ctoc_flags_raw & 0x01);2120 $parsedFrame['ctoc_flags']['top_level'] = (bool) ($ctoc_flags_raw & 0x03);2121 2122 unset($ctoc_flags_raw, $terminator_position);2123 2124 if ($frame_offset < strlen($parsedFrame['data'])) {2125 $parsedFrame['subframes'] = array();2126 while ($frame_offset < strlen($parsedFrame['data'])) {2127 // <Optional embedded sub-frames>2128 $subframe = array();2129 $subframe['name'] = substr($parsedFrame['data'], $frame_offset, 4);2130 $frame_offset += 4;2131 $subframe['size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));2132 $frame_offset += 4;2133 $subframe['flags_raw'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));2134 $frame_offset += 2;2135 if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {2136 $this->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)');2137 break;2138 }2139 $subframe_rawdata = substr($parsedFrame['data'], $frame_offset, $subframe['size']);2140 $frame_offset += $subframe['size'];2141 2142 $subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));2143 $subframe['text'] = substr($subframe_rawdata, 1);2144 $subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);2145 $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;2146 switch (substr($encoding_converted_text, 0, 2)) {2147 case "\xFF\xFE":2148 case "\xFE\xFF":2149 switch (strtoupper($info['id3v2']['encoding'])) {2150 case 'ISO-8859-1':2151 case 'UTF-8':2152 $encoding_converted_text = substr($encoding_converted_text, 2);2153 // remove unwanted byte-order-marks2154 break;2155 default:2156 // ignore2157 break;2158 }2159 break;2160 default:2161 // do not remove BOM2162 break;2163 }2164 2165 if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {2166 if ($subframe['name'] == 'TIT2') {2167 2173 $parsedFrame['toc_name'] = $encoding_converted_text; 2168 2174 } elseif ($subframe['name'] == 'TIT3') { … … 2182 2188 } 2183 2189 2184 2190 /** 2191 * @param string $data 2192 * 2193 * @return string 2194 */ 2185 2195 public function DeUnsynchronise($data) { 2186 2196 return str_replace("\xFF\x00", "\xFF", $data); 2187 2197 } 2188 2198 2199 /** 2200 * @param int $index 2201 * 2202 * @return string 2203 */ 2189 2204 public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { 2190 2205 static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( … … 2197 2212 } 2198 2213 2214 /** 2215 * @param int $index 2216 * 2217 * @return string 2218 */ 2199 2219 public function LookupExtendedHeaderRestrictionsTextEncodings($index) { 2200 2220 static $LookupExtendedHeaderRestrictionsTextEncodings = array( … … 2205 2225 } 2206 2226 2227 /** 2228 * @param int $index 2229 * 2230 * @return string 2231 */ 2207 2232 public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { 2208 2233 static $LookupExtendedHeaderRestrictionsTextFieldSize = array( … … 2215 2240 } 2216 2241 2242 /** 2243 * @param int $index 2244 * 2245 * @return string 2246 */ 2217 2247 public function LookupExtendedHeaderRestrictionsImageEncoding($index) { 2218 2248 static $LookupExtendedHeaderRestrictionsImageEncoding = array( … … 2223 2253 } 2224 2254 2255 /** 2256 * @param int $index 2257 * 2258 * @return string 2259 */ 2225 2260 public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { 2226 2261 static $LookupExtendedHeaderRestrictionsImageSizeSize = array( … … 2233 2268 } 2234 2269 2270 /** 2271 * @param string $currencyid 2272 * 2273 * @return string 2274 */ 2235 2275 public function LookupCurrencyUnits($currencyid) { 2236 2276 … … 2429 2469 } 2430 2470 2431 2471 /** 2472 * @param string $currencyid 2473 * 2474 * @return string 2475 */ 2432 2476 public function LookupCurrencyCountry($currencyid) { 2433 2477 … … 2625 2669 } 2626 2670 2627 2628 2671 /** 2672 * @param string $languagecode 2673 * @param bool $casesensitive 2674 * 2675 * @return string 2676 */ 2629 2677 public static function LanguageLookup($languagecode, $casesensitive=false) { 2630 2678 … … 3082 3130 } 3083 3131 3084 3132 /** 3133 * @param int $index 3134 * 3135 * @return string 3136 */ 3085 3137 public static function ETCOEventLookup($index) { 3086 3138 if (($index >= 0x17) && ($index <= 0xDF)) { … … 3126 3178 } 3127 3179 3180 /** 3181 * @param int $index 3182 * 3183 * @return string 3184 */ 3128 3185 public static function SYTLContentTypeLookup($index) { 3129 3186 static $SYTLContentTypeLookup = array( … … 3142 3199 } 3143 3200 3201 /** 3202 * @param int $index 3203 * @param bool $returnarray 3204 * 3205 * @return array|string 3206 */ 3144 3207 public static function APICPictureTypeLookup($index, $returnarray=false) { 3145 3208 static $APICPictureTypeLookup = array( … … 3172 3235 } 3173 3236 3237 /** 3238 * @param int $index 3239 * 3240 * @return string 3241 */ 3174 3242 public static function COMRReceivedAsLookup($index) { 3175 3243 static $COMRReceivedAsLookup = array( … … 3188 3256 } 3189 3257 3258 /** 3259 * @param int $index 3260 * 3261 * @return string 3262 */ 3190 3263 public static function RVA2ChannelTypeLookup($index) { 3191 3264 static $RVA2ChannelTypeLookup = array( … … 3204 3277 } 3205 3278 3279 /** 3280 * @param string $framename 3281 * 3282 * @return string 3283 */ 3206 3284 public static function FrameNameLongLookup($framename) { 3207 3285 … … 3355 3433 UFI Unique file identifier 3356 3434 UFID Unique file identifier 3357 ULT Unsy chronised lyric/text transcription3435 ULT Unsynchronised lyric/text transcription 3358 3436 USER Terms of use 3359 3437 USLT Unsynchronised lyric/text transcription … … 3387 3465 } 3388 3466 3389 3467 /** 3468 * @param string $framename 3469 * 3470 * @return string 3471 */ 3390 3472 public static function FrameNameShortLookup($framename) { 3391 3473 … … 3539 3621 UFI unique_file_identifier 3540 3622 UFID unique_file_identifier 3541 ULT unsy chronised_lyric3623 ULT unsynchronised_lyric 3542 3624 USER terms_of_use 3543 3625 USLT unsynchronised_lyric … … 3567 3649 } 3568 3650 3651 /** 3652 * @param string $encoding 3653 * 3654 * @return string 3655 */ 3569 3656 public static function TextEncodingTerminatorLookup($encoding) { 3570 3657 // http://www.id3.org/id3v2.4.0-structure.txt … … 3580 3667 } 3581 3668 3669 /** 3670 * @param int $encoding 3671 * 3672 * @return string 3673 */ 3582 3674 public static function TextEncodingNameLookup($encoding) { 3583 3675 // http://www.id3.org/id3v2.4.0-structure.txt … … 3593 3685 } 3594 3686 3687 /** 3688 * @param string $string 3689 * @param string $terminator 3690 * 3691 * @return string 3692 */ 3693 public static function RemoveStringTerminator($string, $terminator) { 3694 // Null terminator at end of comment string is somewhat ambiguous in the specification, may or may not be implemented by various taggers. Remove terminator only if present. 3695 // https://github.com/JamesHeinrich/getID3/issues/121 3696 // https://community.mp3tag.de/t/x-trailing-nulls-in-id3v2-comments/19227 3697 if (substr($string, -strlen($terminator), strlen($terminator)) === $terminator) { 3698 $string = substr($string, 0, -strlen($terminator)); 3699 } 3700 return $string; 3701 } 3702 3703 /** 3704 * @param string $string 3705 * 3706 * @return string 3707 */ 3708 public static function MakeUTF16emptyStringEmpty($string) { 3709 if (in_array($string, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) { 3710 // if string only contains a BOM or terminator then make it actually an empty string 3711 $string = ''; 3712 } 3713 return $string; 3714 } 3715 3716 /** 3717 * @param string $framename 3718 * @param int $id3v2majorversion 3719 * 3720 * @return bool|int 3721 */ 3595 3722 public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { 3596 3723 switch ($id3v2majorversion) { … … 3607 3734 } 3608 3735 3736 /** 3737 * @param string $numberstring 3738 * @param bool $allowdecimal 3739 * @param bool $allownegative 3740 * 3741 * @return bool 3742 */ 3609 3743 public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { 3610 3744 for ($i = 0; $i < strlen($numberstring); $i++) { 3611 if ((chr($numberstring {$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {3612 if (($numberstring {$i}== '.') && $allowdecimal) {3745 if ((chr($numberstring[$i]) < chr('0')) || (chr($numberstring[$i]) > chr('9'))) { 3746 if (($numberstring[$i] == '.') && $allowdecimal) { 3613 3747 // allowed 3614 } elseif (($numberstring {$i}== '-') && $allownegative && ($i == 0)) {3748 } elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) { 3615 3749 // allowed 3616 3750 } else { … … 3622 3756 } 3623 3757 3758 /** 3759 * @param string $datestamp 3760 * 3761 * @return bool 3762 */ 3624 3763 public static function IsValidDateStampString($datestamp) { 3625 3764 if (strlen($datestamp) != 8) { … … 3650 3789 } 3651 3790 3791 /** 3792 * @param int $majorversion 3793 * 3794 * @return int 3795 */ 3652 3796 public static function ID3v2HeaderLength($majorversion) { 3653 3797 return (($majorversion == 2) ? 6 : 10); 3654 3798 } 3655 3799 3800 /** 3801 * @param string $frame_name 3802 * 3803 * @return string|false 3804 */ 3656 3805 public static function ID3v22iTunesBrokenFrameName($frame_name) { 3657 3806 // iTunes (multiple versions) has been known to write ID3v2.3 style frames … … 3740 3889 3741 3890 } 3891
Note: See TracChangeset
for help on using the changeset viewer.