This code provides API compatibility with Edd Dumbill's php xmlrpc library (http://phpxmlrpc.sourceforge.net/) but uses the xmlrpc-epi engine for the actual xml processing. It is intended to provide a smooth transition path for those who would like to be able to use either implementation. To use in your existing application, simply change: include("xmlrpc.inc"); to: include("xmlrpc_emu.inc"); Notes: - This file requires that xmlrpc-epi C extension be installed. See http://xmlrpc-epi.sourceforge.net/ - xmlrpc_decode, xmlrpc_encode are present in both the xmlrpc-epi C extension and the usefulinc implementation, and conflict. They have been enhanced and renamed to val_to_php, php_to_val. - Certain methods are not implemented and will typically return a message saying so. */ // by Edd Dumbill (C) 1999-2001 // // // Copyright (c) 1999,2000,2001 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 "PHP for XML-RPC" 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. // Requires: transport.php // change path as necessary. or set before including this file. if(!$xmlrpc_util_path) { $xmlrpc_util_path = "./utils/"; } include_once("$xmlrpc_util_path/utils.php"); // xmlrpc types. seems like these should be defines instead of globals. $xmlrpcI4="i4"; $xmlrpcInt="int"; $xmlrpcBoolean="boolean"; $xmlrpcDouble="double"; $xmlrpcString="string"; $xmlrpcDateTime="dateTime.iso8601"; $xmlrpcBase64="base64"; $xmlrpcArray="array"; $xmlrpcStruct="struct"; // map local types to xmlrpc-epi-php types. $epiTypeMap = array($xmlrpcI4 => "int", $xmlrpcInt => "int", $xmlrpcBoolean => "boolean", $xmlrpcString => "string", $xmlrpcDouble => "double", $xmlrpcDateTime => "datetime", $xmlrpcBase64 => "base64", $xmlrpcArray => "array", $xmlrpcStruct => "struct"); // map local types to php types $phpTypeMap = array($xmlrpcI4 => "integer", $xmlrpcInt => "integer", $xmlrpcBoolean => "boolean", $xmlrpcString => "string", $xmlrpcDouble => "double", $xmlrpcDateTime => "string", $xmlrpcBase64 => "string", $xmlrpcArray => "array", $xmlrpcStruct => "array"); $xmlrpcTypes=array($xmlrpcI4 => 1, $xmlrpcInt => 1, $xmlrpcBoolean => 1, $xmlrpcString => 1, $xmlrpcDouble => 1, $xmlrpcDateTime => 1, $xmlrpcBase64 => 1, $xmlrpcArray => 2, $xmlrpcStruct => 3); // some error definitions $xmlrpcerr["unknown_method"]=1; $xmlrpcstr["unknown_method"]="Unknown method"; $xmlrpcerr["invalid_return"]=2; $xmlrpcstr["invalid_return"]="Invalid return payload: enabling debugging to examine incoming payload"; $xmlrpcerr["incorrect_params"]=3; $xmlrpcstr["incorrect_params"]="Incorrect parameters passed to method"; $xmlrpcerr["introspect_unknown"]=4; $xmlrpcstr["introspect_unknown"]="Can't introspect: method unknown"; $xmlrpcerr["http_error"]=5; $xmlrpcstr["http_error"]="Didn't receive 200 OK from remote server."; $xmlrpcerr["no_data"]=6; $xmlrpcstr["no_data"]="No data received from server."; $xmlrpcerr["no_ssl"]=7; $xmlrpcstr["no_ssl"]="No SSL support compiled in."; $xmlrpcerr["curl_fail"]=8; $xmlrpcstr["curl_fail"]="CURL error"; $xmlrpc_defencoding="UTF-8"; $xmlrpcName="XML-RPC for PHP (xmlrpc-epi wrapper version)"; $xmlrpcVersion="1.0"; // let user errors start at 800 $xmlrpcerruser=800; /************************************************ * xmlrpc client class. basically a wrapper for * * xu_rpc_http(). * ************************************************/ class xmlrpc_client { var $path; var $server; var $port; var $errno; var $errstring; var $debug=0; var $username=""; var $password=""; // constructor function xmlrpc_client($path, $server, $port=80) { $this->port=$port; $this->server=$server; $this->path=$path; } // public. for debugging info. function setDebug($in) { if ($in) { $this->debug=1; } else { $this->debug=0; } } // public. for http authentication. function setCredentials($u, $p) { $this->username=$u; $this->password=$p; } // public. sent request to server. function send($msg, $timeout=0, $pause=0, $secure=false) { // where msg is an xmlrpcmsg $msg->debug=$this->debug; if ($pause) { sleep($pause); } return $this->sendPayloadHTTP10($msg, $this->server, $this->port, $timeout, $this->username, $this->password); } // private. performs http post request function sendPayloadHTTP10($msg, $server, $port, $timeout=0, $username="", $password="", $secure=false) { // Only create the payload if it was not created previously if (empty($msg->payload)) { $msg->createPayload(); } $response_buf = xu_query_http_post($msg->payload, $server, $this->path, $port, $this->debug, $timeout, $username, $password, $secure); $resp=$msg->parseResponse($response_buf); return $resp; } } // end class xmlrpc_client /****************************************** * a class to represent an xmlrpc response * ******************************************/ class xmlrpcresp { var $xv; var $fn; var $fs; var $hdrs; // constructor. function xmlrpcresp($val, $fcode=0, $fstr="") { if ($fcode!=0) { $this->fn=$fcode; $this->fs=$fstr; } else { $this->xv=$val; } } // public. get methods function faultCode() { return $this->fn;} function faultString() { return $this->fs;} function value() { return $this->xv;} // public. serialize self as xml string. function serialize() { /* check if fault */ if ($this->fn) { $result = xmlrpc_encode_request(null, xu_fault_code($this->fn, $this->fs)); } else { $php_vals = val_to_php($this->xv); // null for methodname indicates response type. $result = xmlrpc_encode_request(null, $php_vals); } return $result; } } /***************************************** * a class to represent an xmlrpc message * *****************************************/ class xmlrpcmsg { var $payload; var $methodname; var $params=array(); var $debug=0; // constructor function xmlrpcmsg($meth, $pars=0) { $this->methodname=$meth; if (is_array($pars) && sizeof($pars)>0) { for ($i=0; $iaddParam($pars[$i]); } } // unused. xmlrpc-epi does this automagically function xml_header() { return "xml_header not supported"; } // unused. not necessary function xml_footer() { return "xml_footer not supported"; } // private. performs the actual message serialization. function createPayload() { $php_params_val = array(); foreach($this->params as $param) { $php_params_val[] = val_to_php($param); } $this->payload = xmlrpc_encode_request($this->methodname, $php_params_val); } // public. returns name of method function method($meth="") { if ($meth!="") { $this->methodname=$meth; } return $this->methodname; } // public. serialization message as xml function serialize() { $this->createPayload(); return $this->payload; } // public. add/retrieve/count message params function addParam($par) { $this->params[]=$par;} function getParam($i) { return $this->params[$i];} function getNumParams() { return sizeof($this->params);} // public. in case we are given a file handle function parseResponseFile($fp) { $ipd=""; while ($data=fread($fp, 32768)) { $ipd.=$data; } return $this->parseResponse($ipd); } // public. parse xml, return as xmlrpcresp. function parseResponse($data="") { $php_val = xmlrpc_decode($data); /* check for fault */ if (is_array($php_val) && isset($php_val[0][faultCode])) { $fc = $php_val[0][faultCode]; $fs = $php_val[0][faultString]; } else { $rpc_val = php_to_val($php_val); } return new xmlrpcresp($rpc_val, $fc, $fs); } } /*************************************** * a class to represent an xmlrpc value * ***************************************/ class xmlrpcval { var $me=array(); var $mytype=0; // constructor function xmlrpcval($val=-1, $type="") { global $xmlrpcTypes; $this->me=array(); $this->mytype=0; if ($val!=-1 || $type!="") { if ($type=="") $type="string"; if ($xmlrpcTypes[$type]==1) { $this->addScalar($val,$type); } else if ($xmlrpcTypes[$type]==2) $this->addArray($val); else if ($xmlrpcTypes[$type]==3) $this->addStruct($val); } } // public. add a php scalar value. function addScalar($val, $type="string") { global $xmlrpcTypes, $xmlrpcBoolean; if ($this->mytype==1) { echo "xmlrpcval: scalar can have only one value
"; return 0; } $typeof=$xmlrpcTypes[$type]; if ($typeof!=1) { echo "xmlrpcval: not a scalar type (${typeof})
"; return 0; } if ($type==$xmlrpcBoolean) { if (strcasecmp($val,"true")==0 || $val==1 || $val==true) { $val=1; } else { $val=0; } } if ($this->mytype==2) { // we're adding to an array here $ar=$this->me["array"]; $ar[]=new xmlrpcval($val, $type); $this->me["array"]=$ar; } else { // a scalar, so set the value and remember we're scalar $this->me[$type]=$val; $this->mytype=$typeof; } return 1; } // public. add a php array function addArray($vals) { global $xmlrpcTypes; if ($this->mytype!=0) { echo "xmlrpcval: already initialized as a [" . $this->kindOf() . "]
"; return 0; } $this->mytype=$xmlrpcTypes["array"]; $this->me["array"]=$vals; return 1; } // public. add a php keyed array as a struct. function addStruct($vals) { global $xmlrpcTypes; if ($this->mytype!=0) { echo "xmlrpcval: already initialized as a [" . $this->kindOf() . "]
"; return 0; } $this->mytype=$xmlrpcTypes["struct"]; $this->me["struct"]=$vals; return 1; } // public. write myself out as html. function dump($ar) { foreach($ar as $key => $val) { echo "$key => $val
"; if ($key == 'array') { foreach($val as $key2 => $val2) { echo "-- $key2 => $val2"; } } } } // public. kind of value. // (not 1 to 1 mapping with xmlrpc types or php types) function kindOf() { switch ($this->mytype) { case 3: return "struct"; break; case 2: return "array"; break; case 1: return "scalar"; break; default: return "undef"; } } // unused. function serializedata($typ, $val) { return "serializedata not supported"; } // public. serialize self as xml. function serialize() { return $this->serializeval($this); } // public. serialize any xmlrpcval object as xml. function serializeval($o) { $php_val = val_to_php($o); $result_xml = xmlrpc_encode($php_val); return $result_xml; } // public. get struct members. function structmem($m) { $nv=$this->me["struct"][$m]; return $nv; } // public. reset struct to first item. function structreset() { reset($this->me["struct"]); } // public. get key/val pair of next struct item. function structeach() { return each($this->me["struct"]); } // public. get php type scalar value. function scalarval() { global $xmlrpcBoolean, $xmlrpcBase64; reset($this->me); list($a,$b)=each($this->me); return $b; } // public. get xmlrpc type of value. function scalartyp() { global $xmlrpcI4, $xmlrpcInt; reset($this->me); list($a,$b)=each($this->me); if ($a==$xmlrpcI4) $a=$xmlrpcInt; return $a; } // public. get array member. function arraymem($m) { $nv=$this->me["array"][$m]; return $nv; } // public. get array size function arraysize() { reset($this->me); list($a,$b)=each($this->me); return sizeof($b); } } // date helpers /***************************************************************** * These stubs need to be implemented in the C extension library. * * When they are, these calls will be commented out. -danda * *****************************************************************/ function iso8601_encode($timet, $utc=0) { // return an 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 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; } function iso8601_decode($idate, $utc=0) { // return a timet in the localtime, or UTC $t=0; if (ereg("([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; } /**************************************************************** * xmlrpc_decode takes a message in PHP xmlrpc object format and * * tranlates it into native PHP types. * ****************************************************************/ function val_to_php($xmlrpc_val) { global $epiTypeMap; global $phpTypeMap; $kind = $xmlrpc_val->kindOf(); if ($kind == "scalar") { $type = $xmlrpc_val->scalartyp(); $php_val = $xmlrpc_val->scalarval(); $php_type = $phpTypeMap[$type]; // value is stored in object as a string. we want // its native type. settype($php_val, $php_type); // magic to let xmlprc-epi engine know about base64 and datetime types. $epi_type = $epiTypeMap[$type]; if($epi_type) { xmlrpc_set_type($php_val, $epi_type); // php_val may now be an object, if epi_type = base64 or datetime. } return $php_val; } // generate php indexed array. recurse for sub-values. else if ($kind == "array") { $size = $xmlrpc_val->arraysize(); $arr = array(); for ($i = 0; $i < $size; $i++) { $arr[] = val_to_php($xmlrpc_val->arraymem($i)); } return $arr; } // generate php keyed array. recurse for sub-values. else if ($kind == "struct") { $xmlrpc_val->structreset(); $arr = array(); while (list($key,$value)=$xmlrpc_val->structeach()) { $arr[$key] = val_to_php($value); } return $arr; } } /**************************************************************** * php_to_val takes native php types and encodes them into * * xmlrpc PHP object format. * ****************************************************************/ function php_to_val($php_val) { global $xmlrpcInt; global $xmlrpcDouble; global $xmlrpcString; global $xmlrpcArray; global $xmlrpcStruct; global $xmlrpcBoolean; global $xmlrpcDateTime; global $xmlrpcBase64; // get the xmlrpc type of value. $type = xmlrpc_get_type($php_val); $xmlrpc_val = new xmlrpcval; switch ($type) { case "array": case "vector": //unused $arr = array(); foreach($php_val as $v) { $arr[] = php_to_val($v); } $xmlrpc_val->addArray($arr); break; case "object": //unused case "struct": foreach($php_val as $k => $v) { $arr[$k] = php_to_val($v); } $xmlrpc_val->addStruct($arr); break; case "integer": //unused case "int": $xmlrpc_val->addScalar($php_val, $xmlrpcInt); break; case "boolean": $xmlrpc_val->addScalar($php_val, $xmlprcBoolean); break; case "double": $xmlrpc_val->addScalar($php_val, $xmlrpcDouble); break; case "string": $xmlrpc_val->addScalar($php_val, $xmlrpcString); break; case "datetime": $xmlrpc_val->addScalar($php_val->scalar, $xmlrpcDateTime); break; case "base64": $xmlrpc_val->addScalar($php_val->scalar, $xmlrpcBase64); break; case "unknown type": default: $xmlrpc_val = false; break; } return $xmlrpc_val; } ?>