Search code examples
phpxmlxml-parsingnon-ascii-charactersshoutcast

Special character 0x85 (octal 205, decimal 133) is crashing PHP's SimpleXMLElement


I am trying to parse an XML string containing the special octal character 205. The XML string comes from a shoutcast server. It seems that this character crashes the internals of SimpleXMLElement.

// This is my metadata.php file:
<?php
    header('Content-type: application/json');
    $output = file_get_contents('error.xml.bak');
    $xml = new SimpleXMLElement($output, LIBXML_NOERROR|LIBXML_ERR_NONE) or die('Something is wrong');
    echo 'OK';
?>

I am getting the following error:

kazepis$ php metadata.php
PHP Fatal error:  Uncaught Exception: String could not be parsed as XML in /var/www/html/wolfclub/metadata.php:4
Stack trace:
#0 /var/www/html/radio/metadata.php(4): SimpleXMLElement->__construct('<?xml version="...', 32)
#1 {main}
thrown in /var/www/html/radio/metadata.php on line 4

You can find the sample XML with the problematic character (decimal 133, octal 205) here: https://wetransfer.com/downloads/f1b615c1cc09c8262cdd9965991b9cd420200123155505/801ab3 or inline:

<?xml version="1.0" standalone="yes" ?><!DOCTYPE SHOUTCASTSERVER [<!ELEMENT SHOUTCASTSERVER (CURRENTLISTENERS,PEAKLISTENERS,MAXLISTENERS,REPORTEDLISTENERS,AVERAGETIME,SERVERGENRE,SERVERURL,SERVERTITLE,SONGTITLE,SONGURL,IRC,ICQ,AIM,WEBHITS,STREAMHITS,STREAMSTATUS,BITRATE,CONTENT,VERSION,WEBDATA,LISTENERS,SONGHISTORY)><!ELEMENT CURRENTLISTENERS (#PCDATA)><!ELEMENT PEAKLISTENERS (#PCDATA)><!ELEMENT MAXLISTENERS (#PCDATA)><!ELEMENT REPORTEDLISTENERS (#PCDATA)><!ELEMENT AVERAGETIME (#PCDATA)><!ELEMENT SERVERGENRE (#PCDATA)><!ELEMENT SERVERURL (#PCDATA)><!ELEMENT SERVERTITLE (#PCDATA)><!ELEMENT SONGTITLE (#PCDATA)><!ELEMENT SONGURL (#PCDATA)><!ELEMENT IRC (#PCDATA)><!ELEMENT ICQ (#PCDATA)><!ELEMENT AIM (#PCDATA)><!ELEMENT WEBHITS (#PCDATA)><!ELEMENT STREAMHITS (#PCDATA)><!ELEMENT STREAMSTATUS (#PCDATA)><!ELEMENT BITRATE (#PCDATA)><!ELEMENT CONTENT (#PCDATA)><!ELEMENT VERSION (#PCDATA)><!ELEMENT WEBDATA (INDEX,LISTEN,PALM7,LOGIN,LOGINFAIL,PLAYED,COOKIE,ADMIN,UPDINFO,KICKSRC,KICKDST,UNBANDST,BANDST,VIEWBAN,UNRIPDST,RIPDST,VIEWRIP,VIEWXML,VIEWLOG,INVALID)><!ELEMENT INDEX (#PCDATA)><!ELEMENT LISTEN (#PCDATA)><!ELEMENT PALM7 (#PCDATA)><!ELEMENT LOGIN (#PCDATA)><!ELEMENT LOGINFAIL (#PCDATA)><!ELEMENT PLAYED (#PCDATA)><!ELEMENT COOKIE (#PCDATA)><!ELEMENT ADMIN (#PCDATA)><!ELEMENT UPDINFO (#PCDATA)><!ELEMENT KICKSRC (#PCDATA)><!ELEMENT KICKDST (#PCDATA)><!ELEMENT UNBANDST (#PCDATA)><!ELEMENT BANDST (#PCDATA)><!ELEMENT VIEWBAN (#PCDATA)><!ELEMENT UNRIPDST (#PCDATA)><!ELEMENT RIPDST (#PCDATA)><!ELEMENT VIEWRIP (#PCDATA)><!ELEMENT VIEWXML (#PCDATA)><!ELEMENT VIEWLOG (#PCDATA)><!ELEMENT INVALID (#PCDATA)><!ELEMENT LISTENERS (LISTENER*)><!ELEMENT LISTENER (HOSTNAME,USERAGENT,UNDERRUNS,CONNECTTIME, POINTER, UID)><!ELEMENT HOSTNAME (#PCDATA)><!ELEMENT USERAGENT (#PCDATA)><!ELEMENT UNDERRUNS (#PCDATA)><!ELEMENT CONNECTTIME (#PCDATA)><!ELEMENT POINTER (#PCDATA)><!ELEMENT UID (#PCDATA)><!ELEMENT SONGHISTORY (SONG*)><!ELEMENT SONG (PLAYEDAT, TITLE)><!ELEMENT PLAYEDAT (#PCDATA)><!ELEMENT TITLE (#PCDATA)>]><SHOUTCASTSERVER><CURRENTLISTENERS>1</CURRENTLISTENERS><PEAKLISTENERS>3</PEAKLISTENERS><MAXLISTENERS>5000</MAXLISTENERS><REPORTEDLISTENERS>1</REPORTEDLISTENERS><AVERAGETIME>1</AVERAGETIME><SERVERGENRE>public</SERVERGENRE><SERVERURL>http://www.virtualdj.com/</SERVERURL><SERVERTITLE>wolf</SERVERTITLE><SONGTITLE>BARRY WHITE                    - YOU&#x27;RE THE FIRST,THE LAST ▒   </SONGTITLE><SONGURL></SONGURL><IRC>wolf</IRC><ICQ>wolf</ICQ><AIM>wolf</AIM><WEBHITS>80</WEBHITS><STREAMHITS>6</STREAMHITS><STREAMSTATUS>1</STREAMSTATUS><BITRATE>96</BITRATE><CONTENT>audio/mpeg</CONTENT><VERSION>1.9.8</VERSION><WEBDATA><INDEX>0</INDEX><LISTEN>0</LISTEN><PALM7>6</PALM7><LOGIN>0</LOGIN><LOGINFAIL>0</LOGINFAIL><PLAYED>0</PLAYED><COOKIE>0</COOKIE><ADMIN>1</ADMIN><UPDINFO>1</UPDINFO><KICKSRC>0</KICKSRC><KICKDST>0</KICKDST><UNBANDST>0</UNBANDST><BANDST>0</BANDST><VIEWBAN>0</VIEWBAN><UNRIPDST>0</UNRIPDST><RIPDST>0</RIPDST><VIEWRIP>0</VIEWRIP><VIEWXML>69</VIEWXML><VIEWLOG>0</VIEWLOG><INVALID>3</INVALID></WEBDATA><LISTENERS><LISTENER><HOSTNAME>78.129.222.56</HOSTNAME><USERAGENT>curl/7.29.0</USERAGENT><UNDERRUNS>0</UNDERRUNS><CONNECTTIME>216</CONNECTTIME><POINTER>0</POINTER><UID>2</UID></LISTENER></LISTENERS><SONGHISTORY><SONG><PLAYEDAT>1579791561</PLAYEDAT><TITLE>BARRY WHITE                    - YOU&#x27;RE THE FIRST,THE LAST ▒   </TITLE></SONG></SONGHISTORY></SHOUTCASTSERVER>

Any ideas why this is happening?

My operating system:

PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian

Thank you!

PHP 7.3.11-1~deb10u1 (cli) (built: Oct 26 2019 14:14:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.11, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.11-1~deb10u1, Copyright (c) 1999-2018, by Zend Technologies

problematic character


Solution

  • I found this: https://www.php.net/manual/en/class.simplexmlelement.php#107869

    However the $errors array as shown in the line $errors = libxml_get_errors(); is always empty in my case. So that snippet did not help. Moreover i also got the following warnings:

    PHP Warning:  DOMDocument::loadXML(): Input is not proper UTF-8, indicate encoding !\nBytes: 0x85 0x20 0x20 0x20 in Entity, line: 1 in /var/www/html/radio/metadata.php on line 6
    [Fri Jan 24 18:56:12.290661 2020] [php7:warn] [pid 17910] [client 130.xxx.xxx.xxx:xxxxx] PHP Warning:  simplexml_import_dom(): Invalid Nodetype to import in /var/www/html/radio/metadata.php on line 12
    

    Anyway, I managed to get over this messy situation by using utf8_encode() to encode my string before feeding it to the SimpleXMLElement constructor.

    My resulting "test" php file is:

    <?php
    //      header('Content-type: application/json');
            $output = file_get_contents('error.xml.bak');
            $output = utf8_encode($output);
    
            $doc = new DOMDocument('1.0', 'utf-8');
            $doc->loadXML($output);
            var_dump($output);
    
            $errors = libxml_get_errors();
            var_dump($errors);
            $xml = simplexml_import_dom($doc);
    //      $xml = new SimpleXMLElement($output, LIBXML_NOERROR|LIBXML_ERR_NONE) or die('Something is wrong');
            var_dump($xml);
    ?>
    

    which results in the following printout without any errors or warning whatsoever...

    string(3396) "]>13500011publichttp://www.virtualdj.com/wolfBARRY WHITE - YOU'RE THE FIRST,THE LAST  wolfwolfwolf806196audio/mpeg1.9.800600001100000000690378.129.222.56curl/7.29.00216021579791561 " array(0) { } object(SimpleXMLElement)#2 (22) { ["CURRENTLISTENERS"]=> string(1) "1" ["PEAKLISTENERS"]=> string(1) "3" ["MAXLISTENERS"]=> string(4) "5000" ["REPORTEDLISTENERS"]=> string(1) "1" ["AVERAGETIME"]=> string(1) "1" ["SERVERGENRE"]=> string(6) "public" ["SERVERURL"]=> string(25) "http://www.virtualdj.com/" ["SERVERTITLE"]=> string(4) "wolf" ["SONGTITLE"]=> string(64) "BARRY WHITE - YOU'RE THE FIRST,THE LAST  " ["SONGURL"]=> object(SimpleXMLElement)#3 (0) { } ["IRC"]=> string(4) "wolf" ["ICQ"]=> string(4) "wolf" ["AIM"]=> string(4) "wolf" ["WEBHITS"]=> string(2) "80" ["STREAMHITS"]=> string(1) "6" ["STREAMSTATUS"]=> string(1) "1" ["BITRATE"]=> string(2) "96" ["CONTENT"]=> string(10) "audio/mpeg" ["VERSION"]=> string(5) "1.9.8" ["WEBDATA"]=> object(SimpleXMLElement)#4 (20) { ["INDEX"]=> string(1) "0" ["LISTEN"]=> string(1) "0" ["PALM7"]=> string(1) "6" ["LOGIN"]=> string(1) "0" ["LOGINFAIL"]=> string(1) "0" ["PLAYED"]=> string(1) "0" ["COOKIE"]=> string(1) "0" ["ADMIN"]=> string(1) "1" ["UPDINFO"]=> string(1) "1" ["KICKSRC"]=> string(1) "0" ["KICKDST"]=> string(1) "0" ["UNBANDST"]=> string(1) "0" ["BANDST"]=> string(1) "0" ["VIEWBAN"]=> string(1) "0" ["UNRIPDST"]=> string(1) "0" ["RIPDST"]=> string(1) "0" ["VIEWRIP"]=> string(1) "0" ["VIEWXML"]=> string(2) "69" ["VIEWLOG"]=> string(1) "0" ["INVALID"]=> string(1) "3" } ["LISTENERS"]=> object(SimpleXMLElement)#5 (1) { ["LISTENER"]=> object(SimpleXMLElement)#7 (6) { ["HOSTNAME"]=> string(13) "78.129.222.56" ["USERAGENT"]=> string(11) "curl/7.29.0" ["UNDERRUNS"]=> string(1) "0" ["CONNECTTIME"]=> string(3) "216" ["POINTER"]=> string(1) "0" ["UID"]=> string(1) "2" } } ["SONGHISTORY"]=> object(SimpleXMLElement)#6 (1) { ["SONG"]=> object(SimpleXMLElement)#7 (2) { ["PLAYEDAT"]=> string(10) "1579791561" ["TITLE"]=> string(64) "BARRY WHITE - YOU'RE THE FIRST,THE LAST  " } } } 
    

    NOTE: The problematic character is STILL THERE! \u0085 but properly encoded so I guess that is why it is not a problem any more... enter image description here

    I also tried the previous version of the code with the SimpleXMLElement constructor:

    <?php
            $output = file_get_contents('error.xml.bak');
            $output = utf8_encode($output);
    
            $xml = new SimpleXMLElement($output, LIBXML_NOERROR|LIBXML_ERR_NONE) or die('Something is wrong');
            echo json_encode($xml);
    ?>
    

    which also worked as expected:

    {"CURRENTLISTENERS":"1","PEAKLISTENERS":"3","MAXLISTENERS":"5000","REPORTEDLISTENERS":"1","AVERAGETIME":"1","SERVERGENRE":"public","SERVERURL":"http:\/\/www.virtualdj.com\/","SERVERTITLE":"wolf","SONGTITLE":"BARRY WHITE - YOU'RE THE FIRST,THE LAST \u0085 ","SONGURL":{},"IRC":"wolf","ICQ":"wolf","AIM":"wolf","WEBHITS":"80","STREAMHITS":"6","STREAMSTATUS":"1","BITRATE":"96","CONTENT":"audio\/mpeg","VERSION":"1.9.8","WEBDATA":{"INDEX":"0","LISTEN":"0","PALM7":"6","LOGIN":"0","LOGINFAIL":"0","PLAYED":"0","COOKIE":"0","ADMIN":"1","UPDINFO":"1","KICKSRC":"0","KICKDST":"0","UNBANDST":"0","BANDST":"0","VIEWBAN":"0","UNRIPDST":"0","RIPDST":"0","VIEWRIP":"0","VIEWXML":"69","VIEWLOG":"0","INVALID":"3"},"LISTENERS":{"LISTENER":{"HOSTNAME":"78.129.222.56","USERAGENT":"curl\/7.29.0","UNDERRUNS":"0","CONNECTTIME":"216","POINTER":"0","UID":"2"}},"SONGHISTORY":{"SONG":{"PLAYEDAT":"1579791561","TITLE":"BARRY WHITE - YOU'RE THE FIRST,THE LAST \u0085 "}}}
    

    NOTE the \u0085 towards the end...