From 0ae8cbf5c0b1a198b963490985b7738392ebcb97 Mon Sep 17 00:00:00 2001 From: ahriman Date: Mon, 3 Dec 2018 19:22:25 -0500 Subject: installed dokuwiki, added to navbar, updated news --- wiki/inc/IXR_Library.php | 1132 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1132 insertions(+) create mode 100644 wiki/inc/IXR_Library.php (limited to 'wiki/inc/IXR_Library.php') diff --git a/wiki/inc/IXR_Library.php b/wiki/inc/IXR_Library.php new file mode 100644 index 0000000..5ae1402 --- /dev/null +++ b/wiki/inc/IXR_Library.php @@ -0,0 +1,1132 @@ + + */ +class IXR_Value { + + /** @var IXR_Value[]|IXR_Date|IXR_Base64|int|bool|double|string */ + var $data; + /** @var string */ + var $type; + + /** + * @param mixed $data + * @param bool $type + */ + function __construct($data, $type = false) { + $this->data = $data; + if(!$type) { + $type = $this->calculateType(); + } + $this->type = $type; + if($type == 'struct') { + // Turn all the values in the array in to new IXR_Value objects + foreach($this->data as $key => $value) { + $this->data[$key] = new IXR_Value($value); + } + } + if($type == 'array') { + for($i = 0, $j = count($this->data); $i < $j; $i++) { + $this->data[$i] = new IXR_Value($this->data[$i]); + } + } + } + + /** + * @return string + */ + function calculateType() { + if($this->data === true || $this->data === false) { + return 'boolean'; + } + if(is_integer($this->data)) { + return 'int'; + } + if(is_double($this->data)) { + return 'double'; + } + + // Deal with IXR object types base64 and date + if(is_object($this->data) && is_a($this->data, 'IXR_Date')) { + return 'date'; + } + if(is_object($this->data) && is_a($this->data, 'IXR_Base64')) { + return 'base64'; + } + + // If it is a normal PHP object convert it in to a struct + if(is_object($this->data)) { + $this->data = get_object_vars($this->data); + return 'struct'; + } + if(!is_array($this->data)) { + return 'string'; + } + + // We have an array - is it an array or a struct? + if($this->isStruct($this->data)) { + return 'struct'; + } else { + return 'array'; + } + } + + /** + * @return bool|string + */ + function getXml() { + // Return XML for this value + switch($this->type) { + case 'boolean': + return '' . (($this->data) ? '1' : '0') . ''; + break; + case 'int': + return '' . $this->data . ''; + break; + case 'double': + return '' . $this->data . ''; + break; + case 'string': + return '' . htmlspecialchars($this->data) . ''; + break; + case 'array': + $return = '' . "\n"; + foreach($this->data as $item) { + $return .= ' ' . $item->getXml() . "\n"; + } + $return .= ''; + return $return; + break; + case 'struct': + $return = '' . "\n"; + foreach($this->data as $name => $value) { + $return .= " $name"; + $return .= $value->getXml() . "\n"; + } + $return .= ''; + return $return; + break; + case 'date': + case 'base64': + return $this->data->getXml(); + break; + } + return false; + } + + /** + * Checks whether or not the supplied array is a struct or not + * + * @param array $array + * @return boolean + */ + function isStruct($array) { + $expected = 0; + foreach($array as $key => $value) { + if((string) $key != (string) $expected) { + return true; + } + $expected++; + } + return false; + } +} + +/** + * IXR_MESSAGE + * + * @package IXR + * @since 1.5 + * + */ +class IXR_Message { + var $message; + var $messageType; // methodCall / methodResponse / fault + var $faultCode; + var $faultString; + var $methodName; + var $params; + + // Current variable stacks + var $_arraystructs = array(); // The stack used to keep track of the current array/struct + var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array + var $_currentStructName = array(); // A stack as well + var $_param; + var $_value; + var $_currentTag; + var $_currentTagContents; + var $_lastseen; + // The XML parser + var $_parser; + + /** + * @param string $message + */ + function __construct($message) { + $this->message =& $message; + } + + /** + * @return bool + */ + function parse() { + // first remove the XML declaration + // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages + $header = preg_replace('/<\?xml.*?\?' . '>/', '', substr($this->message, 0, 100), 1); + $this->message = substr_replace($this->message, $header, 0, 100); + + // workaround for a bug in PHP/libxml2, see http://bugs.php.net/bug.php?id=45996 + $this->message = str_replace('<', '<', $this->message); + $this->message = str_replace('>', '>', $this->message); + $this->message = str_replace('&', '&', $this->message); + $this->message = str_replace(''', ''', $this->message); + $this->message = str_replace('"', '"', $this->message); + $this->message = str_replace("\x0b", ' ', $this->message); //vertical tab + if(trim($this->message) == '') { + return false; + } + $this->_parser = xml_parser_create(); + // Set XML parser to take the case of tags in to account + xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); + // Set XML parser callback functions + xml_set_object($this->_parser, $this); + xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); + xml_set_character_data_handler($this->_parser, 'cdata'); + $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages + $final = false; + do { + if(strlen($this->message) <= $chunk_size) { + $final = true; + } + $part = substr($this->message, 0, $chunk_size); + $this->message = substr($this->message, $chunk_size); + if(!xml_parse($this->_parser, $part, $final)) { + return false; + } + if($final) { + break; + } + } while(true); + xml_parser_free($this->_parser); + + // Grab the error messages, if any + if($this->messageType == 'fault') { + $this->faultCode = $this->params[0]['faultCode']; + $this->faultString = $this->params[0]['faultString']; + } + return true; + } + + /** + * @param $parser + * @param string $tag + * @param $attr + */ + function tag_open($parser, $tag, $attr) { + $this->_currentTagContents = ''; + $this->_currentTag = $tag; + + switch($tag) { + case 'methodCall': + case 'methodResponse': + case 'fault': + $this->messageType = $tag; + break; + /* Deal with stacks of arrays and structs */ + case 'data': // data is to all intents and purposes more interesting than array + $this->_arraystructstypes[] = 'array'; + $this->_arraystructs[] = array(); + break; + case 'struct': + $this->_arraystructstypes[] = 'struct'; + $this->_arraystructs[] = array(); + break; + } + $this->_lastseen = $tag; + } + + /** + * @param $parser + * @param string $cdata + */ + function cdata($parser, $cdata) { + $this->_currentTagContents .= $cdata; + } + + /** + * @param $parser + * @param $tag + */ + function tag_close($parser, $tag) { + $value = null; + $valueFlag = false; + switch($tag) { + case 'int': + case 'i4': + $value = (int) trim($this->_currentTagContents); + $valueFlag = true; + break; + case 'double': + $value = (double) trim($this->_currentTagContents); + $valueFlag = true; + break; + case 'string': + $value = (string) $this->_currentTagContents; + $valueFlag = true; + break; + case 'dateTime.iso8601': + $value = new IXR_Date(trim($this->_currentTagContents)); + $valueFlag = true; + break; + case 'value': + // "If no type is indicated, the type is string." + if($this->_lastseen == 'value') { + $value = (string) $this->_currentTagContents; + $valueFlag = true; + } + break; + case 'boolean': + $value = (boolean) trim($this->_currentTagContents); + $valueFlag = true; + break; + case 'base64': + $value = base64_decode($this->_currentTagContents); + $valueFlag = true; + break; + /* Deal with stacks of arrays and structs */ + case 'data': + case 'struct': + $value = array_pop($this->_arraystructs); + array_pop($this->_arraystructstypes); + $valueFlag = true; + break; + case 'member': + array_pop($this->_currentStructName); + break; + case 'name': + $this->_currentStructName[] = trim($this->_currentTagContents); + break; + case 'methodName': + $this->methodName = trim($this->_currentTagContents); + break; + } + + if($valueFlag) { + if(count($this->_arraystructs) > 0) { + // Add value to struct or array + if($this->_arraystructstypes[count($this->_arraystructstypes) - 1] == 'struct') { + // Add to struct + $this->_arraystructs[count($this->_arraystructs) - 1][$this->_currentStructName[count($this->_currentStructName) - 1]] = $value; + } else { + // Add to array + $this->_arraystructs[count($this->_arraystructs) - 1][] = $value; + } + } else { + // Just add as a parameter + $this->params[] = $value; + } + } + $this->_currentTagContents = ''; + $this->_lastseen = $tag; + } +} + +/** + * IXR_Server + * + * @package IXR + * @since 1.5 + */ +class IXR_Server { + var $data; + /** @var array */ + var $callbacks = array(); + var $message; + /** @var array */ + var $capabilities; + + /** + * @param array|bool $callbacks + * @param bool $data + * @param bool $wait + */ + function __construct($callbacks = false, $data = false, $wait = false) { + $this->setCapabilities(); + if($callbacks) { + $this->callbacks = $callbacks; + } + $this->setCallbacks(); + + if(!$wait) { + $this->serve($data); + } + } + + /** + * @param bool|string $data + */ + function serve($data = false) { + if(!$data) { + + $postData = trim(http_get_raw_post_data()); + if(!$postData) { + header('Content-Type: text/plain'); // merged from WP #9093 + die('XML-RPC server accepts POST requests only.'); + } + $data = $postData; + } + $this->message = new IXR_Message($data); + if(!$this->message->parse()) { + $this->error(-32700, 'parse error. not well formed'); + } + if($this->message->messageType != 'methodCall') { + $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); + } + $result = $this->call($this->message->methodName, $this->message->params); + + // Is the result an error? + if(is_a($result, 'IXR_Error')) { + $this->error($result); + } + + // Encode the result + $r = new IXR_Value($result); + $resultxml = $r->getXml(); + + // Create the XML + $xml = << + + + + $resultxml + + + + + +EOD; + // Send it + $this->output($xml); + } + + /** + * @param string $methodname + * @param array $args + * @return IXR_Error|mixed + */ + function call($methodname, $args) { + if(!$this->hasMethod($methodname)) { + return new IXR_Error(-32601, 'server error. requested method ' . $methodname . ' does not exist.'); + } + $method = $this->callbacks[$methodname]; + + // Perform the callback and send the response + + # Removed for DokuWiki to have a more consistent interface + # if (count($args) == 1) { + # // If only one parameter just send that instead of the whole array + # $args = $args[0]; + # } + + # Adjusted for DokuWiki to use call_user_func_array + + // args need to be an array + $args = (array) $args; + + // Are we dealing with a function or a method? + if(is_string($method) && substr($method, 0, 5) == 'this:') { + // It's a class method - check it exists + $method = substr($method, 5); + if(!method_exists($this, $method)) { + return new IXR_Error(-32601, 'server error. requested class method "' . $method . '" does not exist.'); + } + // Call the method + #$result = $this->$method($args); + $result = call_user_func_array(array(&$this, $method), $args); + } elseif(substr($method, 0, 7) == 'plugin:') { + list($pluginname, $callback) = explode(':', substr($method, 7), 2); + if(!plugin_isdisabled($pluginname)) { + $plugin = plugin_load('action', $pluginname); + return call_user_func_array(array($plugin, $callback), $args); + } else { + return new IXR_Error(-99999, 'server error'); + } + } else { + // It's a function - does it exist? + if(is_array($method)) { + if(!is_callable(array($method[0], $method[1]))) { + return new IXR_Error(-32601, 'server error. requested object method "' . $method[1] . '" does not exist.'); + } + } else if(!function_exists($method)) { + return new IXR_Error(-32601, 'server error. requested function "' . $method . '" does not exist.'); + } + + // Call the function + $result = call_user_func($method, $args); + } + return $result; + } + + /** + * @param int $error + * @param string|bool $message + */ + function error($error, $message = false) { + // Accepts either an error object or an error code and message + if($message && !is_object($error)) { + $error = new IXR_Error($error, $message); + } + $this->output($error->getXml()); + } + + /** + * @param string $xml + */ + function output($xml) { + header('Content-Type: text/xml; charset=utf-8'); + echo '', "\n", $xml; + exit; + } + + /** + * @param string $method + * @return bool + */ + function hasMethod($method) { + return in_array($method, array_keys($this->callbacks)); + } + + function setCapabilities() { + // Initialises capabilities array + $this->capabilities = array( + 'xmlrpc' => array( + 'specUrl' => 'http://www.xmlrpc.com/spec', + 'specVersion' => 1 + ), + 'faults_interop' => array( + 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', + 'specVersion' => 20010516 + ), + 'system.multicall' => array( + 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', + 'specVersion' => 1 + ), + ); + } + + /** + * @return mixed + */ + function getCapabilities() { + return $this->capabilities; + } + + function setCallbacks() { + $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; + $this->callbacks['system.listMethods'] = 'this:listMethods'; + $this->callbacks['system.multicall'] = 'this:multiCall'; + } + + /** + * @return array + */ + function listMethods() { + // Returns a list of methods - uses array_reverse to ensure user defined + // methods are listed before server defined methods + return array_reverse(array_keys($this->callbacks)); + } + + /** + * @param array $methodcalls + * @return array + */ + function multiCall($methodcalls) { + // See http://www.xmlrpc.com/discuss/msgReader$1208 + $return = array(); + foreach($methodcalls as $call) { + $method = $call['methodName']; + $params = $call['params']; + if($method == 'system.multicall') { + $result = new IXR_Error(-32800, 'Recursive calls to system.multicall are forbidden'); + } else { + $result = $this->call($method, $params); + } + if(is_a($result, 'IXR_Error')) { + $return[] = array( + 'faultCode' => $result->code, + 'faultString' => $result->message + ); + } else { + $return[] = array($result); + } + } + return $return; + } +} + +/** + * IXR_Request + * + * @package IXR + * @since 1.5 + */ +class IXR_Request { + /** @var string */ + var $method; + /** @var array */ + var $args; + /** @var string */ + var $xml; + + /** + * @param string $method + * @param array $args + */ + function __construct($method, $args) { + $this->method = $method; + $this->args = $args; + $this->xml = << + +{$this->method} + + +EOD; + foreach($this->args as $arg) { + $this->xml .= ''; + $v = new IXR_Value($arg); + $this->xml .= $v->getXml(); + $this->xml .= "\n"; + } + $this->xml .= ''; + } + + /** + * @return int + */ + function getLength() { + return strlen($this->xml); + } + + /** + * @return string + */ + function getXml() { + return $this->xml; + } +} + +/** + * IXR_Client + * + * @package IXR + * @since 1.5 + * + * Changed for DokuWiki to use DokuHTTPClient + * + * This should be compatible to the original class, but uses DokuWiki's + * HTTP client library which will respect proxy settings + * + * Because the XMLRPC client is not used in DokuWiki currently this is completely + * untested + */ +class IXR_Client extends DokuHTTPClient { + var $posturl = ''; + /** @var IXR_Message|bool */ + var $message = false; + + // Storage place for an error message + /** @var IXR_Error|bool */ + var $xmlerror = false; + + /** + * @param string $server + * @param string|bool $path + * @param int $port + * @param int $timeout + */ + function __construct($server, $path = false, $port = 80, $timeout = 15) { + parent::__construct(); + if(!$path) { + // Assume we have been given a URL instead + $this->posturl = $server; + } else { + $this->posturl = 'http://' . $server . ':' . $port . $path; + } + $this->timeout = $timeout; + } + + /** + * parameters: method and arguments + * @return bool success or error + */ + function query() { + $args = func_get_args(); + $method = array_shift($args); + $request = new IXR_Request($method, $args); + $xml = $request->getXml(); + + $this->headers['Content-Type'] = 'text/xml'; + if(!$this->sendRequest($this->posturl, $xml, 'POST')) { + $this->xmlerror = new IXR_Error(-32300, 'transport error - ' . $this->error); + return false; + } + + // Check HTTP Response code + if($this->status < 200 || $this->status > 206) { + $this->xmlerror = new IXR_Error(-32300, 'transport error - HTTP status ' . $this->status); + return false; + } + + // Now parse what we've got back + $this->message = new IXR_Message($this->resp_body); + if(!$this->message->parse()) { + // XML error + $this->xmlerror = new IXR_Error(-32700, 'parse error. not well formed'); + return false; + } + + // Is the message a fault? + if($this->message->messageType == 'fault') { + $this->xmlerror = new IXR_Error($this->message->faultCode, $this->message->faultString); + return false; + } + + // Message must be OK + return true; + } + + /** + * @return mixed + */ + function getResponse() { + // methodResponses can only have one param - return that + return $this->message->params[0]; + } + + /** + * @return bool + */ + function isError() { + return (is_object($this->xmlerror)); + } + + /** + * @return int + */ + function getErrorCode() { + return $this->xmlerror->code; + } + + /** + * @return string + */ + function getErrorMessage() { + return $this->xmlerror->message; + } +} + +/** + * IXR_Error + * + * @package IXR + * @since 1.5 + */ +class IXR_Error { + var $code; + var $message; + + /** + * @param int $code + * @param string $message + */ + function __construct($code, $message) { + $this->code = $code; + $this->message = htmlspecialchars($message); + } + + /** + * @return string + */ + function getXml() { + $xml = << + + + + + faultCode + {$this->code} + + + faultString + {$this->message} + + + + + + +EOD; + return $xml; + } +} + +/** + * IXR_Date + * + * @package IXR + * @since 1.5 + */ +class IXR_Date { + + /** @var DateTime */ + protected $date; + + /** + * @param int|string $time + */ + public function __construct($time) { + // $time can be a PHP timestamp or an ISO one + if(is_numeric($time)) { + $this->parseTimestamp($time); + } else { + $this->parseIso($time); + } + } + + /** + * Parse unix timestamp + * + * @param int $timestamp + */ + protected function parseTimestamp($timestamp) { + $this->date = new DateTime('@' . $timestamp); + } + + /** + * Parses less or more complete iso dates and much more, if no timezone given assumes UTC + * + * @param string $iso + */ + protected function parseIso($iso) { + $this->date = new DateTime($iso, new DateTimeZone("UTC")); + } + + /** + * Returns date in ISO 8601 format + * + * @return string + */ + public function getIso() { + return $this->date->format(DateTime::ISO8601); + } + + /** + * Returns date in valid xml + * + * @return string + */ + public function getXml() { + return '' . $this->getIso() . ''; + } + + /** + * Returns Unix timestamp + * + * @return int + */ + function getTimestamp() { + return $this->date->getTimestamp(); + } +} + +/** + * IXR_Base64 + * + * @package IXR + * @since 1.5 + */ +class IXR_Base64 { + var $data; + + /** + * @param string $data + */ + function __construct($data) { + $this->data = $data; + } + + /** + * @return string + */ + function getXml() { + return '' . base64_encode($this->data) . ''; + } +} + +/** + * IXR_IntrospectionServer + * + * @package IXR + * @since 1.5 + */ +class IXR_IntrospectionServer extends IXR_Server { + /** @var array[] */ + var $signatures; + /** @var string[] */ + var $help; + + /** + * Constructor + */ + function __construct() { + $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' + ); + } + + /** + * @param string $method + * @param string $callback + * @param string[] $args + * @param string $help + */ + function addCallback($method, $callback, $args, $help) { + $this->callbacks[$method] = $callback; + $this->signatures[$method] = $args; + $this->help[$method] = $help; + } + + /** + * @param string $methodname + * @param array $args + * @return IXR_Error|mixed + */ + 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. Check only, if the minimum count of parameters is specified. More parameters are possible. + // This is a hack to allow optional parameters... + 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); + } + + /** + * @param string $method + * @return array|IXR_Error + */ + 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; + } + + /** + * @param string $method + * @return mixed + */ + function methodHelp($method) { + return $this->help[$method]; + } +} + +/** + * IXR_ClientMulticall + * + * @package IXR + * @since 1.5 + */ +class IXR_ClientMulticall extends IXR_Client { + + /** @var array[] */ + var $calls = array(); + + /** + * @param string $server + * @param string|bool $path + * @param int $port + */ + function __construct($server, $path = false, $port = 80) { + parent::__construct($server, $path, $port); + //$this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; + } + + /** + * Add a call + */ + function addCall() { + $args = func_get_args(); + $methodName = array_shift($args); + $struct = array( + 'methodName' => $methodName, + 'params' => $args + ); + $this->calls[] = $struct; + } + + /** + * @return bool + */ + function query() { + // Prepare multicall, then call the parent::query() method + return parent::query('system.multicall', $this->calls); + } +} + -- cgit 1.4.1-2-gfad0