Make WordPress Core


Ignore:
Timestamp:
09/14/2019 07:06:09 PM (6 years ago)
Author:
jorbin
Message:

Update getID3 library to fix issues with PHP7.4

Updates to trunk version that includes fixes for PHP7.4

Changelog:
https://github.com/JamesHeinrich/getID3/compare/v1.9.14...00f3fbfd77e583099ca70a3cf0bc092e113d2b20

See: #47751,#47783.
Fixes: #48040.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/ID3/module.tag.id3v2.php

    r41196 r46112  
    11<?php
     2
    23/////////////////////////////////////////////////////////////////
    34/// 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                            //
    99/////////////////////////////////////////////////////////////////
    1010///                                                            //
     
    2121    public $StartingOffset = 0;
    2222
     23    /**
     24     * @return bool
     25     */
    2326    public function Analyze() {
    2427        $info = &$this->getid3->info;
     
    5760        if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
    5861
    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]);
    6164
    6265            // shortcut
     
    7780        }
    7881
    79         $id3_flags = ord($header{5});
     82        $id3_flags = ord($header[5]);
    8083        switch ($id3v2_majorversion) {
    8184            case 2:
     
    258261                    $thisfile_id3v2['padding']['valid']  = true;
    259262                    for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
    260                         if ($framedata{$i} != "\x00") {
     263                        if ($framedata[$i] != "\x00") {
    261264                            $thisfile_id3v2['padding']['valid'] = false;
    262265                            $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
     
    267270                    break; // skip rest of ID3v2 header
    268271                }
     272                $frame_header = null;
     273                $frame_name   = null;
     274                $frame_size   = null;
     275                $frame_flags  = null;
    269276                if ($id3v2_majorversion == 2) {
    270277                    // Frame ID  $xx xx xx (three characters)
     
    320327                    $len = strlen($framedata);
    321328                    for ($i = 0; $i < $len; $i++) {
    322                         if ($framedata{$i} != "\x00") {
     329                        if ($framedata[$i] != "\x00") {
    323330                            $thisfile_id3v2['padding']['valid'] = false;
    324331                            $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
     
    428435            if (substr($footer, 0, 3) == '3DI') {
    429436                $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]);
    432439            }
    433440            if ($thisfile_id3v2['majorversion_footer'] <= 4) {
    434                 $id3_flags = ord(substr($footer{5}));
     441                $id3_flags = ord($footer[5]);
    435442                $thisfile_id3v2_flags['unsynch_footer']  = (bool) ($id3_flags & 0x80);
    436443                $thisfile_id3v2_flags['extfoot_footer']  = (bool) ($id3_flags & 0x40);
     
    453460        }
    454461
    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) {
    457464                if (strstr($value, '/')) {
    458                     list($thisfile_id3v2['comments']['tracknum'][$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]);
    459466                }
    460467            }
     
    499506    }
    500507
    501 
     508    /**
     509     * @param string $genrestring
     510     *
     511     * @return array
     512     */
    502513    public function ParseID3v2GenreString($genrestring) {
    503514        // Parse genres into arrays of genreName and genreID
     
    531542            $element = trim($element);
    532543            if ($element) {
    533                 if (preg_match('#^[0-9]{1,3}#', $element)) {
     544                if (preg_match('#^[0-9]{1,3}$#', $element)) {
    534545                    $clean_genres[] = getid3_id3v1::LookupGenreName($element);
    535546                } else {
     
    541552    }
    542553
    543 
     554    /**
     555     * @param array $parsedFrame
     556     *
     557     * @return bool
     558     */
    544559    public function ParseID3v2Frame(&$parsedFrame) {
    545560
     
    658673                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    659674            }
    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']);
    665677            $parsedFrame['encodingid']  = $frame_textencoding;
    666678            $parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
    667679
    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']));
    669681            $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
     682            $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
    670683            if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    671684                $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
     
    679692
    680693
    681         } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
     694        } elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame
    682695            //   There may only be one text information frame of its kind in an tag.
    683696            // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
     
    693706
    694707            $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     708            $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
    695709
    696710            $parsedFrame['encodingid'] = $frame_textencoding;
    697711            $parsedFrame['encoding']   = $this->TextEncodingNameLookup($frame_textencoding);
    698 
    699712            if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    700713                // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
     
    752765                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    753766            }
    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 blank
    757                 $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 0x00
    764             }
    765             if ($frame_terminatorpos) {
    766                 // there are null bytes after the data - this is not according to spec
    767                 // only use data up to first null byte
    768                 $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
    769             } else {
    770                 // no null bytes following data, just use all data
    771                 $frame_urldata = (string) $parsedFrame['data'];
    772             }
    773 
    774767            $parsedFrame['encodingid']  = $frame_textencoding;
    775768            $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
    779774            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']);
    781776            }
    782777            unset($parsedFrame['data']);
    783778
    784779
    785         } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
     780        } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames
    786781            //   There may only be one URL link frame of its kind in a tag,
    787782            //   except when stated otherwise in the frame description
     
    790785            // URL              <text string>
    791786
    792             $parsedFrame['url'] = trim($parsedFrame['data']);
     787            $parsedFrame['url'] = trim($parsedFrame['data']); // always ISO-8859-1
    793788            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']);
    795790            }
    796791            unset($parsedFrame['data']);
     
    814809            $parsedFrame['data_raw']   = (string) substr($parsedFrame['data'], $frame_offset);
    815810
    816             // http://www.getid3.org/phpBB3/viewtopic.php?t=1369
     811            // https://www.getid3.org/phpBB3/viewtopic.php?t=1369
    817812            // "this tag typically contains null terminated strings, which are associated in pairs"
    818813            // "there are users that use the tag incorrectly"
     
    934929            $parsedFrame['bitsformsdeviation']      = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
    935930            $parsedFrame['data'] = substr($parsedFrame['data'], 10);
     931            $deviationbitstream = '';
    936932            while ($frame_offset < strlen($parsedFrame['data'])) {
    937933                $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
     
    995991                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    996992            }
    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']);
    1002995            $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
     996            $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
    1003997
    1004998            $parsedFrame['encodingid']   = $frame_textencoding;
     
    10071001            $parsedFrame['language']     = $frame_language;
    10081002            $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
    1009             $parsedFrame['description']  = $frame_description;
    10101003            if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    10111004                $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
     
    10621055
    10631056                    $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)) {
    10651058                        // timestamp probably omitted for first data item
    10661059                    } else {
     
    11031096                    $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    11041097                }
    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']);
    11101100                $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
     1101                $frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator);
    11111102
    11121103                $parsedFrame['encodingid']   = $frame_textencoding;
     
    11151106                $parsedFrame['language']     = $frame_language;
    11161107                $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
    1117                 $parsedFrame['description']  = $frame_description;
    11181108                $parsedFrame['data']         = $frame_text;
    11191109                if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
     
    14081398                    $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    14091399                }
    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;
    14141409                }
    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']    = '';
    14301416                $imageinfo = array();
    14311417                if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
    14321418                    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]);
    14341420                        if ($imagechunkcheck[0]) {
    14351421                            $parsedFrame['image_width']  = $imagechunkcheck[0];
     
    14471433                        break;
    14481434                    }
     1435                    $dir = '';
    14491436                    if ($this->getid3->option_save_attachments === true) {
    14501437                        // great
     
    15341521                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    15351522            }
    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']);
    15411525            $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
    15421526
     
    15471531            $parsedFrame['mime']        = $frame_mimetype;
    15481532            $parsedFrame['filename']    = $frame_filename;
    1549             $parsedFrame['description'] = $frame_description;
    15501533            unset($parsedFrame['data']);
    15511534
     
    16171600
    16181601            $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']);
    16241604            $frame_offset = $frame_terminatorpos + strlen("\x00");
    16251605
    16261606            $parsedFrame['ownerid']     = $frame_ownerid;
    16271607            $parsedFrame['data']        = (string) substr($parsedFrame['data'], $frame_offset);
    1628             $parsedFrame['description'] = $frame_description;
    16291608            unset($parsedFrame['data']);
    16301609
     
    17221701            $parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
    17231702
    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));
    17251705            if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
    17261706                $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
     
    17601740
    17611741            $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
     1742            $parsedFrame['seller'] = $this->RemoveStringTerminator($parsedFrame['seller'], $this->TextEncodingTerminatorLookup($frame_textencoding));
    17621743            unset($parsedFrame['data']);
    17631744
     
    18181799                $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
    18191800            }
    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']);
    18251803            $frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
    18261804
     
    18391817            $parsedFrame['receivedas']        = $this->COMRReceivedAsLookup($frame_receivedasid);
    18401818            $parsedFrame['sellername']        = $frame_sellername;
    1841             $parsedFrame['description']       = $frame_description;
    18421819            $parsedFrame['mime']              = $frame_mimetype;
    18431820            $parsedFrame['logo']              = $frame_sellerlogo;
     
    20031980            // Start time      $xx xx xx xx
    20041981            // End time        $xx xx xx xx
    2005             // Start offset    $xx xx xx xx
    2006             // End offset      $xx xx xx xx
    2007             // <Optional embedded sub-frames>
     1982            // Start offset    $xx xx xx xx
     1983            // End offset      $xx xx xx xx
     1984            // <Optional embedded sub-frames>
    20081985
    20091986            $frame_offset = 0;
     
    20382015                    if ($subframe['size'] > (strlen($parsedFrame['data']) - $frame_offset)) {
    20392016                        $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)');
    20402143                        break;
    20412144                    }
     
    20682171                    if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
    20692172                        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.0
    2097             // <ID3v2.3 or ID3v2.4 frame header, ID: "CTOC">           (10 bytes)
    2098             // Element ID      <text string> $00
    2099             // CTOC flags        %xx
    2100             // Entry count       $xx
    2101             // 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-marks
    2154                                     break;
    2155                                 default:
    2156                                     // ignore
    2157                                     break;
    2158                             }
    2159                             break;
    2160                         default:
    2161                             // do not remove BOM
    2162                             break;
    2163                     }
    2164 
    2165                     if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
    2166                         if ($subframe['name'] == 'TIT2') {
    21672173                            $parsedFrame['toc_name']        = $encoding_converted_text;
    21682174                        } elseif ($subframe['name'] == 'TIT3') {
     
    21822188    }
    21832189
    2184 
     2190    /**
     2191     * @param string $data
     2192     *
     2193     * @return string
     2194     */
    21852195    public function DeUnsynchronise($data) {
    21862196        return str_replace("\xFF\x00", "\xFF", $data);
    21872197    }
    21882198
     2199    /**
     2200     * @param int $index
     2201     *
     2202     * @return string
     2203     */
    21892204    public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) {
    21902205        static $LookupExtendedHeaderRestrictionsTagSizeLimits = array(
     
    21972212    }
    21982213
     2214    /**
     2215     * @param int $index
     2216     *
     2217     * @return string
     2218     */
    21992219    public function LookupExtendedHeaderRestrictionsTextEncodings($index) {
    22002220        static $LookupExtendedHeaderRestrictionsTextEncodings = array(
     
    22052225    }
    22062226
     2227    /**
     2228     * @param int $index
     2229     *
     2230     * @return string
     2231     */
    22072232    public function LookupExtendedHeaderRestrictionsTextFieldSize($index) {
    22082233        static $LookupExtendedHeaderRestrictionsTextFieldSize = array(
     
    22152240    }
    22162241
     2242    /**
     2243     * @param int $index
     2244     *
     2245     * @return string
     2246     */
    22172247    public function LookupExtendedHeaderRestrictionsImageEncoding($index) {
    22182248        static $LookupExtendedHeaderRestrictionsImageEncoding = array(
     
    22232253    }
    22242254
     2255    /**
     2256     * @param int $index
     2257     *
     2258     * @return string
     2259     */
    22252260    public function LookupExtendedHeaderRestrictionsImageSizeSize($index) {
    22262261        static $LookupExtendedHeaderRestrictionsImageSizeSize = array(
     
    22332268    }
    22342269
     2270    /**
     2271     * @param string $currencyid
     2272     *
     2273     * @return string
     2274     */
    22352275    public function LookupCurrencyUnits($currencyid) {
    22362276
     
    24292469    }
    24302470
    2431 
     2471    /**
     2472     * @param string $currencyid
     2473     *
     2474     * @return string
     2475     */
    24322476    public function LookupCurrencyCountry($currencyid) {
    24332477
     
    26252669    }
    26262670
    2627 
    2628 
     2671    /**
     2672     * @param string $languagecode
     2673     * @param bool   $casesensitive
     2674     *
     2675     * @return string
     2676     */
    26292677    public static function LanguageLookup($languagecode, $casesensitive=false) {
    26302678
     
    30823130    }
    30833131
    3084 
     3132    /**
     3133     * @param int $index
     3134     *
     3135     * @return string
     3136     */
    30853137    public static function ETCOEventLookup($index) {
    30863138        if (($index >= 0x17) && ($index <= 0xDF)) {
     
    31263178    }
    31273179
     3180    /**
     3181     * @param int $index
     3182     *
     3183     * @return string
     3184     */
    31283185    public static function SYTLContentTypeLookup($index) {
    31293186        static $SYTLContentTypeLookup = array(
     
    31423199    }
    31433200
     3201    /**
     3202     * @param int   $index
     3203     * @param bool $returnarray
     3204     *
     3205     * @return array|string
     3206     */
    31443207    public static function APICPictureTypeLookup($index, $returnarray=false) {
    31453208        static $APICPictureTypeLookup = array(
     
    31723235    }
    31733236
     3237    /**
     3238     * @param int $index
     3239     *
     3240     * @return string
     3241     */
    31743242    public static function COMRReceivedAsLookup($index) {
    31753243        static $COMRReceivedAsLookup = array(
     
    31883256    }
    31893257
     3258    /**
     3259     * @param int $index
     3260     *
     3261     * @return string
     3262     */
    31903263    public static function RVA2ChannelTypeLookup($index) {
    31913264        static $RVA2ChannelTypeLookup = array(
     
    32043277    }
    32053278
     3279    /**
     3280     * @param string $framename
     3281     *
     3282     * @return string
     3283     */
    32063284    public static function FrameNameLongLookup($framename) {
    32073285
     
    33553433            UFI Unique file identifier
    33563434            UFID    Unique file identifier
    3357             ULT Unsychronised lyric/text transcription
     3435            ULT Unsynchronised lyric/text transcription
    33583436            USER    Terms of use
    33593437            USLT    Unsynchronised lyric/text transcription
     
    33873465    }
    33883466
    3389 
     3467    /**
     3468     * @param string $framename
     3469     *
     3470     * @return string
     3471     */
    33903472    public static function FrameNameShortLookup($framename) {
    33913473
     
    35393621            UFI unique_file_identifier
    35403622            UFID    unique_file_identifier
    3541             ULT unsychronised_lyric
     3623            ULT unsynchronised_lyric
    35423624            USER    terms_of_use
    35433625            USLT    unsynchronised_lyric
     
    35673649    }
    35683650
     3651    /**
     3652     * @param string $encoding
     3653     *
     3654     * @return string
     3655     */
    35693656    public static function TextEncodingTerminatorLookup($encoding) {
    35703657        // http://www.id3.org/id3v2.4.0-structure.txt
     
    35803667    }
    35813668
     3669    /**
     3670     * @param int $encoding
     3671     *
     3672     * @return string
     3673     */
    35823674    public static function TextEncodingNameLookup($encoding) {
    35833675        // http://www.id3.org/id3v2.4.0-structure.txt
     
    35933685    }
    35943686
     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     */
    35953722    public static function IsValidID3v2FrameName($framename, $id3v2majorversion) {
    35963723        switch ($id3v2majorversion) {
     
    36073734    }
    36083735
     3736    /**
     3737     * @param string $numberstring
     3738     * @param bool   $allowdecimal
     3739     * @param bool   $allownegative
     3740     *
     3741     * @return bool
     3742     */
    36093743    public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
    36103744        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) {
    36133747                    // allowed
    3614                 } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
     3748                } elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) {
    36153749                    // allowed
    36163750                } else {
     
    36223756    }
    36233757
     3758    /**
     3759     * @param string $datestamp
     3760     *
     3761     * @return bool
     3762     */
    36243763    public static function IsValidDateStampString($datestamp) {
    36253764        if (strlen($datestamp) != 8) {
     
    36503789    }
    36513790
     3791    /**
     3792     * @param int $majorversion
     3793     *
     3794     * @return int
     3795     */
    36523796    public static function ID3v2HeaderLength($majorversion) {
    36533797        return (($majorversion == 2) ? 6 : 10);
    36543798    }
    36553799
     3800    /**
     3801     * @param string $frame_name
     3802     *
     3803     * @return string|false
     3804     */
    36563805    public static function ID3v22iTunesBrokenFrameName($frame_name) {
    36573806        // iTunes (multiple versions) has been known to write ID3v2.3 style frames
     
    37403889
    37413890}
     3891
Note: See TracChangeset for help on using the changeset viewer.