1*7f8f2456SAndreas Gohr<?php 2*7f8f2456SAndreas Gohr 3*7f8f2456SAndreas Gohrnamespace IXR\Server; 4*7f8f2456SAndreas Gohr 5*7f8f2456SAndreas Gohr 6*7f8f2456SAndreas Gohruse IXR\DataType\Value; 7*7f8f2456SAndreas Gohruse IXR\Exception\ServerException; 8*7f8f2456SAndreas Gohruse IXR\Message\Error; 9*7f8f2456SAndreas Gohruse IXR\Message\Message; 10*7f8f2456SAndreas Gohr 11*7f8f2456SAndreas Gohrclass Server 12*7f8f2456SAndreas Gohr{ 13*7f8f2456SAndreas Gohr protected $callbacks = []; 14*7f8f2456SAndreas Gohr protected $message; 15*7f8f2456SAndreas Gohr protected $capabilities; 16*7f8f2456SAndreas Gohr 17*7f8f2456SAndreas Gohr public function __construct($callbacks = false, $data = false, $wait = false) 18*7f8f2456SAndreas Gohr { 19*7f8f2456SAndreas Gohr $this->setCapabilities(); 20*7f8f2456SAndreas Gohr if ($callbacks) { 21*7f8f2456SAndreas Gohr $this->callbacks = $callbacks; 22*7f8f2456SAndreas Gohr } 23*7f8f2456SAndreas Gohr $this->setCallbacks(); 24*7f8f2456SAndreas Gohr if (!$wait) { 25*7f8f2456SAndreas Gohr $this->serve($data); 26*7f8f2456SAndreas Gohr } 27*7f8f2456SAndreas Gohr } 28*7f8f2456SAndreas Gohr 29*7f8f2456SAndreas Gohr public function serve($data = false) 30*7f8f2456SAndreas Gohr { 31*7f8f2456SAndreas Gohr if (!$data) { 32*7f8f2456SAndreas Gohr if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') { 33*7f8f2456SAndreas Gohr header('Content-Type: text/plain'); // merged from WP #9093 34*7f8f2456SAndreas Gohr throw new ServerException('XML-RPC server accepts POST requests only.'); 35*7f8f2456SAndreas Gohr } 36*7f8f2456SAndreas Gohr 37*7f8f2456SAndreas Gohr $data = file_get_contents('php://input'); 38*7f8f2456SAndreas Gohr } 39*7f8f2456SAndreas Gohr $this->message = new Message($data); 40*7f8f2456SAndreas Gohr if (!$this->message->parse()) { 41*7f8f2456SAndreas Gohr $this->error(-32700, 'parse error. not well formed'); 42*7f8f2456SAndreas Gohr } 43*7f8f2456SAndreas Gohr if ($this->message->messageType != 'methodCall') { 44*7f8f2456SAndreas Gohr $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); 45*7f8f2456SAndreas Gohr } 46*7f8f2456SAndreas Gohr $result = $this->call($this->message->methodName, $this->message->params); 47*7f8f2456SAndreas Gohr 48*7f8f2456SAndreas Gohr // Is the result an error? 49*7f8f2456SAndreas Gohr if ($result instanceof Error) { 50*7f8f2456SAndreas Gohr $this->error($result); 51*7f8f2456SAndreas Gohr } 52*7f8f2456SAndreas Gohr 53*7f8f2456SAndreas Gohr // Encode the result 54*7f8f2456SAndreas Gohr $r = new Value($result); 55*7f8f2456SAndreas Gohr $resultxml = $r->getXml(); 56*7f8f2456SAndreas Gohr 57*7f8f2456SAndreas Gohr // Create the XML 58*7f8f2456SAndreas Gohr $xml = <<<EOD 59*7f8f2456SAndreas Gohr<methodResponse> 60*7f8f2456SAndreas Gohr <params> 61*7f8f2456SAndreas Gohr <param> 62*7f8f2456SAndreas Gohr <value> 63*7f8f2456SAndreas Gohr $resultxml 64*7f8f2456SAndreas Gohr </value> 65*7f8f2456SAndreas Gohr </param> 66*7f8f2456SAndreas Gohr </params> 67*7f8f2456SAndreas Gohr</methodResponse> 68*7f8f2456SAndreas Gohr 69*7f8f2456SAndreas GohrEOD; 70*7f8f2456SAndreas Gohr // Send it 71*7f8f2456SAndreas Gohr $this->output($xml); 72*7f8f2456SAndreas Gohr } 73*7f8f2456SAndreas Gohr 74*7f8f2456SAndreas Gohr protected function call($methodname, $args) 75*7f8f2456SAndreas Gohr { 76*7f8f2456SAndreas Gohr if (!$this->hasMethod($methodname)) { 77*7f8f2456SAndreas Gohr return new Error(-32601, 'server error. requested method ' . $methodname . ' does not exist.'); 78*7f8f2456SAndreas Gohr } 79*7f8f2456SAndreas Gohr $method = $this->callbacks[$methodname]; 80*7f8f2456SAndreas Gohr // Perform the callback and send the response 81*7f8f2456SAndreas Gohr 82*7f8f2456SAndreas Gohr if (is_array($args) && count($args) == 1) { 83*7f8f2456SAndreas Gohr // If only one parameter just send that instead of the whole array 84*7f8f2456SAndreas Gohr $args = $args[0]; 85*7f8f2456SAndreas Gohr } 86*7f8f2456SAndreas Gohr 87*7f8f2456SAndreas Gohr try { 88*7f8f2456SAndreas Gohr // Are we dealing with a function or a method? 89*7f8f2456SAndreas Gohr if (is_string($method) && substr($method, 0, 5) === 'this:') { 90*7f8f2456SAndreas Gohr // It's a class method - check it exists 91*7f8f2456SAndreas Gohr $method = substr($method, 5); 92*7f8f2456SAndreas Gohr 93*7f8f2456SAndreas Gohr return $this->$method($args); 94*7f8f2456SAndreas Gohr } 95*7f8f2456SAndreas Gohr 96*7f8f2456SAndreas Gohr return call_user_func($method, $args); 97*7f8f2456SAndreas Gohr } catch (\BadFunctionCallException $exception) { 98*7f8f2456SAndreas Gohr return new Error(-32601, "server error. requested callable '{$method}' does not exist."); 99*7f8f2456SAndreas Gohr } 100*7f8f2456SAndreas Gohr 101*7f8f2456SAndreas Gohr } 102*7f8f2456SAndreas Gohr 103*7f8f2456SAndreas Gohr public function error($error, $message = false) 104*7f8f2456SAndreas Gohr { 105*7f8f2456SAndreas Gohr // Accepts either an error object or an error code and message 106*7f8f2456SAndreas Gohr if ($message && !is_object($error)) { 107*7f8f2456SAndreas Gohr $error = new Error($error, $message); 108*7f8f2456SAndreas Gohr } 109*7f8f2456SAndreas Gohr $this->output($error->getXml()); 110*7f8f2456SAndreas Gohr } 111*7f8f2456SAndreas Gohr 112*7f8f2456SAndreas Gohr public function output($xml) 113*7f8f2456SAndreas Gohr { 114*7f8f2456SAndreas Gohr $xml = '<?xml version="1.0"?>' . "\n" . $xml; 115*7f8f2456SAndreas Gohr $length = strlen($xml); 116*7f8f2456SAndreas Gohr header('Connection: close'); 117*7f8f2456SAndreas Gohr header('Content-Length: ' . $length); 118*7f8f2456SAndreas Gohr header('Content-Type: text/xml'); 119*7f8f2456SAndreas Gohr header('Date: ' . date('r')); 120*7f8f2456SAndreas Gohr echo $xml; 121*7f8f2456SAndreas Gohr exit; 122*7f8f2456SAndreas Gohr } 123*7f8f2456SAndreas Gohr 124*7f8f2456SAndreas Gohr protected function hasMethod($method) 125*7f8f2456SAndreas Gohr { 126*7f8f2456SAndreas Gohr return in_array($method, array_keys($this->callbacks)); 127*7f8f2456SAndreas Gohr } 128*7f8f2456SAndreas Gohr 129*7f8f2456SAndreas Gohr protected function setCapabilities() 130*7f8f2456SAndreas Gohr { 131*7f8f2456SAndreas Gohr // Initialises capabilities array 132*7f8f2456SAndreas Gohr $this->capabilities = [ 133*7f8f2456SAndreas Gohr 'xmlrpc' => [ 134*7f8f2456SAndreas Gohr 'specUrl' => 'http://www.xmlrpc.com/spec', 135*7f8f2456SAndreas Gohr 'specVersion' => 1 136*7f8f2456SAndreas Gohr ], 137*7f8f2456SAndreas Gohr 'faults_interop' => [ 138*7f8f2456SAndreas Gohr 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', 139*7f8f2456SAndreas Gohr 'specVersion' => 20010516 140*7f8f2456SAndreas Gohr ], 141*7f8f2456SAndreas Gohr 'system.multicall' => [ 142*7f8f2456SAndreas Gohr 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', 143*7f8f2456SAndreas Gohr 'specVersion' => 1 144*7f8f2456SAndreas Gohr ], 145*7f8f2456SAndreas Gohr ]; 146*7f8f2456SAndreas Gohr } 147*7f8f2456SAndreas Gohr 148*7f8f2456SAndreas Gohr public function getCapabilities($args) 149*7f8f2456SAndreas Gohr { 150*7f8f2456SAndreas Gohr return $this->capabilities; 151*7f8f2456SAndreas Gohr } 152*7f8f2456SAndreas Gohr 153*7f8f2456SAndreas Gohr public function setCallbacks() 154*7f8f2456SAndreas Gohr { 155*7f8f2456SAndreas Gohr $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; 156*7f8f2456SAndreas Gohr $this->callbacks['system.listMethods'] = 'this:listMethods'; 157*7f8f2456SAndreas Gohr $this->callbacks['system.multicall'] = 'this:multiCall'; 158*7f8f2456SAndreas Gohr } 159*7f8f2456SAndreas Gohr 160*7f8f2456SAndreas Gohr public function listMethods($args) 161*7f8f2456SAndreas Gohr { 162*7f8f2456SAndreas Gohr // Returns a list of methods - uses array_reverse to ensure user defined 163*7f8f2456SAndreas Gohr // methods are listed before server defined methods 164*7f8f2456SAndreas Gohr return array_reverse(array_keys($this->callbacks)); 165*7f8f2456SAndreas Gohr } 166*7f8f2456SAndreas Gohr 167*7f8f2456SAndreas Gohr public function multiCall($methodcalls) 168*7f8f2456SAndreas Gohr { 169*7f8f2456SAndreas Gohr // See http://www.xmlrpc.com/discuss/msgReader$1208 170*7f8f2456SAndreas Gohr $return = []; 171*7f8f2456SAndreas Gohr foreach ($methodcalls as $call) { 172*7f8f2456SAndreas Gohr $method = $call['methodName']; 173*7f8f2456SAndreas Gohr $params = $call['params']; 174*7f8f2456SAndreas Gohr if ($method == 'system.multicall') { 175*7f8f2456SAndreas Gohr $result = new Error(-32600, 'Recursive calls to system.multicall are forbidden'); 176*7f8f2456SAndreas Gohr } else { 177*7f8f2456SAndreas Gohr $result = $this->call($method, $params); 178*7f8f2456SAndreas Gohr } 179*7f8f2456SAndreas Gohr if ($result instanceof Error) { 180*7f8f2456SAndreas Gohr $return[] = [ 181*7f8f2456SAndreas Gohr 'faultCode' => $result->code, 182*7f8f2456SAndreas Gohr 'faultString' => $result->message 183*7f8f2456SAndreas Gohr ]; 184*7f8f2456SAndreas Gohr } else { 185*7f8f2456SAndreas Gohr $return[] = [$result]; 186*7f8f2456SAndreas Gohr } 187*7f8f2456SAndreas Gohr } 188*7f8f2456SAndreas Gohr return $return; 189*7f8f2456SAndreas Gohr } 190*7f8f2456SAndreas Gohr} 191