diff options
-rw-r--r-- | engine/handlers/xml-rpc_handler.php | 48 | ||||
-rw-r--r-- | engine/lib/xml-rpc.php | 510 | ||||
-rw-r--r-- | htaccess_dist | 3 | ||||
-rw-r--r-- | languages/en.php | 14 | ||||
-rw-r--r-- | views/xml/xml-rpc/output.php | 16 |
5 files changed, 589 insertions, 2 deletions
diff --git a/engine/handlers/xml-rpc_handler.php b/engine/handlers/xml-rpc_handler.php new file mode 100644 index 000000000..85de621b3 --- /dev/null +++ b/engine/handlers/xml-rpc_handler.php @@ -0,0 +1,48 @@ +<?php + /** + * Elgg XML-RPC handler. + * + * @package Elgg + * @subpackage Core + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2 + * @author Marcus Povey + * @copyright Curverider Ltd 2008 + * @link http://elgg.org/ + */ + + // Load Elgg engine + require_once("../start.php"); + global $CONFIG; + + // Register the error handler + error_reporting(E_ALL); + set_error_handler('__php_xmlrpc_error_handler'); + + // Register a default exception handler + set_exception_handler('__php_xmlrpc_exception_handler'); + + // Set some defaults + $result = null; + $view = 'xml'; // Set default view regardless + + // Get the post data + $input = get_post_data(); + + if ($input) + { + // Parse structures from xml + $call = new XMLRPCCall($input); + + // Process call + $result = trigger_xmlrpc_handler($call); + } + else + throw new CallException(elgg_echo('xmlrpc:noinputdata')); + + if (!($result instanceof XMLRPCResponse)) + throw new APIException(elgg_echo('APIException:ApiResultUnknown')); + + // Output result + page_draw("XML-RPC", elgg_echo("xml-rpc/output", array('result' => $result))); + +?>
\ No newline at end of file diff --git a/engine/lib/xml-rpc.php b/engine/lib/xml-rpc.php new file mode 100644 index 000000000..d6a391fce --- /dev/null +++ b/engine/lib/xml-rpc.php @@ -0,0 +1,510 @@ +<?php + /** + * Elgg XML-RPC library. + * Contains functions and classes to handle XML-RPC services, currently only server only. + * + * @package Elgg + * @subpackage Core + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2 + * @author Marcus Povey + * @copyright Curverider Ltd 2008 + * @link http://elgg.org/ + */ + + // XMLRPC Call //////////////////////////////////////////////////////////////////////////// + + /** + * @class XMLRPCCall + * This class represents + * @author Marcus Povey + */ + class XMLRPCCall + { + /** Method name */ + private $methodname; + /** Parameters */ + private $params; + + /** + * Construct a new XML RPC Call + * + * @param string $xml + */ + function __construct($xml) + { + $this->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; + + print_r($this); + } + } + + // 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 "<value><i4>{$this->value}</i4></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 "<value><boolean>{$code}</boolean></value>\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><string>{$value}</string></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 "<value><double>{$this->value}</double></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><dateTime.iso8601>{$value}</dateTime.iso8601></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><base64>{$value}</base64></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 .= "<member><name>$k</name>$v</member>"; + } + + return <<< END +<struct> + $params +</struct> +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 +<array> + <data> + $params + </data> +</array> +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>$param</param>\n"; + + return <<< END +<methodResponse> + <params> + $params + </params> +</methodResponse> +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 +<methodResponse> + <fault> + <value> + {$this->parameters[0]} + </value> + </fault> +</methodResponse> +END; + } + } + + // 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()])) + { + $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 (API) *** : " . $exception); + + page_draw($exception->getMessage(), elgg_view("xml-rpc/output", array('result' => new XMLRPCErrorResponse($exception->getMessage(), $exception->getCode()==0 ? -32400 : $exception->getCode())))); + } +?>
\ No newline at end of file diff --git a/htaccess_dist b/htaccess_dist index f44966365..720a9b4b3 100644 --- a/htaccess_dist +++ b/htaccess_dist @@ -20,3 +20,6 @@ RewriteRule ^json\/([0-9]+)\/([A-Za-z]+)\/([A-Za-z]+)\/$ services/export/handler RewriteRule ^\_css\/css\.css$ _css/css.php
RewriteRule ^pg\/([A-Za-z]+)\/(.*)$ engine/handlers/pagehandler.php?handler=$1&page=$2
+
+RewriteRule xml-rpc.php engine/handlers/xml-rpc_handler.php
+RewriteRule mt/mt-xmlrpc.cgi engine/handlers/xml-rpc_handler.php
diff --git a/languages/en.php b/languages/en.php index b8a60d9a9..fcaeafd10 100644 --- a/languages/en.php +++ b/languages/en.php @@ -127,7 +127,11 @@ 'APIException:MissingContentType' => "Missing content type for post data",
'SecurityException:InvalidPostHash' => "POST data hash is invalid - Expected %s but got %s.",
'SecurityException:DupePacket' => "Packet signature already seen.",
- 'SecurityException:InvalidAPIKey' => "Invalid or missing API Key.",
+ 'SecurityException:InvalidAPIKey' => "Invalid or missing API Key.", + + 'NotImplementedException:XMLRPCMethodNotImplemented' => "XML-RPC method call '%s' not implemented.", + 'InvalidParameterException:UnexpectedReturnFormat' => "Call to method '%s' returned an unexpected result.", + 'CallException:NotRPCCall' => "Call does not appear to be a valid XML-RPC call",
/**
* User details
@@ -313,7 +317,13 @@ */
'welcome' => "Welcome %s",
- 'welcome_message' => "Welcome to this Elgg installation.",
+ 'welcome_message' => "Welcome to this Elgg installation.", + + + /** + * XML-RPC + */ + 'xmlrpc:noinputdata' => "Input data missing",
);
diff --git a/views/xml/xml-rpc/output.php b/views/xml/xml-rpc/output.php new file mode 100644 index 000000000..711ca9fba --- /dev/null +++ b/views/xml/xml-rpc/output.php @@ -0,0 +1,16 @@ +<?php + /** + * Elgg XML output for XML-RPC + * + * @package Elgg + * @subpackage Core + * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2 + * @author Marcus Povey + * @copyright Curverider Ltd 2008 + * @link http://elgg.org/ + */ + + $result = $vars['result']; + + echo "$result"; +?>
\ No newline at end of file |