<?php
/*
xmlrpc_emu.inc -- xmlrpc.inc wrapper.
08/30/01 - last modified by Dan Libby <hide@address.com>
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
// <hide@address.com>
//
// $Id: xmlrpc_emu.inc 2 2004-08-31 08:21:00Z kkrusteff $
// 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; $i<sizeof($pars); $i++)
$this->addParam($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 "<B>xmlrpcval</B>: scalar can have only one value<BR>";
return 0;
}
$typeof=$xmlrpcTypes[$type];
if ($typeof!=1) {
echo "<B>xmlrpcval</B>: not a scalar type (${typeof})<BR>";
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 "<B>xmlrpcval</B>: already initialized as a [" .
$this->kindOf() . "]<BR>";
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 "<B>xmlrpcval</B>: already initialized as a [" .
$this->kindOf() . "]<BR>";
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<br>";
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;
}
?>