1<?php 2/** 3 * Description of IJR_Server 4 * 5 * @author Andreas Gohr <andi@splitbrain.org> 6 * @author Magnus Wolf <mwolf2706@googlemail.com> 7 */ 8class IJR_Server { 9 10 var $data; 11 var $callbacks = array(); 12 var $message; 13 var $capabilities; 14 var $method; 15 var $id; 16 17 18 function IJR_Server($callbacks = false, $data = false) 19 { 20 $this->setCapabilities(); 21 if ($callbacks) { 22 $this->callbacks = $callbacks; 23 } 24 $this->setCallbacks(); 25 $this->serve($data); 26 } 27 28 29 private function verifyData($data) 30 { 31 if($data == NULL){ 32 $this->error(-32700, 'decode error. no correct json string'); 33 } 34 35 if(!$data['method']){ 36 $this->error(-32600, 'server error. invalid json-rpc. not conforming to spec. Request must be a method'); 37 } 38 39 if(!$data['jsonrpc'] || $data['jsonrpc']!='2.0'){ 40 $this->error(-32600, 'server error. invalid json-rpc. wrong jsonrpc-spec 2.0 is available'); 41 } 42 } 43 44 function serve($data = false) 45 { 46 if (!$data) { 47 global $HTTP_RAW_POST_DATA; 48 if (!$HTTP_RAW_POST_DATA) { 49 die('JSON-RPC server accepts POST requests only.'); 50 } 51 $data = $HTTP_RAW_POST_DATA; 52 } 53 54 $data = json_decode($data, true); 55 $this->verifyData($data); 56 $this->id = $data['id']; 57 $parameter = array(); 58 59 foreach($data['params'] as $param){ 60 foreach($param as $key => $value){ 61 $parameter[] = $value; 62 } 63 } 64 65 $res = $this->call($data['method']['methodName'], $parameter); 66 if (is_a($res, 'IJR_Error')) { 67 $this->error($res); 68 } 69 70 // Send it 71 $result['jsonrpc']='2.0'; 72 $result['result']=$res; 73 $result['error']=''; 74 $result['id']=$this->id; 75 $result = json_encode($result); 76 $this->output($result); 77 } 78 79 80 private function callClassMethod($method, $args) 81 { 82 $method = substr($method, 5); 83 if (!method_exists($this, $method)) { 84 return new IJR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); 85 } 86 return call_user_func_array(array(&$this,$method),$args); 87 } 88 89 90 private function callPlugin($pluginname, $callback, $method, $args) 91 { 92 require_once(DOKU_INC.'inc/pluginutils.php'); 93 list($pluginname, $callback) = explode(':', substr($method, 7), 2); 94 if(!plugin_isdisabled($pluginname)) 95 { 96 $plugin = plugin_load('action', $pluginname); 97 return call_user_func_array(array($plugin, $callback), $args); 98 } 99 else 100 { 101 return new IJR_Error(-99999, 'server error'); 102 } 103 } 104 105 106 private function callFunction($method, $args) 107 { 108 if (!function_exists($method)) 109 { 110 return new IJR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); 111 } 112 return call_user_func_array($method,$args); 113 } 114 115 116 protected function call($methodname, $args) 117 { 118 if (!$this->hasMethod($methodname)) 119 { 120 return new IJR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); 121 } 122 $method = $this->callbacks[$methodname]; 123 // Perform the callback and send the response 124# Adjusted for DokuWiki to use call_user_func_array 125 // args need to be an array 126 $args = (array) $args; 127 if (substr($method, 0, 5) == 'this:') 128 { 129 $result = $this->callClassMethod($method, $args); 130 } 131 elseif (substr($method, 0, 7) == 'plugin:') 132 { 133 return $this->callPlugin($pluginname, $callback, $method, $args); 134 } 135 else 136 { 137 $result = $this->callFunction($method, $args); 138 } 139 return $result; 140 } 141 142 143 function error($error, $message = false) 144 { 145 if ($message && !is_object($error)) { 146 $error = new IJR_Error($error, $message); 147 } 148 $result['jsonrpc']='2.0'; 149 $result['result']=''; 150 $result['error']=$error->getJson(); 151 $result['id']=$this->id; 152 $result = json_encode($result); 153 $this->output($result); 154 } 155 156 157 function output($json) 158 { 159 $length = strlen($json); 160 header('Connection: close'); 161 header('Content-Length: '.$length); 162 header('Content-Type: application/json'); 163 header('Date: '.date('r')); 164 echo $json; 165 exit; 166 } 167 168 169 function hasMethod($method) { 170 return in_array($method, array_keys($this->callbacks)); 171 } 172 173 174 function setCapabilities() { 175 // Initialises capabilities array 176 $this->capabilities = array( 177 'xmlrpc' => array( 178 'specUrl' => 'http://www.xmlrpc.com/spec', 179 'specVersion' => 1 180 ), 181 'faults_interop' => array( 182 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', 183 'specVersion' => 20010516 184 ), 185 'system.multicall' => array( 186 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', 187 'specVersion' => 1 188 ), 189 ); 190 } 191 192 193 function getCapabilities() { 194 return $this->capabilities; 195 } 196 197 198 function setCallbacks() { 199 $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; 200 $this->callbacks['system.listMethods'] = 'this:listMethods'; 201 $this->callbacks['system.multicall'] = 'this:multiCall'; 202 } 203 204 205 function listMethods() { 206 // Returns a list of methods - uses array_reverse to ensure user defined 207 // methods are listed before server defined methods 208 return array_reverse(array_keys($this->callbacks)); 209 } 210 211 212 function multiCall($methodcalls) { 213 // See http://www.xmlrpc.com/discuss/msgReader$1208 214 $return = array(); 215 foreach ($methodcalls as $call) { 216 $method = $call['methodName']; 217 $params = $call['params']; 218 if ($method == 'system.multicall') { 219 $result = new IJR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); 220 } else { 221 $result = $this->call($method, $params); 222 } 223 if (is_a($result, 'IJR_Error')) { 224 $return[] = array( 225 'faultCode' => $result->code, 226 'faultString' => $result->message 227 ); 228 } else { 229 $return[] = array($result); 230 } 231 } 232 return $return; 233 } 234} 235?> 236