summaryrefslogtreecommitdiff
path: root/inc/remote.php
diff options
context:
space:
mode:
authorAndreas Gohr <andi@splitbrain.org>2012-03-23 10:02:01 +0100
committerAndreas Gohr <andi@splitbrain.org>2012-03-23 10:02:01 +0100
commita2b7fdb8e14f02aff9ab4b1b5646f640123ec0c7 (patch)
tree676ba2c0959bb1d99c34074a9ac4043862d91871 /inc/remote.php
parent7518cd81d69733d64ee5c80a8e5df4fcaa33246d (diff)
parentb967e5fc8fd93226eba7926c13b73b93878f182b (diff)
downloadrpg-a2b7fdb8e14f02aff9ab4b1b5646f640123ec0c7.tar.gz
rpg-a2b7fdb8e14f02aff9ab4b1b5646f640123ec0c7.tar.bz2
Merge branch 'master' of https://github.com/dom-mel/dokuwiki into pull-request-87
* 'master' of https://github.com/dom-mel/dokuwiki: (38 commits) removed requires, changed conf check in xmlrpc.php removed require_once for autoloaded fulltext.php updated comment added dokuwiki.getXMLRPCAPIVersion and wiki.getRPCVersionSupported added RPC_CALL_ADD event. replaced $HTTP_RAW_POST_DATA with http_get_raw_post_data function changed error code for unauthorized method calls. typo fixes moved plugin and core method calls to seperate function corrected comment added getapi methods to remote plugin removed unused class fixed testcase refactored RemoteAccessDenied to RemoteAccessDeniedException adjusted test cases delegate file and date transformation to remote library treat null as empty array added missing getTime added missing getVersion set login as public method ...
Diffstat (limited to 'inc/remote.php')
-rw-r--r--inc/remote.php253
1 files changed, 253 insertions, 0 deletions
diff --git a/inc/remote.php b/inc/remote.php
new file mode 100644
index 000000000..2ef28afd2
--- /dev/null
+++ b/inc/remote.php
@@ -0,0 +1,253 @@
+<?php
+
+if (!defined('DOKU_INC')) die();
+
+class RemoteException extends Exception {}
+class RemoteAccessDeniedException extends RemoteException {}
+
+/**
+ * This class provides information about remote access to the wiki.
+ *
+ * == Types of methods ==
+ * There are two types of remote methods. The first is the core methods.
+ * These are always available and provided by dokuwiki.
+ * The other is plugin methods. These are provided by remote plugins.
+ *
+ * == Information structure ==
+ * The information about methods will be given in an array with the following structure:
+ * array(
+ * 'method.remoteName' => array(
+ * 'args' => array(
+ * 'type eg. string|int|...|date|file',
+ * )
+ * 'name' => 'method name in class',
+ * 'return' => 'type',
+ * 'public' => 1/0 - method bypass default group check (used by login)
+ * ['doc' = 'method documentation'],
+ * )
+ * )
+ *
+ * plugin names are formed the following:
+ * core methods begin by a 'dokuwiki' or 'wiki' followed by a . and the method name itself.
+ * i.e.: dokuwiki.version or wiki.getPage
+ *
+ * plugin methods are formed like 'plugin.<plugin name>.<method name>'.
+ * i.e.: plugin.clock.getTime or plugin.clock_gmt.getTime
+ *
+ * @throws RemoteException
+ */
+class RemoteAPI {
+
+ /**
+ * @var RemoteAPICore
+ */
+ private $coreMethods = null;
+
+ /**
+ * @var array remote methods provided by dokuwiki plugins - will be filled lazy via
+ * {@see RemoteAPI#getPluginMethods}
+ */
+ private $pluginMethods = null;
+
+ /**
+ * @var array contains custom calls to the api. Plugins can use the XML_CALL_REGISTER event.
+ * The data inside is 'custom.call.something' => array('plugin name', 'remote method name')
+ *
+ * The remote method name is the same as in the remote name returned by _getMethods().
+ */
+ private $pluginCustomCalls = null;
+
+ private $dateTransformation;
+ private $fileTransformation;
+
+ public function __construct() {
+ $this->dateTransformation = array($this, 'dummyTransformation');
+ $this->fileTransformation = array($this, 'dummyTransformation');
+ }
+
+ /**
+ * Get all available methods with remote access.
+ *
+ * @return array with information to all available methods
+ */
+ public function getMethods() {
+ return array_merge($this->getCoreMethods(), $this->getPluginMethods());
+ }
+
+ /**
+ * call a method via remote api.
+ *
+ * @param string $method name of the method to call.
+ * @param array $args arguments to pass to the given method
+ * @return mixed result of method call, must be a primitive type.
+ */
+ public function call($method, $args = array()) {
+ if ($args === null) {
+ $args = array();
+ }
+ list($type, $pluginName, $call) = explode('.', $method, 3);
+ if ($type === 'plugin') {
+ return $this->callPlugin($pluginName, $method, $args);
+ }
+ if ($this->coreMethodExist($method)) {
+ return $this->callCoreMethod($method, $args);
+ }
+ return $this->callCustomCallPlugin($method, $args);
+ }
+
+ private function coreMethodExist($name) {
+ $coreMethods = $this->getCoreMethods();
+ return array_key_exists($name, $coreMethods);
+ }
+
+ private function callCustomCallPlugin($method, $args) {
+ $customCalls = $this->getCustomCallPlugins();
+ if (!array_key_exists($method, $customCalls)) {
+ throw new RemoteException('Method does not exist', -32603);
+ }
+ $customCall = $customCalls[$method];
+ return $this->callPlugin($customCall[0], $customCall[1], $args);
+ }
+
+ private function getCustomCallPlugins() {
+ if ($this->pluginCustomCalls === null) {
+ $data = array();
+ trigger_event('RPC_CALL_ADD', $data);
+ $this->pluginCustomCalls = $data;
+ }
+ return $this->pluginCustomCalls;
+ }
+
+ private function callPlugin($pluginName, $method, $args) {
+ $plugin = plugin_load('remote', $pluginName);
+ $methods = $this->getPluginMethods();
+ if (!$plugin) {
+ throw new RemoteException('Method does not exist', -32603);
+ }
+ $this->checkAccess($methods[$method]);
+ $name = $this->getMethodName($methods, $method);
+ return call_user_func_array(array($plugin, $name), $args);
+ }
+
+ private function callCoreMethod($method, $args) {
+ $coreMethods = $this->getCoreMethods();
+ $this->checkAccess($coreMethods[$method]);
+ if (!isset($coreMethods[$method])) {
+ throw new RemoteException('Method does not exist', -32603);
+ }
+ $this->checkArgumentLength($coreMethods[$method], $args);
+ return call_user_func_array(array($this->coreMethods, $this->getMethodName($coreMethods, $method)), $args);
+ }
+
+ private function checkAccess($methodMeta) {
+ if (!isset($methodMeta['public'])) {
+ $this->forceAccess();
+ } else{
+ if ($methodMeta['public'] == '0') {
+ $this->forceAccess();
+ }
+ }
+ }
+
+ private function checkArgumentLength($method, $args) {
+ if (count($method['args']) < count($args)) {
+ throw new RemoteException('Method does not exist - wrong parameter count.', -32603);
+ }
+ }
+
+ private function getMethodName($methodMeta, $method) {
+ if (isset($methodMeta[$method]['name'])) {
+ return $methodMeta[$method]['name'];
+ }
+ $method = explode('.', $method);
+ return $method[count($method)-1];
+ }
+
+ /**
+ * @return bool true if the current user has access to remote api.
+ */
+ public function hasAccess() {
+ global $conf;
+ global $USERINFO;
+ if (!$conf['remote']) {
+ return false;
+ }
+ if(!$conf['useacl']) {
+ return true;
+ }
+ if(trim($conf['remoteuser']) == '') {
+ return true;
+ }
+
+ return auth_isMember($conf['remoteuser'], $_SERVER['REMOTE_USER'], (array) $USERINFO['grps']);
+ }
+
+ /**
+ * @throws RemoteException On denied access.
+ * @return void
+ */
+ public function forceAccess() {
+ if (!$this->hasAccess()) {
+ throw new RemoteAccessDeniedException('server error. not authorized to call method', -32604);
+ }
+ }
+
+ /**
+ * @return array all plugin methods.
+ */
+ public function getPluginMethods() {
+ if ($this->pluginMethods === null) {
+ $this->pluginMethods = array();
+ $plugins = plugin_list('remote');
+
+ foreach ($plugins as $pluginName) {
+ $plugin = plugin_load('remote', $pluginName);
+ if (!is_subclass_of($plugin, 'DokuWiki_Remote_Plugin')) {
+ throw new RemoteException("Plugin $pluginName does not implement DokuWiki_Remote_Plugin");
+ }
+
+ $methods = $plugin->_getMethods();
+ foreach ($methods as $method => $meta) {
+ $this->pluginMethods["plugin.$pluginName.$method"] = $meta;
+ }
+ }
+ }
+ return $this->pluginMethods;
+ }
+
+ /**
+ * @param RemoteAPICore $apiCore this parameter is used for testing. Here you can pass a non-default RemoteAPICore
+ * instance. (for mocking)
+ * @return array all core methods.
+ */
+ public function getCoreMethods($apiCore = null) {
+ if ($this->coreMethods === null) {
+ if ($apiCore === null) {
+ $this->coreMethods = new RemoteAPICore($this);
+ } else {
+ $this->coreMethods = $apiCore;
+ }
+ }
+ return $this->coreMethods->__getRemoteInfo();
+ }
+
+ public function toFile($data) {
+ return call_user_func($this->fileTransformation, $data);
+ }
+
+ public function toDate($data) {
+ return call_user_func($this->dateTransformation, $data);
+ }
+
+ public function dummyTransformation($data) {
+ return $data;
+ }
+
+ public function setDateTransformation($dateTransformation) {
+ $this->dateTransformation = $dateTransformation;
+ }
+
+ public function setFileTransformation($fileTransformation) {
+ $this->fileTransformation = $fileTransformation;
+ }
+}