\n";
- }
- else
- {
- return "\n\n";
- }
- }
-
- /**
- * @access private
- */
- function xml_footer()
- {
- return '';
- }
-
- /**
- * @access private
- */
- function kindOf()
- {
- return 'msg';
- }
-
- /**
- * @access private
- */
- function createPayload($charset_encoding='')
- {
- if ($charset_encoding != '')
- $this->content_type = 'text/xml; charset=' . $charset_encoding;
- else
- $this->content_type = 'text/xml';
- $this->payload=$this->xml_header($charset_encoding);
- $this->payload.='' . $this->methodname . "\n";
- $this->payload.="\n";
- for($i=0; $iparams); $i++)
- {
- $p=$this->params[$i];
- $this->payload.="\n" . $p->serialize($charset_encoding) .
- "\n";
- }
- $this->payload.="\n";
- $this->payload.=$this->xml_footer();
- }
-
- /**
- * Gets/sets the xmlrpc method to be invoked
- * @param string $meth the method to be set (leave empty not to set it)
- * @return string the method that will be invoked
- * @access public
- */
- function method($meth='')
- {
- if($meth!='')
- {
- $this->methodname=$meth;
- }
- return $this->methodname;
- }
-
- /**
- * Returns xml representation of the message. XML prologue included
- * @return string the xml representation of the message, xml prologue included
- * @access public
- */
- function serialize($charset_encoding='')
- {
- $this->createPayload($charset_encoding);
- return $this->payload;
- }
-
- /**
- * Add a parameter to the list of parameters to be used upon method invocation
- * @param xmlrpcval $par
- * @return boolean false on failure
- * @access public
- */
- function addParam($par)
- {
- // add check: do not add to self params which are not xmlrpcvals
- if(is_object($par) && is_a($par, 'xmlrpcval'))
- {
- $this->params[]=$par;
- return true;
- }
- else
- {
- return false;
- }
- }
-
- /**
- * Returns the nth parameter in the message. The index zero-based.
- * @param integer $i the index of the parameter to fetch (zero based)
- * @return xmlrpcval the i-th parameter
- * @access public
- */
- function getParam($i) { return $this->params[$i]; }
-
- /**
- * Returns the number of parameters in the messge.
- * @return integer the number of parameters currently set
- * @access public
- */
- function getNumParams() { return count($this->params); }
-
- /**
- * Given an open file handle, read all data available and parse it as axmlrpc response.
- * NB: the file handle is not closed by this function.
- * NNB: might have trouble in rare cases to work on network streams, as we
- * check for a read of 0 bytes instead of feof($fp).
- * But since checking for feof(null) returns false, we would risk an
- * infinite loop in that case, because we cannot trust the caller
- * to give us a valid pointer to an open file...
- * @access public
- * @return xmlrpcresp
- * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
- */
- function &parseResponseFile($fp)
- {
- $ipd='';
- while($data=fread($fp, 32768))
- {
- $ipd.=$data;
- }
- //fclose($fp);
- $r =& $this->parseResponse($ipd);
- return $r;
- }
-
- /**
- * Parses HTTP headers and separates them from data.
- * @access private
- */
- function &parseResponseHeaders(&$data, $headers_processed=false)
- {
- // Support "web-proxy-tunelling" connections for https through proxies
- if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
- {
- // Look for CR/LF or simple LF as line separator,
- // (even though it is not valid http)
- $pos = strpos($data,"\r\n\r\n");
- if($pos || is_int($pos))
- {
- $bd = $pos+4;
- }
- else
- {
- $pos = strpos($data,"\n\n");
- if($pos || is_int($pos))
- {
- $bd = $pos+2;
- }
- else
- {
- // No separation between response headers and body: fault?
- $bd = 0;
- }
- }
- if ($bd)
- {
- // this filters out all http headers from proxy.
- // maybe we could take them into account, too?
- $data = substr($data, $bd);
- }
- else
- {
- error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTPS via proxy error, tunnel connection possibly failed');
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
- return $r;
- }
- }
-
- // Strip HTTP 1.1 100 Continue header if present
- while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
- {
- $pos = strpos($data, 'HTTP', 12);
- // server sent a Continue header without any (valid) content following...
- // give the client a chance to know it
- if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
- {
- break;
- }
- $data = substr($data, $pos);
- }
- if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
- {
- $errstr= substr($data, 0, strpos($data, "\n")-1);
- error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr);
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
- return $r;
- }
-
- $GLOBALS['_xh']['headers'] = array();
- $GLOBALS['_xh']['cookies'] = array();
-
- // be tolerant to usage of \n instead of \r\n to separate headers and data
- // (even though it is not valid http)
- $pos = strpos($data,"\r\n\r\n");
- if($pos || is_int($pos))
- {
- $bd = $pos+4;
- }
- else
- {
- $pos = strpos($data,"\n\n");
- if($pos || is_int($pos))
- {
- $bd = $pos+2;
- }
- else
- {
- // No separation between response headers and body: fault?
- // we could take some action here instead of going on...
- $bd = 0;
- }
- }
- // be tolerant to line endings, and extra empty lines
- $ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
- while(list(,$line) = @each($ar))
- {
- // take care of multi-line headers and cookies
- $arr = explode(':',$line,2);
- if(count($arr) > 1)
- {
- $header_name = strtolower(trim($arr[0]));
- /// @todo some other headers (the ones that allow a CSV list of values)
- /// do allow many values to be passed using multiple header lines.
- /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
- /// instead of replacing it for those...
- if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
- {
- if ($header_name == 'set-cookie2')
- {
- // version 2 cookies:
- // there could be many cookies on one line, comma separated
- $cookies = explode(',', $arr[1]);
- }
- else
- {
- $cookies = array($arr[1]);
- }
- foreach ($cookies as $cookie)
- {
- // glue together all received cookies, using a comma to separate them
- // (same as php does with getallheaders())
- if (isset($GLOBALS['_xh']['headers'][$header_name]))
- $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
- else
- $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
- // parse cookie attributes, in case user wants to correctly honour them
- // feature creep: only allow rfc-compliant cookie attributes?
- // @todo support for server sending multiple time cookie with same name, but using different PATHs
- $cookie = explode(';', $cookie);
- foreach ($cookie as $pos => $val)
- {
- $val = explode('=', $val, 2);
- $tag = trim($val[0]);
- $val = trim(@$val[1]);
- /// @todo with version 1 cookies, we should strip leading and trailing " chars
- if ($pos == 0)
- {
- $cookiename = $tag;
- $GLOBALS['_xh']['cookies'][$tag] = array();
- $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
- }
- else
- {
- if ($tag != 'value')
- {
- $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
- }
- }
- }
- }
- }
- else
- {
- $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
- }
- }
- elseif(isset($header_name))
- {
- /// @todo version1 cookies might span multiple lines, thus breaking the parsing above
- $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
- }
- }
-
- $data = substr($data, $bd);
-
- if($this->debug && count($GLOBALS['_xh']['headers']))
- {
- print '';
- foreach($GLOBALS['_xh']['headers'] as $header => $value)
- {
- print htmlentities("HEADER: $header: $value\n");
- }
- foreach($GLOBALS['_xh']['cookies'] as $header => $value)
- {
- print htmlentities("COOKIE: $header={$value['value']}\n");
- }
- print "
\n";
- }
-
- // if CURL was used for the call, http headers have been processed,
- // and dechunking + reinflating have been carried out
- if(!$headers_processed)
- {
- // Decode chunked encoding sent by http 1.1 servers
- if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
- {
- if(!$data = decode_chunked($data))
- {
- error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server');
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
- return $r;
- }
- }
-
- // Decode gzip-compressed stuff
- // code shamelessly inspired from nusoap library by Dietrich Ayala
- if(isset($GLOBALS['_xh']['headers']['content-encoding']))
- {
- $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
- if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
- {
- // if decoding works, use it. else assume data wasn't gzencoded
- if(function_exists('gzinflate'))
- {
- if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
- {
- $data = $degzdata;
- if($this->debug)
- print "---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---
";
- }
- elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
- {
- $data = $degzdata;
- if($this->debug)
- print "---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---
";
- }
- else
- {
- error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server');
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
- return $r;
- }
- }
- else
- {
- error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
- return $r;
- }
- }
- }
- } // end of 'if needed, de-chunk, re-inflate response'
-
- // real stupid hack to avoid PHP 4 complaining about returning NULL by ref
- $r = null;
- $r =& $r;
- return $r;
- }
-
- /**
- * Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
- * @param string $data the xmlrpc response, eventually including http headers
- * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
- * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
- * @return xmlrpcresp
- * @access public
- */
- function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
- {
- if($this->debug)
- {
- //by maHo, replaced htmlspecialchars with htmlentities
- print "---GOT---\n" . htmlentities($data) . "\n---END---\n
";
- }
-
- if($data == '')
- {
- error_log('XML-RPC: xmlrpcmsg::parseResponse: no response received from server.');
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
- return $r;
- }
-
- $GLOBALS['_xh']=array();
-
- $raw_data = $data;
- // parse the HTTP headers of the response, if present, and separate them from data
- if(substr($data, 0, 4) == 'HTTP')
- {
- $r =& $this->parseResponseHeaders($data, $headers_processed);
- if ($r)
- {
- // failed processing of HTTP response headers
- // save into response obj the full payload received, for debugging
- $r->raw_data = $data;
- return $r;
- }
- }
- else
- {
- $GLOBALS['_xh']['headers'] = array();
- $GLOBALS['_xh']['cookies'] = array();
- }
-
- if($this->debug)
- {
- $start = strpos($data, '', $start);
- $comments = substr($data, $start, $end-$start);
- print "---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n
";
- }
- }
-
- // be tolerant of extra whitespace in response body
- $data = trim($data);
-
- /// @todo return an error msg if $data=='' ?
-
- // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
- // idea from Luca Mariano originally in PEARified version of the lib
- $bd = false;
- // Poor man's version of strrpos for php 4...
- $pos = strpos($data, '');
- while($pos || is_int($pos))
- {
- $bd = $pos+17;
- $pos = strpos($data, '', $bd);
- }
- if($bd)
- {
- $data = substr($data, 0, $bd);
- }
-
- // if user wants back raw xml, give it to him
- if ($return_type == 'xml')
- {
- $r = new xmlrpcresp($data, 0, '', 'xml');
- $r->hdrs = $GLOBALS['_xh']['headers'];
- $r->_cookies = $GLOBALS['_xh']['cookies'];
- $r->raw_data = $raw_data;
- return $r;
- }
-
- // try to 'guestimate' the character encoding of the received response
- $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
-
- $GLOBALS['_xh']['ac']='';
- //$GLOBALS['_xh']['qt']=''; //unused...
- $GLOBALS['_xh']['stack'] = array();
- $GLOBALS['_xh']['valuestack'] = array();
- $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
- $GLOBALS['_xh']['isf_reason']='';
- $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
-
- // if response charset encoding is not known / supported, try to use
- // the default encoding and parse the xml anyway, but log a warning...
- if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
- // the following code might be better for mb_string enabled installs, but
- // makes the lib about 200% slower...
- //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
- {
- error_log('XML-RPC: xmlrpcmsg::parseResponse: invalid charset encoding of received response: '.$resp_encoding);
- $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
- }
- $parser = xml_parser_create($resp_encoding);
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
- // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
- // the xml parser to give us back data in the expected charset.
- // What if internal encoding is not in one of the 3 allowed?
- // we use the broadest one, ie. utf8
- // This allows to send data which is native in various charset,
- // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
- if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
- {
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
- }
- else
- {
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
- }
-
- if ($return_type == 'phpvals')
- {
- xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
- }
- else
- {
- xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
- }
-
- xml_set_character_data_handler($parser, 'xmlrpc_cd');
- xml_set_default_handler($parser, 'xmlrpc_dh');
-
- // first error check: xml not well formed
- if(!xml_parse($parser, $data, count($data)))
- {
- // thanks to Peter Kocks
- if((xml_get_current_line_number($parser)) == 1)
- {
- $errstr = 'XML error at line 1, check URL';
- }
- else
- {
- $errstr = sprintf('XML error: %s at line %d, column %d',
- xml_error_string(xml_get_error_code($parser)),
- xml_get_current_line_number($parser), xml_get_current_column_number($parser));
- }
- error_log($errstr);
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
- xml_parser_free($parser);
- if($this->debug)
- {
- print $errstr;
- }
- $r->hdrs = $GLOBALS['_xh']['headers'];
- $r->_cookies = $GLOBALS['_xh']['cookies'];
- $r->raw_data = $raw_data;
- return $r;
- }
- xml_parser_free($parser);
- // second error check: xml well formed but not xml-rpc compliant
- if ($GLOBALS['_xh']['isf'] > 1)
- {
- if ($this->debug)
- {
- /// @todo echo something for user?
- }
-
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
- $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
- }
- // third error check: parsing of the response has somehow gone boink.
- // NB: shall we omit this check, since we trust the parsing code?
- elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
- {
- // something odd has happened
- // and it's time to generate a client side error
- // indicating something odd went on
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
- $GLOBALS['xmlrpcstr']['invalid_return']);
- }
- else
- {
- if ($this->debug)
- {
- print "---PARSED---\n";
- // somehow htmlentities chokes on var_export, and some full html string...
- //print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
- print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
- print "\n---END---
";
- }
-
- // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
- $v =& $GLOBALS['_xh']['value'];
-
- if($GLOBALS['_xh']['isf'])
- {
- /// @todo we should test here if server sent an int and a string,
- /// and/or coerce them into such...
- if ($return_type == 'xmlrpcvals')
- {
- $errno_v = $v->structmem('faultCode');
- $errstr_v = $v->structmem('faultString');
- $errno = $errno_v->scalarval();
- $errstr = $errstr_v->scalarval();
- }
- else
- {
- $errno = $v['faultCode'];
- $errstr = $v['faultString'];
- }
-
- if($errno == 0)
- {
- // FAULT returned, errno needs to reflect that
- $errno = -1;
- }
-
- $r = new xmlrpcresp(0, $errno, $errstr);
- }
- else
- {
- $r=new xmlrpcresp($v, 0, '', $return_type);
- }
- }
-
- $r->hdrs = $GLOBALS['_xh']['headers'];
- $r->_cookies = $GLOBALS['_xh']['cookies'];
- $r->raw_data = $raw_data;
- return $r;
- }
- }
-
- class xmlrpcval
- {
- var $me=array();
- var $mytype=0;
- var $_php_class=null;
-
- /**
- * @param mixed $val
- * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
- */
- function xmlrpcval($val=-1, $type='')
- {
- /// @todo: optimization creep - do not call addXX, do it all inline.
- /// downside: booleans will not be coerced anymore
- if($val!==-1 || $type!='')
- {
- // optimization creep: inlined all work done by constructor
- switch($type)
- {
- case '':
- $this->mytype=1;
- $this->me['string']=$val;
- break;
- case 'i4':
- case 'int':
- case 'double':
- case 'string':
- case 'boolean':
- case 'dateTime.iso8601':
- case 'base64':
- case 'null':
- $this->mytype=1;
- $this->me[$type]=$val;
- break;
- case 'array':
- $this->mytype=2;
- $this->me['array']=$val;
- break;
- case 'struct':
- $this->mytype=3;
- $this->me['struct']=$val;
- break;
- default:
- error_log("XML-RPC: xmlrpcval::xmlrpcval: not a known type ($type)");
- }
- /*if($type=='')
- {
- $type='string';
- }
- if($GLOBALS['xmlrpcTypes'][$type]==1)
- {
- $this->addScalar($val,$type);
- }
- elseif($GLOBALS['xmlrpcTypes'][$type]==2)
- {
- $this->addArray($val);
- }
- elseif($GLOBALS['xmlrpcTypes'][$type]==3)
- {
- $this->addStruct($val);
- }*/
- }
- }
-
- /**
- * Add a single php value to an (unitialized) xmlrpcval
- * @param mixed $val
- * @param string $type
- * @return int 1 or 0 on failure
- */
- function addScalar($val, $type='string')
- {
- $typeof=@$GLOBALS['xmlrpcTypes'][$type];
- if($typeof!=1)
- {
- error_log("XML-RPC: xmlrpcval::addScalar: not a scalar type ($type)");
- return 0;
- }
-
- // coerce booleans into correct values
- // NB: we should either do it for datetimes, integers and doubles, too,
- // or just plain remove this check, implemented on booleans only...
- if($type==$GLOBALS['xmlrpcBoolean'])
- {
- if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
- {
- $val=true;
- }
- else
- {
- $val=false;
- }
- }
-
- switch($this->mytype)
- {
- case 1:
- error_log('XML-RPC: xmlrpcval::addScalar: scalar xmlrpcval can have only one value');
- return 0;
- case 3:
- error_log('XML-RPC: xmlrpcval::addScalar: cannot add anonymous scalar to struct xmlrpcval');
- return 0;
- case 2:
- // we're adding a scalar value to an array here
- //$ar=$this->me['array'];
- //$ar[]=new xmlrpcval($val, $type);
- //$this->me['array']=$ar;
- // Faster (?) avoid all the costly array-copy-by-val done here...
- $this->me['array'][]=new xmlrpcval($val, $type);
- return 1;
- default:
- // a scalar, so set the value and remember we're scalar
- $this->me[$type]=$val;
- $this->mytype=$typeof;
- return 1;
- }
- }
-
- /**
- * Add an array of xmlrpcval objects to an xmlrpcval
- * @param array $vals
- * @return int 1 or 0 on failure
- * @access public
- *
- * @todo add some checking for $vals to be an array of xmlrpcvals?
- */
- function addArray($vals)
- {
- if($this->mytype==0)
- {
- $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
- $this->me['array']=$vals;
- return 1;
- }
- elseif($this->mytype==2)
- {
- // we're adding to an array here
- $this->me['array'] = array_merge($this->me['array'], $vals);
- return 1;
- }
- else
- {
- error_log('XML-RPC: xmlrpcval::addArray: already initialized as a [' . $this->kindOf() . ']');
- return 0;
- }
- }
-
- /**
- * Add an array of named xmlrpcval objects to an xmlrpcval
- * @param array $vals
- * @return int 1 or 0 on failure
- * @access public
- *
- * @todo add some checking for $vals to be an array?
- */
- function addStruct($vals)
- {
- if($this->mytype==0)
- {
- $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
- $this->me['struct']=$vals;
- return 1;
- }
- elseif($this->mytype==3)
- {
- // we're adding to a struct here
- $this->me['struct'] = array_merge($this->me['struct'], $vals);
- return 1;
- }
- else
- {
- error_log('XML-RPC: xmlrpcval::addStruct: already initialized as a [' . $this->kindOf() . ']');
- return 0;
- }
- }
-
- // poor man's version of print_r ???
- // DEPRECATED!
- function dump($ar)
- {
- foreach($ar as $key => $val)
- {
- echo "$key => $val
";
- if($key == 'array')
- {
- while(list($key2, $val2) = each($val))
- {
- echo "-- $key2 => $val2
";
- }
- }
- }
- }
-
- /**
- * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
- * @return string
- * @access public
- */
- function kindOf()
- {
- switch($this->mytype)
- {
- case 3:
- return 'struct';
- break;
- case 2:
- return 'array';
- break;
- case 1:
- return 'scalar';
- break;
- default:
- return 'undef';
- }
- }
-
- /**
- * @access private
- */
- function serializedata($typ, $val, $charset_encoding='')
- {
- $rs='';
- switch(@$GLOBALS['xmlrpcTypes'][$typ])
- {
- case 1:
- switch($typ)
- {
- case $GLOBALS['xmlrpcBase64']:
- $rs.="<${typ}>" . base64_encode($val) . "${typ}>";
- break;
- case $GLOBALS['xmlrpcBoolean']:
- $rs.="<${typ}>" . ($val ? '1' : '0') . "${typ}>";
- break;
- case $GLOBALS['xmlrpcString']:
- // G. Giunta 2005/2/13: do NOT use htmlentities, since
- // it will produce named html entities, which are invalid xml
- $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "${typ}>";
- break;
- case $GLOBALS['xmlrpcInt']:
- case $GLOBALS['xmlrpcI4']:
- $rs.="<${typ}>".(int)$val."${typ}>";
- break;
- case $GLOBALS['xmlrpcDouble']:
- // avoid using standard conversion of float to string because it is locale-dependent,
- // and also because the xmlrpc spec forbids exponential notation
- // sprintf('%F') would be most likely ok but it is only available since PHP 4.3.10 and PHP 5.0.3.
- // The code below tries its best at keeping max precision while avoiding exp notation,
- // but there is of course no limit in the number of decimal places to be used...
- $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."${typ}>";
- break;
- case $GLOBALS['xmlrpcDateTime']:
- if (is_string($val))
- {
- $rs.="<${typ}>${val}${typ}>";
- }
- else if(is_a($val, 'DateTime'))
- {
- $rs.="<${typ}>".$val->format('Ymd\TH:i:s')."${typ}>";
- }
- else if(is_int($val))
- {
- $rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."${typ}>";
- }
- else
- {
- // not really a good idea here: but what shall we output anyway? left for backward compat...
- $rs.="<${typ}>${val}${typ}>";
- }
- break;
- case $GLOBALS['xmlrpcNull']:
- if ($GLOBALS['xmlrpc_null_apache_encoding'])
- {
- $rs.="";
- }
- else
- {
- $rs.="";
- }
- break;
- default:
- // no standard type value should arrive here, but provide a possibility
- // for xmlrpcvals of unknown type...
- $rs.="<${typ}>${val}${typ}>";
- }
- break;
- case 3:
- // struct
- if ($this->_php_class)
- {
- $rs.='\n";
- }
- else
- {
- $rs.="\n";
- }
- foreach($val as $key2 => $val2)
- {
- $rs.=''.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."\n";
- //$rs.=$this->serializeval($val2);
- $rs.=$val2->serialize($charset_encoding);
- $rs.="\n";
- }
- $rs.='';
- break;
- case 2:
- // array
- $rs.="\n\n";
- for($i=0; $iserializeval($val[$i]);
- $rs.=$val[$i]->serialize($charset_encoding);
- }
- $rs.="\n";
- break;
- default:
- break;
- }
- return $rs;
- }
-
- /**
- * Returns xml representation of the value. XML prologue not included
- * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
- * @return string
- * @access public
- */
- function serialize($charset_encoding='')
- {
- // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
- //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
- //{
- reset($this->me);
- list($typ, $val) = each($this->me);
- return '' . $this->serializedata($typ, $val, $charset_encoding) . "\n";
- //}
- }
-
- // DEPRECATED
- function serializeval($o)
- {
- // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
- //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
- //{
- $ar=$o->me;
- reset($ar);
- list($typ, $val) = each($ar);
- return '' . $this->serializedata($typ, $val) . "\n";
- //}
- }
-
- /**
- * Checks wheter a struct member with a given name is present.
- * Works only on xmlrpcvals of type struct.
- * @param string $m the name of the struct member to be looked up
- * @return boolean
- * @access public
- */
- function structmemexists($m)
- {
- return array_key_exists($m, $this->me['struct']);
- }
-
- /**
- * Returns the value of a given struct member (an xmlrpcval object in itself).
- * Will raise a php warning if struct member of given name does not exist
- * @param string $m the name of the struct member to be looked up
- * @return xmlrpcval
- * @access public
- */
- function structmem($m)
- {
- return $this->me['struct'][$m];
- }
-
- /**
- * Reset internal pointer for xmlrpcvals of type struct.
- * @access public
- */
- function structreset()
- {
- reset($this->me['struct']);
- }
-
- /**
- * Return next member element for xmlrpcvals of type struct.
- * @return xmlrpcval
- * @access public
- */
- function structeach()
- {
- return each($this->me['struct']);
- }
-
- // DEPRECATED! this code looks like it is very fragile and has not been fixed
- // for a long long time. Shall we remove it for 2.0?
- function getval()
- {
- // UNSTABLE
- reset($this->me);
- list($a,$b)=each($this->me);
- // contributed by I Sofer, 2001-03-24
- // add support for nested arrays to scalarval
- // i've created a new method here, so as to
- // preserve back compatibility
-
- if(is_array($b))
- {
- @reset($b);
- while(list($id,$cont) = @each($b))
- {
- $b[$id] = $cont->scalarval();
- }
- }
-
- // add support for structures directly encoding php objects
- if(is_object($b))
- {
- $t = get_object_vars($b);
- @reset($t);
- while(list($id,$cont) = @each($t))
- {
- $t[$id] = $cont->scalarval();
- }
- @reset($t);
- while(list($id,$cont) = @each($t))
- {
- @$b->$id = $cont;
- }
- }
- // end contrib
- return $b;
- }
-
- /**
- * Returns the value of a scalar xmlrpcval
- * @return mixed
- * @access public
- */
- function scalarval()
- {
- reset($this->me);
- list(,$b)=each($this->me);
- return $b;
- }
-
- /**
- * Returns the type of the xmlrpcval.
- * For integers, 'int' is always returned in place of 'i4'
- * @return string
- * @access public
- */
- function scalartyp()
- {
- reset($this->me);
- list($a,)=each($this->me);
- if($a==$GLOBALS['xmlrpcI4'])
- {
- $a=$GLOBALS['xmlrpcInt'];
- }
- return $a;
- }
-
- /**
- * Returns the m-th member of an xmlrpcval of struct type
- * @param integer $m the index of the value to be retrieved (zero based)
- * @return xmlrpcval
- * @access public
- */
- function arraymem($m)
- {
- return $this->me['array'][$m];
- }
-
- /**
- * Returns the number of members in an xmlrpcval of array type
- * @return integer
- * @access public
- */
- function arraysize()
- {
- return count($this->me['array']);
- }
-
- /**
- * Returns the number of members in an xmlrpcval of struct type
- * @return integer
- * @access public
- */
- function structsize()
- {
- return count($this->me['struct']);
- }
- }
-
-
- // date helpers
-
- /**
- * Given a timestamp, return the corresponding ISO8601 encoded string.
- *
- * Really, timezones ought to be supported
- * but the XML-RPC spec says:
- *
- * "Don't assume a timezone. It should be specified by the server in its
- * documentation what assumptions it makes about timezones."
- *
- * These routines always assume localtime unless
- * $utc is set to 1, in which case UTC is assumed
- * and an adjustment for locale is made when encoding
- *
- * @param int $timet (timestamp)
- * @param int $utc (0 or 1)
- * @return string
- */
- function iso8601_encode($timet, $utc=0)
- {
- if(!$utc)
- {
- $t=strftime("%Y%m%dT%H:%M:%S", $timet);
- }
- else
- {
- if(function_exists('gmstrftime'))
- {
- // gmstrftime doesn't exist in some versions
- // of PHP
- $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
- }
- else
- {
- $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
- }
- }
- return $t;
- }
-
- /**
- * Given an ISO8601 date string, return a timet in the localtime, or UTC
- * @param string $idate
- * @param int $utc either 0 or 1
- * @return int (datetime)
- */
- function iso8601_decode($idate, $utc=0)
- {
- $t=0;
- if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
- {
- if($utc)
- {
- $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
- }
- else
- {
- $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
- }
- }
- return $t;
- }
-
- /**
- * Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
- *
- * Works with xmlrpc message objects as input, too.
- *
- * Given proper options parameter, can rebuild generic php object instances
- * (provided those have been encoded to xmlrpc format using a corresponding
- * option in php_xmlrpc_encode())
- * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
- * This means that the remote communication end can decide which php code will
- * get executed on your server, leaving the door possibly open to 'php-injection'
- * style of attacks (provided you have some classes defined on your server that
- * might wreak havoc if instances are built outside an appropriate context).
- * Make sure you trust the remote server/client before eanbling this!
- *
- * @author Dan Libby (dan@libby.com)
- *
- * @param xmlrpcval $xmlrpc_val
- * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects (standard is
- * @return mixed
- */
- function php_xmlrpc_decode($xmlrpc_val, $options=array())
- {
- switch($xmlrpc_val->kindOf())
- {
- case 'scalar':
- if (in_array('extension_api', $options))
- {
- reset($xmlrpc_val->me);
- list($typ,$val) = each($xmlrpc_val->me);
- switch ($typ)
- {
- case 'dateTime.iso8601':
- $xmlrpc_val->scalar = $val;
- $xmlrpc_val->xmlrpc_type = 'datetime';
- $xmlrpc_val->timestamp = iso8601_decode($val);
- return $xmlrpc_val;
- case 'base64':
- $xmlrpc_val->scalar = $val;
- $xmlrpc_val->type = $typ;
- return $xmlrpc_val;
- default:
- return $xmlrpc_val->scalarval();
- }
- }
- if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
- {
- // we return a Datetime object instead of a string
- // since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
- // we cater to all 3 cases here
- $out = $xmlrpc_val->scalarval();
- if (is_string($out))
- {
- $out= strtotime($out);
- }
- if (is_int($out))
- {
- $result = new Datetime();
- $result->setTimestamp($out);
- return $result;
- }
- elseif (is_a($out, 'Datetime'))
- {
- return $out;
- }
- }
- return $xmlrpc_val->scalarval();
- case 'array':
- $size = $xmlrpc_val->arraysize();
- $arr = array();
- for($i = 0; $i < $size; $i++)
- {
- $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
- }
- return $arr;
- case 'struct':
- $xmlrpc_val->structreset();
- // If user said so, try to rebuild php objects for specific struct vals.
- /// @todo should we raise a warning for class not found?
- // shall we check for proper subclass of xmlrpcval instead of
- // presence of _php_class to detect what we can do?
- if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
- && class_exists($xmlrpc_val->_php_class))
- {
- $obj = @new $xmlrpc_val->_php_class;
- while(list($key,$value)=$xmlrpc_val->structeach())
- {
- $obj->$key = php_xmlrpc_decode($value, $options);
- }
- return $obj;
- }
- else
- {
- $arr = array();
- while(list($key,$value)=$xmlrpc_val->structeach())
- {
- $arr[$key] = php_xmlrpc_decode($value, $options);
- }
- return $arr;
- }
- case 'msg':
- $paramcount = $xmlrpc_val->getNumParams();
- $arr = array();
- for($i = 0; $i < $paramcount; $i++)
- {
- $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
- }
- return $arr;
- }
- }
-
- // This constant left here only for historical reasons...
- // it was used to decide if we have to define xmlrpc_encode on our own, but
- // we do not do it anymore
- if(function_exists('xmlrpc_decode'))
- {
- define('XMLRPC_EPI_ENABLED','1');
- }
- else
- {
- define('XMLRPC_EPI_ENABLED','0');
- }
-
- /**
- * Takes native php types and encodes them into xmlrpc PHP object format.
- * It will not re-encode xmlrpcval objects.
- *
- * Feature creep -- could support more types via optional type argument
- * (string => datetime support has been added, ??? => base64 not yet)
- *
- * If given a proper options parameter, php object instances will be encoded
- * into 'special' xmlrpc values, that can later be decoded into php objects
- * by calling php_xmlrpc_decode() with a corresponding option
- *
- * @author Dan Libby (dan@libby.com)
- *
- * @param mixed $php_val the value to be converted into an xmlrpcval object
- * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
- * @return xmlrpcval
- */
- function &php_xmlrpc_encode($php_val, $options=array())
- {
- $type = gettype($php_val);
- switch($type)
- {
- case 'string':
- if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
- else
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
- break;
- case 'integer':
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
- break;
- case 'double':
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
- break;
- //
- // Add support for encoding/decoding of booleans, since they are supported in PHP
- case 'boolean':
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
- break;
- //
- case 'array':
- // PHP arrays can be encoded to either xmlrpc structs or arrays,
- // depending on wheter they are hashes or plain 0..n integer indexed
- // A shorter one-liner would be
- // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
- // but execution time skyrockets!
- $j = 0;
- $arr = array();
- $ko = false;
- foreach($php_val as $key => $val)
- {
- $arr[$key] =& php_xmlrpc_encode($val, $options);
- if(!$ko && $key !== $j)
- {
- $ko = true;
- }
- $j++;
- }
- if($ko)
- {
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
- }
- else
- {
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
- }
- break;
- case 'object':
- if(is_a($php_val, 'xmlrpcval'))
- {
- $xmlrpc_val = $php_val;
- }
- else if(is_a($php_val, 'DateTime'))
- {
- $xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
- }
- else
- {
- $arr = array();
- while(list($k,$v) = each($php_val))
- {
- $arr[$k] = php_xmlrpc_encode($v, $options);
- }
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
- if (in_array('encode_php_objs', $options))
- {
- // let's save original class name into xmlrpcval:
- // might be useful later on...
- $xmlrpc_val->_php_class = get_class($php_val);
- }
- }
- break;
- case 'NULL':
- if (in_array('extension_api', $options))
- {
- $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
- }
- else if (in_array('null_extension', $options))
- {
- $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
- }
- else
- {
- $xmlrpc_val = new xmlrpcval();
- }
- break;
- case 'resource':
- if (in_array('extension_api', $options))
- {
- $xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
- }
- else
- {
- $xmlrpc_val = new xmlrpcval();
- }
- // catch "user function", "unknown type"
- default:
- // giancarlo pinerolo
- // it has to return
- // an empty object in case, not a boolean.
- $xmlrpc_val = new xmlrpcval();
- break;
- }
- return $xmlrpc_val;
- }
-
- /**
- * Convert the xml representation of a method response, method request or single
- * xmlrpc value into the appropriate object (a.k.a. deserialize)
- * @param string $xml_val
- * @param array $options
- * @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
- */
- function php_xmlrpc_decode_xml($xml_val, $options=array())
- {
- $GLOBALS['_xh'] = array();
- $GLOBALS['_xh']['ac'] = '';
- $GLOBALS['_xh']['stack'] = array();
- $GLOBALS['_xh']['valuestack'] = array();
- $GLOBALS['_xh']['params'] = array();
- $GLOBALS['_xh']['pt'] = array();
- $GLOBALS['_xh']['isf'] = 0;
- $GLOBALS['_xh']['isf_reason'] = '';
- $GLOBALS['_xh']['method'] = false;
- $GLOBALS['_xh']['rt'] = '';
- /// @todo 'guestimate' encoding
- $parser = xml_parser_create();
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
- // What if internal encoding is not in one of the 3 allowed?
- // we use the broadest one, ie. utf8!
- if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
- {
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
- }
- else
- {
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
- }
- xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
- xml_set_character_data_handler($parser, 'xmlrpc_cd');
- xml_set_default_handler($parser, 'xmlrpc_dh');
- if(!xml_parse($parser, $xml_val, 1))
- {
- $errstr = sprintf('XML error: %s at line %d, column %d',
- xml_error_string(xml_get_error_code($parser)),
- xml_get_current_line_number($parser), xml_get_current_column_number($parser));
- error_log($errstr);
- xml_parser_free($parser);
- return false;
- }
- xml_parser_free($parser);
- if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
- {
- error_log($GLOBALS['_xh']['isf_reason']);
- return false;
- }
- switch ($GLOBALS['_xh']['rt'])
- {
- case 'methodresponse':
- $v =& $GLOBALS['_xh']['value'];
- if ($GLOBALS['_xh']['isf'] == 1)
- {
- $vc = $v->structmem('faultCode');
- $vs = $v->structmem('faultString');
- $r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
- }
- else
- {
- $r = new xmlrpcresp($v);
- }
- return $r;
- case 'methodcall':
- $m = new xmlrpcmsg($GLOBALS['_xh']['method']);
- for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
- {
- $m->addParam($GLOBALS['_xh']['params'][$i]);
- }
- return $m;
- case 'value':
- return $GLOBALS['_xh']['value'];
- default:
- return false;
- }
- }
-
- /**
- * decode a string that is encoded w/ "chunked" transfer encoding
- * as defined in rfc2068 par. 19.4.6
- * code shamelessly stolen from nusoap library by Dietrich Ayala
- *
- * @param string $buffer the string to be decoded
- * @return string
- */
- function decode_chunked($buffer)
- {
- // length := 0
- $length = 0;
- $new = '';
-
- // read chunk-size, chunk-extension (if any) and crlf
- // get the position of the linebreak
- $chunkend = strpos($buffer,"\r\n") + 2;
- $temp = substr($buffer,0,$chunkend);
- $chunk_size = hexdec( trim($temp) );
- $chunkstart = $chunkend;
- while($chunk_size > 0)
- {
- $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
-
- // just in case we got a broken connection
- if($chunkend == false)
- {
- $chunk = substr($buffer,$chunkstart);
- // append chunk-data to entity-body
- $new .= $chunk;
- $length += strlen($chunk);
- break;
- }
-
- // read chunk-data and crlf
- $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
- // append chunk-data to entity-body
- $new .= $chunk;
- // length := length + chunk-size
- $length += strlen($chunk);
- // read chunk-size and crlf
- $chunkstart = $chunkend + 2;
-
- $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
- if($chunkend == false)
- {
- break; //just in case we got a broken connection
- }
- $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
- $chunk_size = hexdec( trim($temp) );
- $chunkstart = $chunkend;
- }
- return $new;
- }
-
- /**
- * xml charset encoding guessing helper function.
- * Tries to determine the charset encoding of an XML chunk received over HTTP.
- * NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
- * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
- * which will be most probably using UTF-8 anyway...
- *
- * @param string $httpheaders the http Content-type header
- * @param string $xmlchunk xml content buffer
- * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
- *
- * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
- */
- function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
- {
- // discussion: see http://www.yale.edu/pclt/encoding/
- // 1 - test if encoding is specified in HTTP HEADERS
-
- //Details:
- // LWS: (\13\10)?( |\t)+
- // token: (any char but excluded stuff)+
- // quoted string: " (any char but double quotes and cointrol chars)* "
- // header: Content-type = ...; charset=value(; ...)*
- // where value is of type token, no LWS allowed between 'charset' and value
- // Note: we do not check for invalid chars in VALUE:
- // this had better be done using pure ereg as below
- // Note 2: we might be removing whitespace/tabs that ought to be left in if
- // the received charset is a quoted string. But nobody uses such charset names...
-
- /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
- $matches = array();
- if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
- {
- return strtoupper(trim($matches[1], " \t\""));
- }
-
- // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
- // (source: http://www.w3.org/TR/2000/REC-xml-20001006)
- // NOTE: actually, according to the spec, even if we find the BOM and determine
- // an encoding, we should check if there is an encoding specified
- // in the xml declaration, and verify if they match.
- /// @todo implement check as described above?
- /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
- if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
- {
- return 'UCS-4';
- }
- elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
- {
- return 'UTF-16';
- }
- elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
- {
- return 'UTF-8';
- }
-
- // 3 - test if encoding is specified in the xml declaration
- // Details:
- // SPACE: (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
- // EQ: SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
- if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
- '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
- $xmlchunk, $matches))
- {
- return strtoupper(substr($matches[2], 1, -1));
- }
-
- // 4 - if mbstring is available, let it do the guesswork
- // NB: we favour finding an encoding that is compatible with what we can process
- if(extension_loaded('mbstring'))
- {
- if($encoding_prefs)
- {
- $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
- }
- else
- {
- $enc = mb_detect_encoding($xmlchunk);
- }
- // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
- // IANA also likes better US-ASCII, so go with it
- if($enc == 'ASCII')
- {
- $enc = 'US-'.$enc;
- }
- return $enc;
- }
- else
- {
- // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
- // Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
- // this should be the standard. And we should be getting text/xml as request and response.
- // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
- return $GLOBALS['xmlrpc_defencoding'];
- }
- }
-
- /**
- * Checks if a given charset encoding is present in a list of encodings or
- * if it is a valid subset of any encoding in the list
- * @param string $encoding charset to be tested
- * @param mixed $validlist comma separated list of valid charsets (or array of charsets)
- */
- function is_valid_charset($encoding, $validlist)
- {
- $charset_supersets = array(
- 'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
- 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
- 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
- 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
- 'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
- );
- if (is_string($validlist))
- $validlist = explode(',', $validlist);
- if (@in_array(strtoupper($encoding), $validlist))
- return true;
- else
- {
- if (array_key_exists($encoding, $charset_supersets))
- foreach ($validlist as $allowed)
- if (in_array($allowed, $charset_supersets[$encoding]))
- return true;
- return false;
- }
- }
-
-?>
\ No newline at end of file
+
+
+// Copyright (c) 1999,2000,2002 Edd Dumbill.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of the "XML-RPC for PHP" nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/******************************************************************************
+ *
+ * *** DEPRECATED ***
+ *
+ * This file is only used to insure backwards compatibility
+ * with the API of the library <= rev. 3
+ *
+ * If it is included, the library will work without any further autoloading
+ *****************************************************************************/
+
+include_once(__DIR__.'/../src/PhpXmlRpc.php');
+include_once(__DIR__.'/../src/Value.php');
+include_once(__DIR__.'/../src/Request.php');
+include_once(__DIR__.'/../src/Response.php');
+include_once(__DIR__.'/../src/Client.php');
+include_once(__DIR__.'/../src/Encoder.php');
+include_once(__DIR__.'/../src/Helper/Charset.php');
+include_once(__DIR__.'/../src/Helper/Date.php');
+include_once(__DIR__.'/../src/Helper/Http.php');
+include_once(__DIR__.'/../src/Helper/Logger.php');
+include_once(__DIR__.'/../src/Helper/XMLParser.php');
+
+
+/* Expose the global variables which used to be defined */
+PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'ISO-8859-1'; // old default
+PhpXmlRpc\PhpXmlRpc::exportGlobals();
+
+/* some stuff deprecated enough that we do not want to put it in the new lib version */
+
+/// @deprecated
+$GLOBALS['xmlEntities'] = array(
+ 'amp' => '&',
+ 'quot' => '"',
+ 'lt' => '<',
+ 'gt' => '>',
+ 'apos' => "'"
+);
+
+// formulate backslashes for escaping regexp
+// Not in use anymore since 2.0. Shall we remove it?
+/// @deprecated
+$GLOBALS['xmlrpc_backslash'] = chr(92).chr(92);
+
+/* Expose with the old names the classes which have been namespaced */
+
+class xmlrpcval extends PhpXmlRpc\Value
+{
+ /**
+ * @deprecated
+ * @param xmlrpcval $o
+ * @return string
+ */
+ public function serializeval($o)
+ {
+ // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
+ //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
+ //{
+ $ar = $o->me;
+ $val = reset($ar);
+ $typ = key($ar);
+
+ return '' . $this->serializedata($typ, $val) . "\n";
+ //}
+ }
+
+ /**
+ * @deprecated this code looks like it is very fragile and has not been fixed
+ * for a long long time. Shall we remove it for 2.0?
+ */
+ public function getval()
+ {
+ // UNSTABLE
+ $b = reset($this->me);
+ $a = key($this->me);
+ // contributed by I Sofer, 2001-03-24
+ // add support for nested arrays to scalarval
+ // i've created a new method here, so as to
+ // preserve back compatibility
+
+ if (is_array($b)) {
+ foreach($b as $id => $cont) {
+ $b[$id] = $cont->scalarval();
+ }
+ }
+
+ // add support for structures directly encoding php objects
+ if (is_object($b)) {
+ $t = get_object_vars($b);
+ foreach($t as $id => $cont) { {
+ $t[$id] = $cont->scalarval();
+ }
+ foreach($t as $id => $cont) {
+ @$b->$id = $cont;
+ }
+ }
+ // end contrib
+ return $b;
+ }
+
+ /// reset functionality added by parent class: same as it would happen if no interface was declared
+ public function count()
+ {
+ return 1;
+ }
+
+ /// reset functionality added by parent class: same as it would happen if no interface was declared
+ public function getIterator() {
+ return new ArrayIterator($this);
+ }
+}
+
+class xmlrpcmsg extends PhpXmlRpc\Request
+{
+}
+
+class xmlrpcresp extends PhpXmlRpc\Response
+{
+}
+
+class xmlrpc_client extends PhpXmlRpc\Client
+{
+}
+
+/* Expose as global functions the ones which are now class methods */
+
+/// Wrong speling, but we are adamant on backwards compatibility!
+function xmlrpc_encode_entitites($data, $srcEncoding='', $destEncoding='')
+{
+ return PhpXmlRpc\Helper\Charset::instance()->encodeEntitites($data, $srcEncoding, $destEncoding);
+}
+
+function iso8601_encode($timeT, $utc=0)
+{
+ return PhpXmlRpc\Helper\Date::iso8601Encode($timeT, $utc);
+}
+
+function iso8601_decode($iDate, $utc=0)
+{
+ return PhpXmlRpc\Helper\Date::iso8601Decode($iDate, $utc);
+}
+
+function decode_chunked($buffer)
+{
+ return PhpXmlRpc\Helper\Http::decodeChunked($buffer);
+}
+
+function php_xmlrpc_decode($xmlrpcVal, $options=array())
+{
+ $encoder = new PhpXmlRpc\Encoder();
+ return $encoder->decode($xmlrpcVal, $options);
+}
+
+function php_xmlrpc_encode($phpVal, $options=array())
+{
+ $encoder = new PhpXmlRpc\Encoder();
+ return $encoder->encode($phpVal, $options);
+}
+
+function php_xmlrpc_decode_xml($xmlVal, $options=array())
+{
+ $encoder = new PhpXmlRpc\Encoder();
+ return $encoder->decodeXml($xmlVal, $options);
+}
+
+function guess_encoding($httpHeader='', $xmlChunk='', $encodingPrefs=null)
+{
+ return PhpXmlRpc\Helper\XMLParser::guessEncoding($httpHeader, $xmlChunk, $encodingPrefs);
+}
+
+function has_encoding($xmlChunk)
+{
+ return PhpXmlRpc\Helper\XMLParser::hasEncoding($xmlChunk);
+}
+
+function is_valid_charset($encoding, $validList)
+{
+ return PhpXmlRpc\Helper\Charset::instance()->isValidCharset($encoding, $validList);
+}