about summary refs log tree commit diff stats
path: root/wiki/inc/remote.php
diff options
context:
space:
mode:
authorahriman <ahriman@falte.red>2018-12-03 19:22:25 -0500
committerahriman <ahriman@falte.red>2018-12-03 19:22:25 -0500
commit0ae8cbf5c0b1a198b963490985b7738392ebcb97 (patch)
treeb2c77ae72c6b717e2b97492065196ac5ffb2d9e2 /wiki/inc/remote.php
parentf57f6cc5a2d159f90168d292437dc4bd8cd7f934 (diff)
downloadsite-0ae8cbf5c0b1a198b963490985b7738392ebcb97.tar.gz
installed dokuwiki, added to navbar, updated news
Diffstat (limited to 'wiki/inc/remote.php')
-rw-r--r--wiki/inc/remote.php357
1 files changed, 357 insertions, 0 deletions
diff --git a/wiki/inc/remote.php b/wiki/inc/remote.php
new file mode 100644
index 0000000..2d2e327
--- /dev/null
+++ b/wiki/inc/remote.php
@@ -0,0 +1,357 @@
+<?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;
+
+    /**
+     * constructor
+     */
+    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);
+    }
+
+    /**
+     * Check existance of core methods
+     *
+     * @param string $name name of the method
+     * @return bool if method exists
+     */
+    private function coreMethodExist($name) {
+        $coreMethods = $this->getCoreMethods();
+        return array_key_exists($name, $coreMethods);
+    }
+
+    /**
+     * Try to call custom methods provided by plugins
+     *
+     * @param string $method name of method
+     * @param array  $args
+     * @return mixed
+     * @throws RemoteException if method not exists
+     */
+    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);
+    }
+
+    /**
+     * Returns plugin calls that are registered via RPC_CALL_ADD action
+     *
+     * @return array with pairs of custom plugin calls
+     * @triggers RPC_CALL_ADD
+     */
+    private function getCustomCallPlugins() {
+        if ($this->pluginCustomCalls === null) {
+            $data = array();
+            trigger_event('RPC_CALL_ADD', $data);
+            $this->pluginCustomCalls = $data;
+        }
+        return $this->pluginCustomCalls;
+    }
+
+    /**
+     * Call a plugin method
+     *
+     * @param string $pluginName
+     * @param string $method method name
+     * @param array  $args
+     * @return mixed return of custom method
+     * @throws RemoteException
+     */
+    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);
+    }
+
+    /**
+     * Call a core method
+     *
+     * @param string $method name of method
+     * @param array  $args
+     * @return mixed
+     * @throws RemoteException if method not exist
+     */
+    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);
+    }
+
+    /**
+     * Check if access should be checked
+     *
+     * @param array $methodMeta data about the method
+     */
+    private function checkAccess($methodMeta) {
+        if (!isset($methodMeta['public'])) {
+            $this->forceAccess();
+        } else{
+            if ($methodMeta['public'] == '0') {
+                $this->forceAccess();
+            }
+        }
+    }
+
+    /**
+     * Check the number of parameters
+     *
+     * @param array $methodMeta data about the method
+     * @param array $args
+     * @throws RemoteException if wrong parameter count
+     */
+    private function checkArgumentLength($methodMeta, $args) {
+        if (count($methodMeta['args']) < count($args)) {
+            throw new RemoteException('Method does not exist - wrong parameter count.', -32603);
+        }
+    }
+
+    /**
+     * Determine the name of the real method
+     *
+     * @param array $methodMeta list of data of the methods
+     * @param string $method name of method
+     * @return string
+     */
+    private function getMethodName($methodMeta, $method) {
+        if (isset($methodMeta[$method]['name'])) {
+            return $methodMeta[$method]['name'];
+        }
+        $method = explode('.', $method);
+        return $method[count($method)-1];
+    }
+
+    /**
+     * Perform access check for current user
+     *
+     * @return bool true if the current user has access to remote api.
+     * @throws RemoteAccessDeniedException If remote access disabled
+     */
+    public function hasAccess() {
+        global $conf;
+        global $USERINFO;
+        /** @var Input $INPUT */
+        global $INPUT;
+
+        if (!$conf['remote']) {
+            throw new RemoteAccessDeniedException('server error. RPC server not enabled.',-32604); //should not be here,just throw
+        }
+        if(trim($conf['remoteuser']) == '!!not set!!') {
+            return false;
+        }
+        if(!$conf['useacl']) {
+            return true;
+        }
+        if(trim($conf['remoteuser']) == '') {
+            return true;
+        }
+
+        return auth_isMember($conf['remoteuser'], $INPUT->server->str('REMOTE_USER'), (array) $USERINFO['grps']);
+    }
+
+    /**
+     * Requests access
+     *
+     * @return void
+     * @throws RemoteException On denied access.
+     */
+    public function forceAccess() {
+        if (!$this->hasAccess()) {
+            throw new RemoteAccessDeniedException('server error. not authorized to call method', -32604);
+        }
+    }
+
+    /**
+     * Collects all the methods of the enabled Remote Plugins
+     *
+     * @return array all plugin methods.
+     * @throws RemoteException if not implemented
+     */
+    public function getPluginMethods() {
+        if ($this->pluginMethods === null) {
+            $this->pluginMethods = array();
+            $plugins = plugin_list('remote');
+
+            foreach ($plugins as $pluginName) {
+                /** @var DokuWiki_Remote_Plugin $plugin */
+                $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;
+    }
+
+    /**
+     * Collects all the core methods
+     *
+     * @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();
+    }
+
+    /**
+     * Transform file to xml
+     *
+     * @param mixed $data
+     * @return mixed
+     */
+    public function toFile($data) {
+        return call_user_func($this->fileTransformation, $data);
+    }
+
+    /**
+     * Transform date to xml
+     *
+     * @param mixed $data
+     * @return mixed
+     */
+    public function toDate($data) {
+        return call_user_func($this->dateTransformation, $data);
+    }
+
+    /**
+     * A simple transformation
+     *
+     * @param mixed $data
+     * @return mixed
+     */
+    public function dummyTransformation($data) {
+        return $data;
+    }
+
+    /**
+     * Set the transformer function
+     *
+     * @param callback $dateTransformation
+     */
+    public function setDateTransformation($dateTransformation) {
+        $this->dateTransformation = $dateTransformation;
+    }
+
+    /**
+     * Set the transformer function
+     *
+     * @param callback $fileTransformation
+     */
+    public function setFileTransformation($fileTransformation) {
+        $this->fileTransformation = $fileTransformation;
+    }
+}