parse($xml); } /** * Return the method name associated with the call. * * @return string */ public function getMethodName() { return $this->methodname; } /** * Return the parameters. * Returns a nested array of XmlElement. * * @see XmlElement * @return array */ public function getParameters() { return $this->params; } /** * Parse the xml into its components according to spec. * This first version is a little primitive. * * @param string $xml */ private function parse($xml) { $xml = xml_2_object($xml); // sanity check if ((isset($xml->name)) && (strcasecmp($xml->name, "methodCall")!=0)) throw new CallException(elgg_echo('CallException:NotRPCCall')); // method name $this->methodname = $xml->children[0]->content; // parameters $this->params = $xml->children[1]->children; } } // Response classes /////////////////////////////////////////////////////////////////////// /** * @class XMLRPCParameter Superclass for all RPC parameters. * @author Marcus Povey */ abstract class XMLRPCParameter { protected $value; function __construct() { } } /** * @class XMLRPCIntParameter An Integer. * @author Marcus Povey */ class XMLRPCIntParameter extends XMLRPCParameter { function __construct($value) { parent::__construct(); $this->value = (int)$value; } function __toString() { return "{$this->value}\n"; } } /** * @class XMLRPCBoolParameter A boolean. * @author Marcus Povey */ class XMLRPCBoolParameter extends XMLRPCParameter { function __construct($value) { parent::__construct(); $this->value = (bool)$value; } function __toString() { $code = ($this->value) ? "1" : "0"; return "{$code}\n"; } } /** * @class XMLRPCStringParameter A string. * @author Marcus Povey */ class XMLRPCStringParameter extends XMLRPCParameter { function __construct($value) { parent::__construct(); $this->value = $value; } function __toString() { $value = htmlentities($this->value); return "{$value}\n"; } } /** * @class XMLRPCDoubleParameter A double precision signed floating point number. * @author Marcus Povey */ class XMLRPCDoubleParameter extends XMLRPCParameter { function __construct($value) { parent::__construct(); $this->value = (float)$value; } function __toString() { return "{$this->value}\n"; } } /** * @class XMLRPCDateParameter An ISO8601 data and time. * @author Marcus Povey */ class XMLRPCDateParameter extends XMLRPCParameter { /** * Construct a date * * @param int $timestamp The unix timestamp, or blank for "now". */ function __construct($timestamp = 0) { parent::__construct(); $this->value = $timestamp; if (!$timestamp) $this->value = time(); } function __toString() { $value = date('c', $this->value); return "{$value}\n"; } } /** * @class XMLRPCBase64Parameter A base 64 encoded blob of binary. * @author Marcus Povey */ class XMLRPCBase64Parameter extends XMLRPCParameter { /** * Construct a base64 encoded block * * @param string $blob Unencoded binary blob */ function __construct($blob) { parent::__construct(); $this->value = base64_encode($blob); } function __toString() { return "{$value}\n"; } } /** * @class XMLRPCStructParameter A structure containing other XMLRPCParameter objects. * @author Marcus Povey */ class XMLRPCStructParameter extends XMLRPCParameter { /** * Construct a struct. * * @param array $parameters Optional associated array of parameters, if not provided then addField must be used. */ function __construct($parameters = NULL) { parent::__construct(); if (is_array($parameters)) { foreach ($parameters as $k => $v) $this->addField($k, $v); } } /** * Add a field to the container. * * @param string $name The name of the field. * @param XMLRPCParameter $value The value. */ public function addField($name, XMLRPCParameter $value) { if (!is_array($this->value)) $this->value = array(); $this->value[$name] = $value; } function __toString() { $params = ""; foreach ($this->value as $k => $v) { $params .= "$k$v"; } return <<< END $params END; } } /** * @class XMLRPCArrayParameter An array containing other XMLRPCParameter objects. * @author Marcus Povey */ class XMLRPCArrayParameter extends XMLRPCParameter { /** * Construct an array. * * @param array $parameters Optional array of parameters, if not provided then addField must be used. */ function __construct($parameters = NULL) { parent::__construct(); if (is_array($parameters)) { foreach ($parameters as $v) $this->addField($v); } } /** * Add a field to the container. * * @param XMLRPCParameter $value The value. */ public function addField(XMLRPCParameter $value) { if (!is_array($this->value)) $this->value = array(); $this->value[] = $value; } function __toString() { $params = ""; foreach ($this->value as $value) { $params .= "$value"; } return <<< END $params END; } } /** * @class XMLRPCResponse XML-RPC Response. * @author Marcus Povey */ abstract class XMLRPCResponse { /** An array of parameters */ protected $parameters = array(); /** * Add a parameter here. * * @param XMLRPCParameter $param The parameter. */ public function addParameter(XMLRPCParameter $param) { if (!is_array($this->parameters)) $this->parameters = array(); $this->parameters[] = $param; } public function addInt($value) { $this->addParameter(new XMLRPCIntParameter($value)); } public function addString($value) { $this->addParameter(new XMLRPCStringParameter($value)); } public function addDouble($value) { $this->addParameter(new XMLRPCDoubleParameter($value)); } public function addBoolean($value) { $this->addParameter(new XMLRPCBoolParameter($value)); } } /** * @class XMLRPCSuccessResponse * @author Marcus Povey */ class XMLRPCSuccessResponse extends XMLRPCResponse { /** * Output to XML. */ public function __toString() { $params = ""; foreach ($this->parameters as $param) $params .= "$param\n"; return <<< END $params END; } } /** * @class XMLRPCErrorResponse * @author Marcus Povey */ class XMLRPCErrorResponse extends XMLRPCResponse { /** * Set the error response and error code. * * @param string $message The message * @param int $code Error code (default = system error as defined by http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php) */ function __construct($message, $code = -32400) { $this->addParameter( new XMLRPCStructParameter( array ( 'faultCode' => new XMLRPCIntParameter($code), 'faultString' => new XMLRPCStringParameter($message) ) ) ); } /** * Output to XML. */ public function __toString() { return <<< END {$this->parameters[0]} END; } } // Helper functions /////////////////////////////////////////////////////////////////////// /** * parse XMLRPCCall parameters * * Convert an XMLRPCCall result array into native data types * * @param array $parameters * @return array */ function xmlrpc_parse_params($parameters) { $result = array(); foreach ($parameters as $parameter) { $result[] = xmlrpc_scalar_value($parameter); } return $result; } /** * Extract the scalar value of an XMLObject type result array * * @param XMLObject $object * @return mixed */ function xmlrpc_scalar_value($object) { if ($object->name == 'param') { $object = $object->children[0]->children[0]; } switch ($object->name) { case 'string': return $object->content; case 'array': foreach ($object->children[0]->children as $child) { $value[] = xmlrpc_scalar_value($child); } return $value; case 'struct': foreach ($object->children as $child) { $value[$child->children[0]->content] = xmlrpc_scalar_value($child->children[1]->children[0]); } return $value; case 'boolean': return (boolean) $object->content; case 'int': return (int) $object->content; case 'double': return (double) $object->content; case 'dateTime.iso8601': return (int) strtotime($object->content); case 'base64': return base64_decode($object->content); case 'value': return xmlrpc_scalar_value($object->children[0]); default: // TODO unsupported, throw an error return false; } } // Functions for adding handlers ////////////////////////////////////////////////////////// /** XML-RPC Handlers */ $XML_RPC_HANDLERS = array(); /** * Register a method handler for a given XML-RPC method. * * @param string $method Method parameter. * @param string $handler The handler function. This function accepts once XMLRPCCall object and must return a XMLRPCResponse object. * @return bool */ function register_xmlrpc_handler($method, $handler) { global $XML_RPC_HANDLERS; $XML_RPC_HANDLERS[$method] = $handler; } /** * Trigger a method call and pass the relevant parameters to the funciton. * * @param XMLRPCCall $parameters The call and parameters. * @return XMLRPCCall */ function trigger_xmlrpc_handler(XMLRPCCall $parameters) { global $XML_RPC_HANDLERS; // Go through and see if we have a handler if (isset($XML_RPC_HANDLERS[$parameters->getMethodName()])) { $handler = $XML_RPC_HANDLERS[$parameters->getMethodName()]; $result = $handler($parameters); if (!($result instanceof XMLRPCResponse)) throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedReturnFormat')); // Result in right format, return it. return $result; } // if no handler then throw exception throw new NotImplementedException(sprintf(elgg_echo('NotImplementedException:XMLRPCMethodNotImplemented'), $method)); } // Error handler functions //////////////////////////////////////////////////////////////// /** * PHP Error handler function. * This function acts as a wrapper to catch and report PHP error messages. * * @see http://uk3.php.net/set-error-handler * @param unknown_type $errno * @param unknown_type $errmsg * @param unknown_type $filename * @param unknown_type $linenum * @param unknown_type $vars */ function __php_xmlrpc_error_handler($errno, $errmsg, $filename, $linenum, $vars) { $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " . $filename . " (line " . $linenum . ")"; switch ($errno) { case E_USER_ERROR: error_log("ERROR: " . $error); // Since this is a fatal error, we want to stop any further execution but do so gracefully. throw new Exception("ERROR: " . $error); break; case E_WARNING : case E_USER_WARNING : error_log("WARNING: " . $error); break; default: error_log("DEBUG: " . $error); } } /** * PHP Exception handler for XMLRPC. * @param Exception $exception */ function __php_xmlrpc_exception_handler($exception) { error_log("*** FATAL EXCEPTION (XML-RPC) *** : " . $exception); page_draw($exception->getMessage(), elgg_view("xml-rpc/output", array('result' => new XMLRPCErrorResponse($exception->getMessage(), $exception->getCode()==0 ? -32400 : $exception->getCode())))); } ?>