[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 if (!defined('DOKU_INC')) die(); 4 5 class RemoteException extends Exception {} 6 class RemoteAccessDeniedException extends RemoteException {} 7 8 /** 9 * This class provides information about remote access to the wiki. 10 * 11 * == Types of methods == 12 * There are two types of remote methods. The first is the core methods. 13 * These are always available and provided by dokuwiki. 14 * The other is plugin methods. These are provided by remote plugins. 15 * 16 * == Information structure == 17 * The information about methods will be given in an array with the following structure: 18 * array( 19 * 'method.remoteName' => array( 20 * 'args' => array( 21 * 'type eg. string|int|...|date|file', 22 * ) 23 * 'name' => 'method name in class', 24 * 'return' => 'type', 25 * 'public' => 1/0 - method bypass default group check (used by login) 26 * ['doc' = 'method documentation'], 27 * ) 28 * ) 29 * 30 * plugin names are formed the following: 31 * core methods begin by a 'dokuwiki' or 'wiki' followed by a . and the method name itself. 32 * i.e.: dokuwiki.version or wiki.getPage 33 * 34 * plugin methods are formed like 'plugin.<plugin name>.<method name>'. 35 * i.e.: plugin.clock.getTime or plugin.clock_gmt.getTime 36 * 37 * @throws RemoteException 38 */ 39 class RemoteAPI { 40 41 /** 42 * @var RemoteAPICore 43 */ 44 private $coreMethods = null; 45 46 /** 47 * @var array remote methods provided by dokuwiki plugins - will be filled lazy via 48 * {@see RemoteAPI#getPluginMethods} 49 */ 50 private $pluginMethods = null; 51 52 /** 53 * @var array contains custom calls to the api. Plugins can use the XML_CALL_REGISTER event. 54 * The data inside is 'custom.call.something' => array('plugin name', 'remote method name') 55 * 56 * The remote method name is the same as in the remote name returned by _getMethods(). 57 */ 58 private $pluginCustomCalls = null; 59 60 private $dateTransformation; 61 private $fileTransformation; 62 63 /** 64 * constructor 65 */ 66 public function __construct() { 67 $this->dateTransformation = array($this, 'dummyTransformation'); 68 $this->fileTransformation = array($this, 'dummyTransformation'); 69 } 70 71 /** 72 * Get all available methods with remote access. 73 * 74 * @return array with information to all available methods 75 */ 76 public function getMethods() { 77 return array_merge($this->getCoreMethods(), $this->getPluginMethods()); 78 } 79 80 /** 81 * Call a method via remote api. 82 * 83 * @param string $method name of the method to call. 84 * @param array $args arguments to pass to the given method 85 * @return mixed result of method call, must be a primitive type. 86 */ 87 public function call($method, $args = array()) { 88 if ($args === null) { 89 $args = array(); 90 } 91 list($type, $pluginName, /* $call */) = explode('.', $method, 3); 92 if ($type === 'plugin') { 93 return $this->callPlugin($pluginName, $method, $args); 94 } 95 if ($this->coreMethodExist($method)) { 96 return $this->callCoreMethod($method, $args); 97 } 98 return $this->callCustomCallPlugin($method, $args); 99 } 100 101 /** 102 * Check existance of core methods 103 * 104 * @param string $name name of the method 105 * @return bool if method exists 106 */ 107 private function coreMethodExist($name) { 108 $coreMethods = $this->getCoreMethods(); 109 return array_key_exists($name, $coreMethods); 110 } 111 112 /** 113 * Try to call custom methods provided by plugins 114 * 115 * @param string $method name of method 116 * @param array $args 117 * @return mixed 118 * @throws RemoteException if method not exists 119 */ 120 private function callCustomCallPlugin($method, $args) { 121 $customCalls = $this->getCustomCallPlugins(); 122 if (!array_key_exists($method, $customCalls)) { 123 throw new RemoteException('Method does not exist', -32603); 124 } 125 $customCall = $customCalls[$method]; 126 return $this->callPlugin($customCall[0], $customCall[1], $args); 127 } 128 129 /** 130 * Returns plugin calls that are registered via RPC_CALL_ADD action 131 * 132 * @return array with pairs of custom plugin calls 133 * @triggers RPC_CALL_ADD 134 */ 135 private function getCustomCallPlugins() { 136 if ($this->pluginCustomCalls === null) { 137 $data = array(); 138 trigger_event('RPC_CALL_ADD', $data); 139 $this->pluginCustomCalls = $data; 140 } 141 return $this->pluginCustomCalls; 142 } 143 144 /** 145 * Call a plugin method 146 * 147 * @param string $pluginName 148 * @param string $method method name 149 * @param array $args 150 * @return mixed return of custom method 151 * @throws RemoteException 152 */ 153 private function callPlugin($pluginName, $method, $args) { 154 $plugin = plugin_load('remote', $pluginName); 155 $methods = $this->getPluginMethods(); 156 if (!$plugin) { 157 throw new RemoteException('Method does not exist', -32603); 158 } 159 $this->checkAccess($methods[$method]); 160 $name = $this->getMethodName($methods, $method); 161 return call_user_func_array(array($plugin, $name), $args); 162 } 163 164 /** 165 * Call a core method 166 * 167 * @param string $method name of method 168 * @param array $args 169 * @return mixed 170 * @throws RemoteException if method not exist 171 */ 172 private function callCoreMethod($method, $args) { 173 $coreMethods = $this->getCoreMethods(); 174 $this->checkAccess($coreMethods[$method]); 175 if (!isset($coreMethods[$method])) { 176 throw new RemoteException('Method does not exist', -32603); 177 } 178 $this->checkArgumentLength($coreMethods[$method], $args); 179 return call_user_func_array(array($this->coreMethods, $this->getMethodName($coreMethods, $method)), $args); 180 } 181 182 /** 183 * Check if access should be checked 184 * 185 * @param array $methodMeta data about the method 186 */ 187 private function checkAccess($methodMeta) { 188 if (!isset($methodMeta['public'])) { 189 $this->forceAccess(); 190 } else{ 191 if ($methodMeta['public'] == '0') { 192 $this->forceAccess(); 193 } 194 } 195 } 196 197 /** 198 * Check the number of parameters 199 * 200 * @param array $methodMeta data about the method 201 * @param array $args 202 * @throws RemoteException if wrong parameter count 203 */ 204 private function checkArgumentLength($methodMeta, $args) { 205 if (count($methodMeta['args']) < count($args)) { 206 throw new RemoteException('Method does not exist - wrong parameter count.', -32603); 207 } 208 } 209 210 /** 211 * Determine the name of the real method 212 * 213 * @param array $methodMeta list of data of the methods 214 * @param string $method name of method 215 * @return string 216 */ 217 private function getMethodName($methodMeta, $method) { 218 if (isset($methodMeta[$method]['name'])) { 219 return $methodMeta[$method]['name']; 220 } 221 $method = explode('.', $method); 222 return $method[count($method)-1]; 223 } 224 225 /** 226 * Perform access check for current user 227 * 228 * @return bool true if the current user has access to remote api. 229 * @throws RemoteAccessDeniedException If remote access disabled 230 */ 231 public function hasAccess() { 232 global $conf; 233 global $USERINFO; 234 /** @var Input $INPUT */ 235 global $INPUT; 236 237 if (!$conf['remote']) { 238 throw new RemoteAccessDeniedException('server error. RPC server not enabled.',-32604); //should not be here,just throw 239 } 240 if(trim($conf['remoteuser']) == '!!not set!!') { 241 return false; 242 } 243 if(!$conf['useacl']) { 244 return true; 245 } 246 if(trim($conf['remoteuser']) == '') { 247 return true; 248 } 249 250 return auth_isMember($conf['remoteuser'], $INPUT->server->str('REMOTE_USER'), (array) $USERINFO['grps']); 251 } 252 253 /** 254 * Requests access 255 * 256 * @return void 257 * @throws RemoteException On denied access. 258 */ 259 public function forceAccess() { 260 if (!$this->hasAccess()) { 261 throw new RemoteAccessDeniedException('server error. not authorized to call method', -32604); 262 } 263 } 264 265 /** 266 * Collects all the methods of the enabled Remote Plugins 267 * 268 * @return array all plugin methods. 269 * @throws RemoteException if not implemented 270 */ 271 public function getPluginMethods() { 272 if ($this->pluginMethods === null) { 273 $this->pluginMethods = array(); 274 $plugins = plugin_list('remote'); 275 276 foreach ($plugins as $pluginName) { 277 /** @var DokuWiki_Remote_Plugin $plugin */ 278 $plugin = plugin_load('remote', $pluginName); 279 if (!is_subclass_of($plugin, 'DokuWiki_Remote_Plugin')) { 280 throw new RemoteException("Plugin $pluginName does not implement DokuWiki_Remote_Plugin"); 281 } 282 283 $methods = $plugin->_getMethods(); 284 foreach ($methods as $method => $meta) { 285 $this->pluginMethods["plugin.$pluginName.$method"] = $meta; 286 } 287 } 288 } 289 return $this->pluginMethods; 290 } 291 292 /** 293 * Collects all the core methods 294 * 295 * @param RemoteAPICore $apiCore this parameter is used for testing. Here you can pass a non-default RemoteAPICore 296 * instance. (for mocking) 297 * @return array all core methods. 298 */ 299 public function getCoreMethods($apiCore = null) { 300 if ($this->coreMethods === null) { 301 if ($apiCore === null) { 302 $this->coreMethods = new RemoteAPICore($this); 303 } else { 304 $this->coreMethods = $apiCore; 305 } 306 } 307 return $this->coreMethods->__getRemoteInfo(); 308 } 309 310 /** 311 * Transform file to xml 312 * 313 * @param mixed $data 314 * @return mixed 315 */ 316 public function toFile($data) { 317 return call_user_func($this->fileTransformation, $data); 318 } 319 320 /** 321 * Transform date to xml 322 * 323 * @param mixed $data 324 * @return mixed 325 */ 326 public function toDate($data) { 327 return call_user_func($this->dateTransformation, $data); 328 } 329 330 /** 331 * A simple transformation 332 * 333 * @param mixed $data 334 * @return mixed 335 */ 336 public function dummyTransformation($data) { 337 return $data; 338 } 339 340 /** 341 * Set the transformer function 342 * 343 * @param callback $dateTransformation 344 */ 345 public function setDateTransformation($dateTransformation) { 346 $this->dateTransformation = $dateTransformation; 347 } 348 349 /** 350 * Set the transformer function 351 * 352 * @param callback $fileTransformation 353 */ 354 public function setFileTransformation($fileTransformation) { 355 $this->fileTransformation = $fileTransformation; 356 } 357 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body