{$this->method}
EOD;
foreach ($this->args as $arg) {
$this->xml .= '';
$v = new IXR_Value($arg);
$this->xml .= $v->getXml();
$this->xml .= "\n";
}
$this->xml .= '';
}
function getLength() {
return strlen($this->xml);
}
function getXml() {
return $this->xml;
}
}
# FIXME use DokuHTTPClient here
class IXR_Client {
var $server;
var $port;
var $path;
var $useragent;
var $response;
var $message = false;
var $debug = false;
// Storage place for an error message
var $error = false;
function IXR_Client($server, $path = false, $port = 80) {
if (!$path) {
// Assume we have been given a URL instead
$bits = parse_url($server);
$this->server = $bits['host'];
$this->port = isset($bits['port']) ? $bits['port'] : 80;
$this->path = isset($bits['path']) ? $bits['path'] : '/';
// Make absolutely sure we have a path
if (!$this->path) {
$this->path = '/';
}
} else {
$this->server = $server;
$this->path = $path;
$this->port = $port;
}
$this->useragent = 'The Incutio XML-RPC PHP Library';
}
function query() {
$args = func_get_args();
$method = array_shift($args);
$request = new IXR_Request($method, $args);
$length = $request->getLength();
$xml = $request->getXml();
$r = "\r\n";
$request = "POST {$this->path} HTTP/1.0$r";
$request .= "Host: {$this->server}$r";
$request .= "Content-Type: text/xml$r";
$request .= "User-Agent: {$this->useragent}$r";
$request .= "Content-length: {$length}$r$r";
$request .= $xml;
// Now send the request
if ($this->debug) {
echo ''.htmlspecialchars($request)."\n
\n\n";
}
$fp = @fsockopen($this->server, $this->port);
if (!$fp) {
$this->error = new IXR_Error(-32300, 'transport error - could not open socket');
return false;
}
fputs($fp, $request);
$contents = '';
$gotFirstLine = false;
$gettingHeaders = true;
while (!feof($fp)) {
$line = fgets($fp, 4096);
if (!$gotFirstLine) {
// Check line for '200'
if (strstr($line, '200') === false) {
$this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200');
return false;
}
$gotFirstLine = true;
}
if (trim($line) == '') {
$gettingHeaders = false;
}
if (!$gettingHeaders) {
$contents .= trim($line)."\n";
}
}
if ($this->debug) {
echo ''.htmlspecialchars($contents)."\n
\n\n";
}
// Now parse what we've got back
$this->message = new IXR_Message($contents);
if (!$this->message->parse()) {
// XML error
$this->error = new IXR_Error(-32700, 'parse error. not well formed');
return false;
}
// Is the message a fault?
if ($this->message->messageType == 'fault') {
$this->error = new IXR_Error($this->message->faultCode, $this->message->faultString);
return false;
}
// Message must be OK
return true;
}
function getResponse() {
// methodResponses can only have one param - return that
return $this->message->params[0];
}
function isError() {
return (is_object($this->error));
}
function getErrorCode() {
return $this->error->code;
}
function getErrorMessage() {
return $this->error->message;
}
}
class IXR_Error {
var $code;
var $message;
function IXR_Error($code, $message) {
$this->code = $code;
$this->message = $message;
}
function getXml() {
$xml = <<
faultCode
{$this->code}
faultString
{$this->message}
EOD;
return $xml;
}
}
class IXR_Date {
var $year;
var $month;
var $day;
var $hour;
var $minute;
var $second;
function IXR_Date($time) {
// $time can be a PHP timestamp or an ISO one
if (is_numeric($time)) {
$this->parseTimestamp($time);
} else {
$this->parseIso($time);
}
}
function parseTimestamp($timestamp) {
$this->year = date('Y', $timestamp);
$this->month = date('Y', $timestamp);
$this->day = date('Y', $timestamp);
$this->hour = date('H', $timestamp);
$this->minute = date('i', $timestamp);
$this->second = date('s', $timestamp);
}
function parseIso($iso) {
$this->year = substr($iso, 0, 4);
$this->month = substr($iso, 4, 2);
$this->day = substr($iso, 6, 2);
$this->hour = substr($iso, 9, 2);
$this->minute = substr($iso, 12, 2);
$this->second = substr($iso, 15, 2);
}
function getIso() {
return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second;
}
function getXml() {
return ''.$this->getIso().'';
}
function getTimestamp() {
return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
}
}
class IXR_Base64 {
var $data;
function IXR_Base64($data) {
$this->data = $data;
}
function getXml() {
return ''.base64_encode($this->data).'';
}
}
class IXR_IntrospectionServer extends IXR_Server {
var $signatures;
var $help;
function IXR_IntrospectionServer() {
$this->setCallbacks();
$this->setCapabilities();
$this->capabilities['introspection'] = array(
'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
'specVersion' => 1
);
$this->addCallback(
'system.methodSignature',
'this:methodSignature',
array('array', 'string'),
'Returns an array describing the return type and required parameters of a method'
);
$this->addCallback(
'system.getCapabilities',
'this:getCapabilities',
array('struct'),
'Returns a struct describing the XML-RPC specifications supported by this server'
);
$this->addCallback(
'system.listMethods',
'this:listMethods',
array('array'),
'Returns an array of available methods on this server'
);
$this->addCallback(
'system.methodHelp',
'this:methodHelp',
array('string', 'string'),
'Returns a documentation string for the specified method'
);
}
function addCallback($method, $callback, $args, $help) {
$this->callbacks[$method] = $callback;
$this->signatures[$method] = $args;
$this->help[$method] = $help;
}
function call($methodname, $args) {
// Make sure it's in an array
if ($args && !is_array($args)) {
$args = array($args);
}
// Over-rides default call method, adds signature check
if (!$this->hasMethod($methodname)) {
return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.');
}
$method = $this->callbacks[$methodname];
$signature = $this->signatures[$methodname];
$returnType = array_shift($signature);
// Check the number of arguments
if (count($args) != count($signature)) {
// print 'Num of args: '.count($args).' Num in signature: '.count($signature);
return new IXR_Error(-32602, 'server error. wrong number of method parameters');
}
// Check the argument types
$ok = true;
$argsbackup = $args;
for ($i = 0, $j = count($args); $i < $j; $i++) {
$arg = array_shift($args);
$type = array_shift($signature);
switch ($type) {
case 'int':
case 'i4':
if (is_array($arg) || !is_int($arg)) {
$ok = false;
}
break;
case 'base64':
case 'string':
if (!is_string($arg)) {
$ok = false;
}
break;
case 'boolean':
if ($arg !== false && $arg !== true) {
$ok = false;
}
break;
case 'float':
case 'double':
if (!is_float($arg)) {
$ok = false;
}
break;
case 'date':
case 'dateTime.iso8601':
if (!is_a($arg, 'IXR_Date')) {
$ok = false;
}
break;
}
if (!$ok) {
return new IXR_Error(-32602, 'server error. invalid method parameters');
}
}
// It passed the test - run the "real" method call
return parent::call($methodname, $argsbackup);
}
function methodSignature($method) {
if (!$this->hasMethod($method)) {
return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.');
}
// We should be returning an array of types
$types = $this->signatures[$method];
$return = array();
foreach ($types as $type) {
switch ($type) {
case 'string':
$return[] = 'string';
break;
case 'int':
case 'i4':
$return[] = 42;
break;
case 'double':
$return[] = 3.1415;
break;
case 'dateTime.iso8601':
$return[] = new IXR_Date(time());
break;
case 'boolean':
$return[] = true;
break;
case 'base64':
$return[] = new IXR_Base64('base64');
break;
case 'array':
$return[] = array('array');
break;
case 'struct':
$return[] = array('struct' => 'struct');
break;
}
}
return $return;
}
function methodHelp($method) {
return $this->help[$method];
}
}
class IXR_ClientMulticall extends IXR_Client {
var $calls = array();
function IXR_ClientMulticall($server, $path = false, $port = 80) {
parent::IXR_Client($server, $path, $port);
$this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)';
}
function addCall() {
$args = func_get_args();
$methodName = array_shift($args);
$struct = array(
'methodName' => $methodName,
'params' => $args
);
$this->calls[] = $struct;
}
function query() {
// Prepare multicall, then call the parent::query() method
return parent::query('system.multicall', $this->calls);
}
}
?>