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)');