NuSoap and UTF-8 / mbstring incompatibility

Help
Adrien
2011-03-16
2013-06-06
  • Adrien
    Adrien
    2011-03-16

    Hi,

    Their is a bug in NuSoap with UTF-8 and mbstring with option func_overload.
    func_overload replace the function "strlen" by "mb_strlen" who returns the number of caracters of a string.

    In NuSoap, strlen is used to count the size in bytes. But if the string is UTF-8, the returned size is not good.

    I've create a function named "strsize" to correct this problem :

    -- nusoap_org.php 2011-03-16 15:59:06.000000000 +0100
    +++ nusoap.php 2011-03-16 15:58:05.000000000 +0100
    @@ -390,6 +390,22 @@
    }

    /**
    + * Return size in bytes of a string
    + *
    + * @param string $str The string
    + * @return int The size in bytes
    + * @access private
    + */
    + function strsize($str) {
    + if (!function_exists('mb_strlen')) {
    + return strlen($str);
    + }
    + else {
    + return mb_strlen($str, '8bit');
    + }
    + }
    +
    + /**
    * serializes PHP values in accordance w/ section 5. Type information is
    * not serialized if $use == 'literal'.
    *
    @@ -675,7 +691,7 @@
         // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
         // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1

    - $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
    + $this->debug("In serializeEnvelope length=" .$this->strsize($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
    $this->debug("headers:");
    $this->appendDebug($this->varDump($headers));
    $this->debug("namespaces:");
    @@ -2566,7 +2582,7 @@
    */
    function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {

    - $this->debug('entered send() with data of length: '.strlen($data));
    + $this->debug('entered send() with data of length: '.$this->strsize($data));

    $this->tryagain = true;
    $tries = 0;
    @@ -2846,7 +2862,7 @@

    // add content-length header
    if ($this->request_method != 'GET') {
    - $this->setHeader('Content-Length', strlen($data));
    + $this->setHeader('Content-Length', $this->strsize($data));
    }

    // start building outgoing payload:
    @@ -2897,12 +2913,12 @@

      if ($this->io_method() == 'socket') {
    // send payload
    - if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
    + if(!fputs($this->fp, $this->outgoing_payload,$this->strsize($this->outgoing_payload))) {
    $this->setError('couldn\'t write message data to socket');
    $this->debug('couldn\'t write message data to socket');
    return false;
    }
    - $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
    + $this->debug('wrote data to socket, length = ' .$this->strsize($this->outgoing_payload));
    return true;
      } else if ($this->io_method() == 'curl') {
    // set payload
    @@ -2956,19 +2972,19 @@
    // We might EOF during header read.
    if(feof($this->fp)) {
    $this->incoming_payload = $data;
    - $this->debug('found no headers before EOF after length ' . strlen($data));
    + $this->debug('found no headers before EOF after length ' .$this->strsize($data));
    $this->debug("received before EOF:\n" . $data);
    $this->setError('server failed to send headers');
    return false;
    }

    $tmp = fgets($this->fp, 256);
    - $tmplen = strlen($tmp);
    + $tmplen =$this->strsize($tmp);
    $this->debug("read line of $tmplen bytes: " . trim($tmp));

    if ($tmplen == 0) {
    $this->incoming_payload = $data;
    - $this->debug('socket read of headers timed out after length ' . strlen($data));
    + $this->debug('socket read of headers timed out after length ' .$this->strsize($data));
    $this->debug("read before timeout: " . $data);
    $this->setError('socket read of headers timed out');
    return false;
    @@ -2992,7 +3008,7 @@
    }
    // store header data
    $this->incoming_payload .= $data;
    - $this->debug('found end of headers after length ' . strlen($data));
    + $this->debug('found end of headers after length ' .$this->strsize($data));
    // process headers
    $header_data = trim(substr($data,0,$pos));
    $header_array = explode($lb,$header_data);
    @@ -3037,11 +3053,11 @@
    do {
    if ($chunked) {
    $tmp = fgets($this->fp, 256);
    - $tmplen = strlen($tmp);
    + $tmplen =$this->strsize($tmp);
    $this->debug("read chunk line of $tmplen bytes");
    if ($tmplen == 0) {
    $this->incoming_payload = $data;
    - $this->debug('socket read of chunk length timed out after length ' . strlen($data));
    + $this->debug('socket read of chunk length timed out after length ' .$this->strsize($data));
    $this->debug("read before timeout:\n" . $data);
    $this->setError('socket read of chunk length timed out');
    return false;
    @@ -3053,11 +3069,11 @@
        while (($strlen < $content_length) && (!feof($this->fp))) {
        $readlen = min(8192, $content_length - $strlen);
    $tmp = fread($this->fp, $readlen);
    - $tmplen = strlen($tmp);
    + $tmplen =$this->strsize($tmp);
    $this->debug("read buffer of $tmplen bytes");
    if (($tmplen == 0) && (!feof($this->fp))) {
    $this->incoming_payload = $data;
    - $this->debug('socket read of body timed out after length ' . strlen($data));
    + $this->debug('socket read of body timed out after length ' .$this->strsize($data));
    $this->debug("read before timeout:\n" . $data);
    $this->setError('socket read of body timed out');
    return false;
    @@ -3067,11 +3083,11 @@
    }
    if ($chunked && ($content_length > 0)) {
    $tmp = fgets($this->fp, 256);
    - $tmplen = strlen($tmp);
    + $tmplen =$this->strsize($tmp);
    $this->debug("read chunk terminator of $tmplen bytes");
    if ($tmplen == 0) {
    $this->incoming_payload = $data;
    - $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
    + $this->debug('socket read of chunk terminator timed out after length ' .$this->strsize($data));
    $this->debug("read before timeout:\n" . $data);
    $this->setError('socket read of chunk terminator timed out');
    return false;
    @@ -3081,9 +3097,9 @@
    if (feof($this->fp)) {
    $this->debug('read to EOF');
    }
    - $this->debug('read body of length ' . strlen($data));
    + $this->debug('read body of length ' .$this->strsize($data));
    $this->incoming_payload .= $data;
    - $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
    + $this->debug('received a total of '.$this->strsize($this->incoming_payload).' bytes of data from server');

    // close filepointer
    if(
    @@ -3174,7 +3190,7 @@
    $header_array = explode($lb,$header_data);
    $data = ltrim(substr($data,$pos));
    $this->debug('found proper separation of headers and document');
    - $this->debug('cleaned data, stringlen: '.strlen($data));
    + $this->debug('cleaned data, stringlen: '.$this->strsize($data));
    // clean headers
    foreach ($header_array as $header_line) {
    $arr = explode(':',$header_line,2);
    @@ -3257,17 +3273,17 @@
    // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
    // this means there are no Zlib headers, although there should be
    $this->debug('The gzinflate function exists');
    - $datalen = strlen($data);
    + $datalen =$this->strsize($data);
    if ($this->incoming_headers == 'deflate') {
    if ($degzdata = @gzinflate($data)) {
        $data = $degzdata;
    -     $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
    -     if (strlen($data) < $datalen) {
    +     $this->debug('The payload has been inflated to ' .$this->strsize($data) . ' bytes');
    +     if ($this->strsize($data) < $datalen) {
        // test for the case that the payload has been compressed twice
        $this->debug('The inflated payload is smaller than the gzipped one; try again');
    if ($degzdata = @gzinflate($data)) {
        $data = $degzdata;
    -     $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
    +     $this->debug('The payload has been inflated again to ' .$this->strsize($data) . ' bytes');
    }
        }
        } else {
    @@ -3277,13 +3293,13 @@
    } elseif ($this->incoming_headers == 'gzip') {
    if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
    $data = $degzdata;
    -     $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
    -     if (strlen($data) < $datalen) {
    +     $this->debug('The payload has been un-gzipped to ' .$this->strsize($data) . ' bytes');
    +     if ($this->strsize($data) < $datalen) {
        // test for the case that the payload has been compressed twice
        $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
    if ($degzdata = @gzinflate(substr($data, 10))) {
        $data = $degzdata;
    -     $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
    +     $this->debug('The payload has been un-gzipped again to ' .$this->strsize($data) . ' bytes');
    }
        }
        } else {
    @@ -3307,7 +3323,7 @@
    $this->debug('No Content-Encoding header');
    }

    - if(strlen($data) == 0){
    + if($this->strsize($data) == 0){
    $this->debug('no data after headers!');
    $this->setError('no data present after HTTP headers');
    return false;
    @@ -4235,7 +4251,7 @@
    //begin code to compress payload - by John
    // NOTE: there is no way to know whether the Web server will also compress
    // this data.
    - if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers)) {
    + if ($this->strsize($payload) > 1024 && isset($this->headers) && isset($this->headers)) {
    if (strstr($this->headers, 'gzip')) {
    if (function_exists('gzencode')) {
    if (isset($this->debug_flag) && $this->debug_flag) {
    @@ -4266,7 +4282,7 @@
    }
    }
    //end code
    - $this->outgoing_headers = "Content-Length: ".strlen($payload);
    + $this->outgoing_headers = "Content-Length: ".$this->strsize($payload);
    reset($this->outgoing_headers);
    foreach($this->outgoing_headers as $hdr){
    header($hdr, false);
    @@ -4304,7 +4320,7 @@
    * @access   private
    */
         function parseRequest($headers, $data) {
    - $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
    + $this->debug('Entering parseRequest() for data of length ' .$this->strsize($data) . ' headers:');
    $this->appendDebug($this->varDump($headers));
         if (!isset($headers)) {
    $this->setError('Request not of type text/xml (no content-type header)');
    @@ -6606,7 +6622,7 @@
    } else {
    $this->debug('No XML declaration');
    }
    - $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding);
    + $this->debug('Entering nusoap_parser(), length='.$this->strsize($xml).', encoding='.$encoding);
    // Create an XML parser - why not xml_parser_create_ns?
    $this->parser = xml_parser_create($this->xml_encoding);
    // Set the options for parsing the XML data.
    @@ -7442,7 +7458,7 @@
    // serialize envelope
    $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
    $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
    - $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
    + $this->debug('SOAP message length=' .$this->strsize($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
    // send
    $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
    if($errstr = $this->getError()){
    @@ -7594,7 +7610,7 @@
    if($this->http_encoding != ''){
    $http->setEncoding($this->http_encoding);
    }
    - $this->debug('sending message, length='.strlen($msg));
    + $this->debug('sending message, length='.$this->strsize($msg));
    if(preg_match('/^http:/',$this->endpoint)){
    //if(strpos($this->endpoint,'http:')){
    $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
    @@ -7628,7 +7644,7 @@
    } elseif($this->getError()){
    return false;
    } else {
    - $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers);
    + $this->debug('got response, length='.$this->strsize($this->responseData).' type='.$http->incoming_headers);
    return $this->parseResponse($http->incoming_headers, $this->responseData);
    }
    break;
    @@ -7648,7 +7664,7 @@
    * @access   private
    */
         function parseResponse($headers, $data) {
    - $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
    + $this->debug('Entering parseResponse() for data of length ' .$this->strsize($data) . ' headers:');
    $this->appendDebug($this->varDump($headers));
         if (!isset($headers)) {
    $this->setError('Response not of type text/xml (no content-type header)');