diff --git a/_test/tests/inc/remoteapicore.test.php b/_test/tests/inc/remoteapicore.test.php
new file mode 100644
index 0000000000000000000000000000000000000000..a83a4eb26cc835f7204bf55e21b2b40aa80a7e24
--- /dev/null
+++ b/_test/tests/inc/remoteapicore.test.php
@@ -0,0 +1,424 @@
+<?php
+
+/**
+ * Class remoteapicore_test
+ */
+class remoteapicore_test extends DokuWikiTest {
+
+    protected $userinfo;
+    protected $oldAuthAcl;
+    /** @var  RemoteAPI */
+    protected $remote;
+
+    public function setUp() {
+        parent::setUp();
+        global $conf;
+        global $USERINFO;
+        global $AUTH_ACL;
+        global $auth;
+        $this->oldAuthAcl = $AUTH_ACL;
+        $this->userinfo = $USERINFO;
+        $auth = new DokuWiki_Auth_Plugin();
+
+        $conf['remote'] = 1;
+        $conf['remoteuser'] = '@user';
+        $conf['useacl'] = 0;
+
+        $this->remote = new RemoteAPI();
+    }
+
+    public function tearDown() {
+        global $USERINFO;
+        global $AUTH_ACL;
+
+        $USERINFO = $this->userinfo;
+        $AUTH_ACL = $this->oldAuthAcl;
+
+    }
+
+    public function test_core() {
+        $this->assertEquals(getVersion(), $this->remote->call('dokuwiki.getVersion'));
+//        $params = array('user', 'passwrd');
+//        $this->assertEquals(, $remoteApi->call('dokuwiki.login'));                   //TODO
+
+//        $this->assertEquals(, $remoteApi->call('dokuwiki.logoff'));                  //TODO
+
+        $file1 = wikiFN('wiki:dokuwiki');
+        $file2 = wikiFN('wiki:syntax');
+        $expected = array(
+            array(
+                'id' => 'wiki:dokuwiki',
+                'rev' => filemtime($file1),
+                'mtime' => filemtime($file1),
+                'size' => filesize($file1),
+                'hash' => md5(trim(rawWiki('wiki:dokuwiki')))
+            ),
+            array(
+                'id' => 'wiki:syntax',
+                'rev' => filemtime($file2),
+                'mtime' => filemtime($file2),
+                'size' => filesize($file2),
+                'hash' => md5(trim(rawWiki('wiki:syntax')))
+            )
+        );
+        $params = array(
+            'wiki:',
+            array(
+                'depth' => 0, // 0 for all
+                'hash' => 1,
+                'skipacl' => 1 // is ignored
+            )
+        );
+        $this->assertEquals($expected, $this->remote->call('dokuwiki.getPagelist', $params));
+
+        idx_addPage('wiki:syntax'); //full text search depends on index
+        $expected = array(
+            array(
+                'id' => 'wiki:syntax',
+                'score' => 1,
+                'rev' => filemtime($file2),
+                'mtime' => filemtime($file2),
+                'size' => filesize($file2),
+                'snippet' => ' a footnote)) by using double parentheses.
+
+===== <strong class="search_hit">Sectioning</strong> =====
+
+You can use up to five different levels of',
+                'title' => 'wiki:syntax'
+            )
+        );
+        $params = array('Sectioning');
+        $this->assertEquals($expected, $this->remote->call('dokuwiki.search', $params));
+
+        $timeexpect = time();
+        $timeactual = $this->remote->call('dokuwiki.getTime');
+        $this->assertTrue(($timeexpect <= $timeactual) && ($timeactual <= $timeexpect + 1));
+
+        $expected = array(
+            'locked' => array('wiki:dokuwiki', 'wiki:syntax', 'nonexisting'),
+            'lockfail' => array(),
+            'unlocked' => array(),
+            'unlockfail' => array(),
+        );
+        $params = array(
+            array(
+                'lock' => array('wiki:dokuwiki', 'wiki:syntax', 'nonexisting'),
+                'unlock' => array()
+            )
+        );
+        $this->assertEquals($expected, $this->remote->call('dokuwiki.setLocks', $params));
+
+        $expected = array(
+            'locked' => array(),
+            'lockfail' => array(),
+            'unlocked' => array('wiki:dokuwiki', 'wiki:syntax', 'nonexisting'),
+            'unlockfail' => array('nonexisting2'),
+        );
+        $params = array(
+            array(
+                'lock' => array(),
+                'unlock' => array('wiki:dokuwiki', 'wiki:syntax', 'nonexisting', 'nonexisting2')
+            )
+        );
+        $this->assertEquals($expected, $this->remote->call('dokuwiki.setLocks', $params));
+
+        global $conf;
+        $this->assertEquals($conf['title'], $this->remote->call('dokuwiki.getTitle'));
+
+        $file3 = wikiFN('nice_page');
+        $content = "====Title====\nText";
+        $params = array(
+            'nice_page',
+            $content,
+            array(
+                'minor' => false,
+                'sum' => 'Summary of nice text'
+            )
+        );
+        $this->assertEquals(true, $this->remote->call('wiki.putPage', $params));  //TODO check exceptions
+        $this->assertEquals($content, rawWiki('nice_page'));
+
+        $rev[1] = filemtime(wikiFN('nice_page')); //stored for later
+        sleep(1); // wait for new revision ID
+
+        $params = array('nice_page');
+        $this->assertEquals($content, $this->remote->call('wiki.getPage', $params));
+
+        $morecontent = "\nOther text.";
+        $secondcontent = $content . $morecontent;
+        $params_append = array(
+            'nice_page',
+            $morecontent,
+            array()
+        );
+        $this->assertEquals(true, $this->remote->call('dokuwiki.appendPage', $params_append));
+        $this->assertEquals($secondcontent, rawWiki('nice_page'));
+
+        $params = array('nice_page', '');
+        $this->assertEquals($secondcontent, $this->remote->call('wiki.getPageVersion', $params));
+        $params = array('nice_page', $rev[1]);
+        $this->assertEquals($content, $this->remote->call('wiki.getPageVersion', $params));
+        $params = array('nice_page', 1234);
+        $this->assertEquals('', $this->remote->call('wiki.getPageVersion', $params), 'Not existing revision');
+        $params = array('notexisting', 1234);
+        $this->assertEquals('', $this->remote->call('wiki.getPageVersion', $params), 'Not existing page');
+
+        $html1 = "\n<h3 class=\"sectionedit1\" id=\"title\">Title</h3>\n<div class=\"level3\">\n\n<p>\nText\n";
+        $html2 = "Other text.\n";
+        $html3 = "</p>\n\n</div>\n";
+        $params = array('nice_page');
+        $this->assertEquals($html1 . $html2 . $html3, $this->remote->call('wiki.getPageHTML', $params));
+
+        $params = array('nice_page', '');
+        $this->assertEquals($html1 . $html2 . $html3, $this->remote->call('wiki.getPageHTMLVersion', $params));
+        $params = array('nice_page', $rev[1]);
+        $this->assertEquals($html1 . $html3, $this->remote->call('wiki.getPageHTMLVersion', $params));
+        $params = array('nice_page', 1234);
+        $this->assertEquals('', $this->remote->call('wiki.getPageHTMLVersion', $params));
+
+        $expected = array(
+            array(
+                'id' => 'wiki:syntax',
+                'perms' => 8,
+                'size' => filesize($file2),
+                'lastModified' => filemtime($file2)
+            ),
+            array(
+                'id' => 'nice_page',
+                'perms' => 8,
+                'size' => filesize($file3),
+                'lastModified' => filemtime($file3)
+            )
+        );
+        $this->assertEquals($expected, $this->remote->call('wiki.getAllPages')); //only indexed pages
+
+        $params = array('wiki:syntax');
+        $this->assertEquals(ft_backlinks('wiki:syntax'), $this->remote->call('wiki.getBackLinks', $params));
+
+        $expected = array(
+            'name' => 'nice_page',
+            'lastModified' => filemtime($file3),
+            'author' => clientIP(),
+            'version' => filemtime($file3)
+        );
+        $params = array('nice_page');
+        $this->assertEquals($expected, $this->remote->call('wiki.getPageInfo', $params));
+
+        $expected = array(
+            'name' => 'nice_page',
+            'lastModified' => $rev[1],
+            'author' => clientIP(),
+            'version' => $rev[1]
+        );
+        $params = array('nice_page', $rev[1]);
+        $this->assertEquals($expected, $this->remote->call('wiki.getPageInfoVersion', $params));
+
+        $rev[2] = filemtime(wikiFN('nice_page'));
+        sleep(1); // wait for new revision ID
+        $this->remote->call('dokuwiki.appendPage', $params_append);
+        $rev[3] = filemtime(wikiFN('nice_page'));
+        sleep(1);
+        $this->remote->call('dokuwiki.appendPage', $params_append);
+        $rev[4] = filemtime(wikiFN('nice_page'));
+        sleep(1);
+        $this->remote->call('dokuwiki.appendPage', $params_append);
+        $rev[5] = filemtime(wikiFN('nice_page'));
+        sleep(1);
+        $this->remote->call('dokuwiki.appendPage', $params_append);
+        $rev[6] = filemtime(wikiFN('nice_page'));
+
+        $expected = array(
+            array(
+                'name' => 'nice_page',
+                'lastModified' => $rev[6],
+                'author' => '',
+                'version' => $rev[6],
+                'perms' => 8,
+                'size' => 78
+            )
+        );
+        $params = array(strtotime("-1 year"));
+        $this->assertEquals($expected, $this->remote->call('wiki.getRecentChanges', $params));
+
+        $params = array('nice_page', 0);
+        $versions = $this->remote->call('wiki.getPageVersions', $params);
+        $this->assertEquals($rev[6], $versions[0]['version']);
+        $this->assertEquals($rev[5], $versions[1]['version']);
+        $this->assertEquals($rev[1], $versions[5]['version']);
+        $this->assertEquals(6, count($this->remote->call('wiki.getPageVersions', $params)));
+
+        $params = array('nice_page', 1);
+        $versions = $this->remote->call('wiki.getPageVersions', $params);
+        $this->assertEquals($rev[5], $versions[0]['version']);
+        $this->assertEquals($rev[4], $versions[1]['version']);
+        $this->assertEquals(5, count($this->remote->call('wiki.getPageVersions', $params)));
+
+        $conf['recent'] = 3; //set number of page returned
+        $params = array('nice_page', 1);
+        $this->assertEquals(3, count($this->remote->call('wiki.getPageVersions', $params)));
+
+        $params = array('nice_page', $conf['recent']);
+        $versions = $this->remote->call('wiki.getPageVersions', $params);
+        $this->assertEquals($rev[3], $versions[0]['version']); //skips current,1st old,2nd old
+        $this->assertEquals(3, count($this->remote->call('wiki.getPageVersions', $params)));
+
+        $params = array('nice_page', 2 * $conf['recent']);
+        $this->assertEquals(0, count($this->remote->call('wiki.getPageVersions', $params)));
+
+        //remove page
+        $file3 = wikiFN('nice_page');
+        $content = "";
+        $params = array(
+            'nice_page',
+            $content,
+            array(
+                'minor' => false,
+            )
+        );
+        $this->assertEquals(true, $this->remote->call('wiki.putPage', $params));
+        $this->assertFalse(file_exists($file3));
+
+        $params = array('nice_page', 0);
+        $this->assertEquals(2, count($this->remote->call('wiki.getPageVersions', $params)));
+
+        $params = array('nice_page', 1);
+        $this->assertEquals(3, count($this->remote->call('wiki.getPageVersions', $params)));
+
+        $params = array('nice_page');
+        $this->assertEquals(AUTH_UPLOAD, $this->remote->call('wiki.aclCheck', $params));
+
+        global $conf;
+        global $AUTH_ACL, $USERINFO;
+        $conf['useacl'] = 1;
+        $_SERVER['REMOTE_USER'] = 'john';
+        $USERINFO['grps'] = array('user');
+        $AUTH_ACL = array(
+            '*                  @ALL           0',
+            '*                  @user          2', //edit
+        );
+
+        $params = array('nice_page');
+        $this->assertEquals(AUTH_EDIT, $this->remote->call('wiki.aclCheck', $params));
+
+        $this->assertEquals(DOKU_API_VERSION, $this->remote->call('dokuwiki.getXMLRPCAPIVersion'));
+
+        $this->assertEquals(2, $this->remote->call('wiki.getRPCVersionSupported'));
+    }
+
+    public function test_core2() {
+        $localdoku = array(
+            'type' => 'local',
+            'page' => 'DokuWiki',
+            'href' => DOKU_BASE . DOKU_SCRIPT . '?id=DokuWiki'
+        );
+        $expected = array(  //no local links
+            $localdoku,
+            array(
+                'type' => 'extern',
+                'page' => 'http://www.freelists.org',
+                'href' => 'http://www.freelists.org'
+            ),
+            array(
+                'type' => 'extern',
+                'page' => 'https://tools.ietf.org/html/rfc1855',
+                'href' => 'https://tools.ietf.org/html/rfc1855'
+            ),
+            array(
+                'type' => 'extern',
+                'page' => 'http://www.catb.org/~esr/faqs/smart-questions.html',
+                'href' => 'http://www.catb.org/~esr/faqs/smart-questions.html'
+            ),
+            $localdoku,
+            $localdoku
+        );
+        $params = array('mailinglist');
+        $this->assertEquals($expected, $this->remote->call('wiki.listLinks', $params));
+    }
+
+    public function test_coreattachments() {
+        global $conf;
+        global $AUTH_ACL, $USERINFO;
+
+        $filecontent = io_readFile(mediaFN('wiki:dokuwiki-128.png'), false);
+        $params = array('test:dokuwiki-128_2.png', $filecontent, array('ow' => false));
+        $this->assertEquals('test:dokuwiki-128_2.png', $this->remote->call('wiki.putAttachment', $params)); //prints a success div
+
+        $params = array('test:dokuwiki-128_2.png');
+        $this->assertEquals($filecontent, $this->remote->call('wiki.getAttachment', $params));
+        $rev = filemtime(mediaFN('test:dokuwiki-128_2.png'));
+
+        $expected = array(
+            'lastModified' => $rev,
+            'size' => 27895,
+        );
+        $params = array('test:dokuwiki-128_2.png');
+        $this->assertEquals($expected, $this->remote->call('wiki.getAttachmentInfo', $params));
+
+        $params = array(strtotime("-5 year"));
+        $expected = array(
+            array(
+                'name' => 'test:dokuwiki-128_2.png',
+                'lastModified' => $rev,
+                'author' => '',
+                'version' => $rev,
+                'perms' => 8,
+                'size' => 27895 //actual size, not size change
+            )
+        );
+        $this->assertEquals($expected, $this->remote->call('wiki.getRecentMediaChanges', $params));
+
+        sleep(1);
+        $conf['useacl'] = 1;
+        $_SERVER['REMOTE_USER'] = 'john';
+        $USERINFO['grps'] = array('user');
+        $AUTH_ACL = array(
+            '*                  @ALL           0',
+            '*                  @user          16',
+        );
+
+        $params = array('test:dokuwiki-128_2.png');
+        $this->assertEquals(0, $this->remote->call('wiki.deleteAttachment', $params));
+
+        $rev2 = filemtime($conf['media_changelog']);
+        $expected = array(
+            'lastModified' => $rev2,
+            'size' => 0,
+        );
+        $params = array('test:dokuwiki-128_2.png');
+        $this->assertEquals($expected, $this->remote->call('wiki.getAttachmentInfo', $params));
+
+        $expected = array(
+            'lastModified' => 0,
+            'size' => 0,
+        );
+        $params = array('test:nonexisting.png');
+        $this->assertEquals($expected, $this->remote->call('wiki.getAttachmentInfo', $params));
+
+        $media1 = mediaFN('wiki:dokuwiki-128.png');
+        $expected = array(
+            array(
+                'id' => 'wiki:dokuwiki-128.png',
+                'file' => 'dokuwiki-128.png',
+                'size' => filesize($media1),
+                'mtime' => filemtime($media1),
+                'writable' => 1,
+                'isimg' => 1,
+                'hash' => md5(io_readFile($media1, false)),
+                'perms' => 16,
+                'lastModified' => filemtime($media1)
+            )
+        );
+        $params = array(
+            'wiki:',
+            array(
+                'depth' => 0, // 0 for all
+                'hash' => 1,
+                'skipacl' => 1, // is ignored
+                'showmsg' => true, //useless??
+                'pattern' => '/128/' //filter
+            )
+        );
+        $this->assertEquals($expected, $this->remote->call('wiki.getAttachments', $params));
+    }
+
+}
diff --git a/_test/tests/inc/remoteapicore_aclcheck.test.php b/_test/tests/inc/remoteapicore_aclcheck.test.php
new file mode 100644
index 0000000000000000000000000000000000000000..25aff331f5ed26f451495dba543b9aa96dd84baa
--- /dev/null
+++ b/_test/tests/inc/remoteapicore_aclcheck.test.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * Class remoteapicore_test
+ */
+class remoteapicore_aclcheck_test extends DokuWikiTest {
+
+    protected $userinfo;
+    protected $oldAuthAcl;
+    /** @var  RemoteAPI */
+    protected $remote;
+
+    protected $pluginsEnabled = array('auth_plugin_authplain');
+
+    protected function reloadUsers() {
+        global $auth;
+
+        /* auth caches data loaded from file, but recreated object forces reload */
+        $auth = new auth_plugin_authplain();
+    }
+
+    public function setUp() {
+        global $config_cascade;
+        global $conf;
+        global $USERINFO;
+        global $AUTH_ACL;
+
+        parent::setUp();
+
+        $name = $config_cascade['plainauth.users']['default'];
+        copy($name, $name . ".orig");
+        $this->reloadUsers();
+
+        $this->oldAuthAcl = $AUTH_ACL;
+        $this->userinfo = $USERINFO;
+
+        $conf['remote'] = 1;
+        $conf['remoteuser'] = '@user';
+        $conf['useacl'] = 0;
+
+        $this->remote = new RemoteAPI();
+
+    }
+
+    public function tearDown() {
+        global $USERINFO;
+        global $AUTH_ACL;
+        global $config_cascade;
+
+        parent::tearDown();
+
+        $USERINFO = $this->userinfo;
+        $AUTH_ACL = $this->oldAuthAcl;
+
+        $name = $config_cascade['plainauth.users']['default'];
+        copy($name . ".orig", $name);
+    }
+
+    public function test_checkacl() {
+        global $conf;
+        global $AUTH_ACL, $USERINFO;
+        /** @var auth_plugin_authplain $auth */
+        global $auth;
+
+        $conf['useacl'] = 1;
+        $_SERVER['REMOTE_USER'] = 'john';
+        $USERINFO['grps'] = array('user');
+        $AUTH_ACL = array(
+            '*                  @ALL           0', //none
+            '*                  @user          2', //edit
+            '*                  @more          4', //create
+            'nice_page          user2          8'  //upload
+        );
+
+        $params = array('nice_page');
+        $this->assertEquals(AUTH_EDIT, $this->remote->call('wiki.aclCheck', $params));
+
+        $auth->createUser("user1", "54321", "a User", "you@example.com");
+        $auth->createUser("user2", "543210", "You", "he@example.com");
+        $auth->createUser("mwuser", "12345", "Wiki User", "me@example.com", array('more')); //not in default group
+
+        $params = array(
+            'nice_page',
+            'user1'
+        );
+        $this->assertEquals(AUTH_EDIT, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'nice_page',
+            'mwuser' // member of group 'more'
+        );
+        $this->assertEquals(AUTH_CREATE, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'nice_page',
+            'mwuser',
+            array() //groups not retrieved
+        );
+        $this->assertEquals(AUTH_NONE, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'nice_page',
+            'notexistinguser',
+            array('more')
+        );
+        $this->assertEquals(AUTH_CREATE, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'nice_page',
+            'user2'
+        );
+        $this->assertEquals(AUTH_UPLOAD, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'nice_page',
+            'user2',
+            array() //groups not retrieved
+        );
+        $this->assertEquals(AUTH_UPLOAD, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'unknown_page',
+            'user2'
+        );
+        $this->assertEquals(AUTH_EDIT, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'unknown_page',
+            'user2',
+            array() //groups not retrieved
+        );
+        $this->assertEquals(AUTH_NONE, $this->remote->call('wiki.aclCheck', $params));
+
+        $params = array(
+            'nice_page',
+            'testuser' // superuser set via conf
+        );
+        $this->assertEquals(AUTH_ADMIN, $this->remote->call('wiki.aclCheck', $params));
+    }
+
+}
diff --git a/inc/RemoteAPICore.php b/inc/RemoteAPICore.php
index c0e6869f1dbdcf4b8f450498debe73f249d0fc86..407e63185b3ec519abcf00a6f5e23023701ca3d2 100644
--- a/inc/RemoteAPICore.php
+++ b/inc/RemoteAPICore.php
@@ -3,12 +3,19 @@
 /**
  * Increased whenever the API is changed
  */
-define('DOKU_API_VERSION', 9);
+define('DOKU_API_VERSION', 10);
 
+/**
+ * Provides the core methods for the remote API.
+ * The methods are ordered in 'wiki.<method>' and 'dokuwiki.<method>' namespaces
+ */
 class RemoteAPICore {
 
     private $api;
 
+    /**
+     * @param RemoteAPI $api
+     */
     public function __construct(RemoteAPI $api) {
         $this->api = $api;
     }
@@ -18,7 +25,7 @@ class RemoteAPICore {
      *
      * @return array
      */
-    function __getRemoteInfo() {
+    public function __getRemoteInfo() {
         return array(
             'dokuwiki.getVersion' => array(
                 'args' => array(),
@@ -97,7 +104,7 @@ class RemoteAPICore {
             ), 'wiki.getPageInfo' => array(
                 'args' => array('string'),
                 'return' => 'array',
-                'doc' => 'Returns a struct with info about the page.',
+                'doc' => 'Returns a struct with info about the page, latest version.',
                 'name' => 'pageInfo'
             ), 'wiki.getPageInfoVersion' => array(
                 'args' => array('string', 'int'),
@@ -126,9 +133,9 @@ class RemoteAPICore {
                 'return' => 'array',
                 'Returns a struct about all recent media changes since given timestamp.'
             ), 'wiki.aclCheck' => array(
-                'args' => array('string'),
+                'args' => array('string', 'string', 'array'),
                 'return' => 'int',
-                'doc' => 'Returns the permissions of a given wiki page.'
+                'doc' => 'Returns the permissions of a given wiki page. By default, for current user/groups'
             ), 'wiki.putAttachment' => array(
                 'args' => array('string', 'file', 'array'),
                 'return' => 'array',
@@ -166,14 +173,14 @@ class RemoteAPICore {
     /**
      * @return string
      */
-    function getVersion() {
+    public function getVersion() {
         return getVersion();
     }
 
     /**
      * @return int unix timestamp
      */
-    function getTime() {
+    public function getTime() {
         return time();
     }
 
@@ -185,7 +192,7 @@ class RemoteAPICore {
      * @return string page text.
      * @throws RemoteAccessDeniedException if no permission for page
      */
-    function rawPage($id,$rev=''){
+    public function rawPage($id,$rev=''){
         $id = $this->resolvePageId($id);
         if(auth_quickaclcheck($id) < AUTH_READ){
             throw new RemoteAccessDeniedException('You are not allowed to read this file', 111);
@@ -208,7 +215,7 @@ class RemoteAPICore {
      * @throws RemoteAccessDeniedException no permission for media
      * @throws RemoteException not exist
      */
-    function getAttachment($id){
+    public function getAttachment($id){
         $id = cleanID($id);
         if (auth_quickaclcheck(getNS($id).':*') < AUTH_READ) {
             throw new RemoteAccessDeniedException('You are not allowed to read this file', 211);
@@ -231,7 +238,7 @@ class RemoteAPICore {
      * @param string $id page id
      * @return array
      */
-    function getAttachmentInfo($id){
+    public function getAttachmentInfo($id){
         $id = cleanID($id);
         $info = array(
             'lastModified' => $this->api->toDate(0),
@@ -239,9 +246,18 @@ class RemoteAPICore {
         );
 
         $file = mediaFN($id);
-        if ((auth_quickaclcheck(getNS($id).':*') >= AUTH_READ) && file_exists($file)){
-            $info['lastModified'] = $this->api->toDate(filemtime($file));
-            $info['size'] = filesize($file);
+        if(auth_quickaclcheck(getNS($id) . ':*') >= AUTH_READ) {
+            if(file_exists($file)) {
+                $info['lastModified'] = $this->api->toDate(filemtime($file));
+                $info['size'] = filesize($file);
+            } else {
+                //Is it deleted media with changelog?
+                $medialog = new MediaChangeLog($id);
+                $revisions = $medialog->getRevisions(0, 1);
+                if(!empty($revisions)) {
+                    $info['lastModified'] = $this->api->toDate($revisions[0]);
+                }
+            }
         }
 
         return $info;
@@ -255,7 +271,7 @@ class RemoteAPICore {
      * @return null|string html
      * @throws RemoteAccessDeniedException no access to page
      */
-    function htmlPage($id,$rev=''){
+    public function htmlPage($id,$rev=''){
         $id = $this->resolvePageId($id);
         if(auth_quickaclcheck($id) < AUTH_READ){
             throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
@@ -268,7 +284,7 @@ class RemoteAPICore {
      *
      * @return array
      */
-    function listPages(){
+    public function listPages(){
         $list  = array();
         $pages = idx_get_indexer()->getPages();
         $pages = array_filter(array_filter($pages,'isVisiblePage'),'page_exists');
@@ -298,7 +314,7 @@ class RemoteAPICore {
      *    $opts['hash']    do md5 sum of content?
      * @return array
      */
-    function readNamespace($ns,$opts){
+    public function readNamespace($ns,$opts){
         global $conf;
 
         if(!is_array($opts)) $opts=array();
@@ -317,7 +333,7 @@ class RemoteAPICore {
      * @param string $query
      * @return array
      */
-    function search($query){
+    public function search($query){
         $regex = array();
         $data  = ft_pageSearch($query,$regex);
         $pages = array();
@@ -352,7 +368,7 @@ class RemoteAPICore {
      *
      * @return string
      */
-    function getTitle(){
+    public function getTitle(){
         global $conf;
         return $conf['title'];
     }
@@ -375,7 +391,7 @@ class RemoteAPICore {
      * @return array
      * @throws RemoteAccessDeniedException no access to the media files
      */
-    function listAttachments($ns, $options = array()) {
+    public function listAttachments($ns, $options = array()) {
         global $conf;
 
         $ns = cleanID($ns);
@@ -422,7 +438,7 @@ class RemoteAPICore {
      * @throws RemoteAccessDeniedException no access for page
      * @throws RemoteException page not exist
      */
-    function pageInfo($id,$rev=''){
+    public function pageInfo($id,$rev=''){
         $id = $this->resolvePageId($id);
         if(auth_quickaclcheck($id) < AUTH_READ){
             throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
@@ -458,7 +474,7 @@ class RemoteAPICore {
      * @throws RemoteAccessDeniedException no write access for page
      * @throws RemoteException no id, empty new page or locked
      */
-    function putPage($id, $text, $params) {
+    public function putPage($id, $text, $params) {
         global $TEXT;
         global $lang;
 
@@ -519,7 +535,7 @@ class RemoteAPICore {
      * @param array $params such as summary,minor
      * @return bool|string
      */
-    function appendPage($id, $text, $params) {
+    public function appendPage($id, $text, $params) {
         $currentpage = $this->rawPage($id);
         if (!is_string($currentpage)) {
             return $currentpage;
@@ -538,7 +554,7 @@ class RemoteAPICore {
      * @return false|string
      * @throws RemoteException
      */
-    function putAttachment($id, $file, $params) {
+    public function putAttachment($id, $file, $params) {
         $id = cleanID($id);
         $auth = auth_quickaclcheck(getNS($id).':*');
 
@@ -572,7 +588,7 @@ class RemoteAPICore {
      * @throws RemoteAccessDeniedException no permissions
      * @throws RemoteException file in use or not deleted
      */
-    function deleteAttachment($id){
+    public function deleteAttachment($id){
         $id = cleanID($id);
         $auth = auth_quickaclcheck(getNS($id).':*');
         $res = media_delete($id, $auth);
@@ -588,14 +604,31 @@ class RemoteAPICore {
     }
 
     /**
-     * Returns the permissions of a given wiki page
+     * Returns the permissions of a given wiki page for the current user or another user
      *
      * @param string $id page id
+     * @param string|null $user username
+     * @param array|null $groups array of groups
      * @return int permission level
      */
-    function aclCheck($id) {
+    public function aclCheck($id, $user = null, $groups = null) {
+        /** @var DokuWiki_Auth_Plugin $auth */
+        global $auth;
+
         $id = $this->resolvePageId($id);
-        return auth_quickaclcheck($id);
+        if($user === null) {
+            return auth_quickaclcheck($id);
+        } else {
+            if($groups === null) {
+                $userinfo = $auth->getUserData($user);
+                if($userinfo === false) {
+                    $groups = array();
+                } else {
+                    $groups = $userinfo['grps'];
+                }
+            }
+            return auth_aclcheck($id, $user, $groups);
+        }
     }
 
     /**
@@ -607,7 +640,7 @@ class RemoteAPICore {
      * @return array
      * @throws RemoteAccessDeniedException  no read access for page
      */
-    function listLinks($id) {
+    public function listLinks($id) {
         $id = $this->resolvePageId($id);
         if(auth_quickaclcheck($id) < AUTH_READ){
             throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
@@ -618,7 +651,6 @@ class RemoteAPICore {
         $ins   = p_cached_instructions(wikiFN($id));
 
         // instantiate new Renderer - needed for interwiki links
-        include(DOKU_INC.'inc/parser/xhtml.php');
         $Renderer = new Doku_Renderer_xhtml();
         $Renderer->interwiki = getInterwiki();
 
@@ -661,7 +693,7 @@ class RemoteAPICore {
      * @return array
      * @throws RemoteException no valid timestamp
      */
-    function getRecentChanges($timestamp) {
+    public function getRecentChanges($timestamp) {
         if(strlen($timestamp) != 10) {
             throw new RemoteException('The provided value is not a valid timestamp', 311);
         }
@@ -699,7 +731,7 @@ class RemoteAPICore {
      * @return array
      * @throws RemoteException no valid timestamp
      */
-    function getRecentMediaChanges($timestamp) {
+    public function getRecentMediaChanges($timestamp) {
         if(strlen($timestamp) != 10)
             throw new RemoteException('The provided value is not a valid timestamp', 311);
 
@@ -728,16 +760,18 @@ class RemoteAPICore {
 
     /**
      * Returns a list of available revisions of a given wiki page
+     * Number of returned pages is set by $conf['recent']
+     * However not accessible pages are skipped, so less than $conf['recent'] could be returned
      *
      * @author Michael Klier <chi@chimeric.de>
      *
      * @param string $id    page id
-     * @param int    $first skip the first n changelog lines
+     * @param int    $first skip the first n changelog lines (0 = from current(if exists), 1 = from 1st old rev, 2 = from 2nd old rev, etc)
      * @return array
      * @throws RemoteAccessDeniedException no read access for page
      * @throws RemoteException empty id
      */
-    function pageVersions($id, $first) {
+    public function pageVersions($id, $first) {
         $id = $this->resolvePageId($id);
         if(auth_quickaclcheck($id) < AUTH_READ) {
             throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
@@ -750,25 +784,19 @@ class RemoteAPICore {
             throw new RemoteException('Empty page ID', 131);
         }
 
+        $first = (int) $first;
+        $first_rev = $first - 1;
+        $first_rev = $first_rev < 0 ? 0 : $first_rev;
         $pagelog = new PageChangeLog($id);
-        $revisions = $pagelog->getRevisions($first, $conf['recent']+1);
+        $revisions = $pagelog->getRevisions($first_rev, $conf['recent']);
 
-        if(count($revisions)==0 && $first!=0) {
-            $first=0;
-            $revisions = $pagelog->getRevisions($first, $conf['recent']+1);
-        }
-
-        if(count($revisions)>0 && $first==0) {
+        if($first == 0) {
             array_unshift($revisions, '');  // include current revision
             if ( count($revisions) > $conf['recent'] ){
                 array_pop($revisions);          // remove extra log entry
             }
         }
 
-        if(count($revisions) > $conf['recent']) {
-            array_pop($revisions); // remove extra log entry
-        }
-
         if(!empty($revisions)) {
             foreach($revisions as $rev) {
                 $file = wikiFN($id,$rev);
@@ -800,7 +828,7 @@ class RemoteAPICore {
     /**
      * The version of Wiki RPC API supported
      */
-    function wiki_RPCVersion(){
+    public function wiki_RPCVersion(){
         return 2;
     }
 
@@ -817,7 +845,7 @@ class RemoteAPICore {
      * @param array[] $set list pages with array('lock' => array, 'unlock' => array)
      * @return array
      */
-    function setLocks($set){
+    public function setLocks($set){
         $locked     = array();
         $lockfail   = array();
         $unlocked   = array();
@@ -855,7 +883,7 @@ class RemoteAPICore {
      *
      * @return int
      */
-    function getAPIVersion(){
+    public function getAPIVersion(){
         return DOKU_API_VERSION;
     }
 
@@ -866,7 +894,7 @@ class RemoteAPICore {
      * @param string $pass
      * @return int
      */
-    function login($user,$pass){
+    public function login($user,$pass){
         global $conf;
         /** @var DokuWiki_Auth_Plugin $auth */
         global $auth;
@@ -896,12 +924,12 @@ class RemoteAPICore {
      *
      * @return int
      */
-    function logoff(){
+    public function logoff(){
         global $conf;
         global $auth;
         if(!$conf['useacl']) return 0;
         if(!$auth) return 0;
-        
+
         auth_logoff();
 
         return 1;
diff --git a/inc/remote.php b/inc/remote.php
index 771d12d252901f6f1de5206e0715db7746e4eba0..2d2e327c826f63eaccf6faa2455f0c9e7ea77313 100644
--- a/inc/remote.php
+++ b/inc/remote.php
@@ -226,6 +226,7 @@ class RemoteAPI {
      * 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;
diff --git a/lib/exe/xmlrpc.php b/lib/exe/xmlrpc.php
index 9ba157b8fdffde7e6741b7bdb4b5f2d668219721..6421c4a48a8465c2975559015b14cabdd7f9fb15 100644
--- a/lib/exe/xmlrpc.php
+++ b/lib/exe/xmlrpc.php
@@ -11,12 +11,12 @@ if(!$conf['remote']) die('XML-RPC server not enabled.');
  * XMLRPC functions.
  */
 class dokuwiki_xmlrpc_server extends IXR_Server {
-    var $remote;
+    protected $remote;
 
     /**
      * Constructor. Register methods and run Server
      */
-    function __construct(){
+    public function __construct(){
         $this->remote = new RemoteAPI();
         $this->remote->setDateTransformation(array($this, 'toDate'));
         $this->remote->setFileTransformation(array($this, 'toFile'));
@@ -28,7 +28,7 @@ class dokuwiki_xmlrpc_server extends IXR_Server {
      * @param array $args
      * @return IXR_Error|mixed
      */
-    function call($methodname, $args){
+    public function call($methodname, $args){
         try {
             $result = $this->remote->call($methodname, $args);
             return $result;
@@ -49,7 +49,7 @@ class dokuwiki_xmlrpc_server extends IXR_Server {
      * @param string|int $data iso date(yyyy[-]mm[-]dd[ hh:mm[:ss]]) or timestamp
      * @return IXR_Date
      */
-    function toDate($data) {
+    public function toDate($data) {
         return new IXR_Date($data);
     }
 
@@ -57,7 +57,7 @@ class dokuwiki_xmlrpc_server extends IXR_Server {
      * @param string $data
      * @return IXR_Base64
      */
-    function toFile($data) {
+    public function toFile($data) {
         return new IXR_Base64($data);
     }
 }
diff --git a/lib/plugins/acl/admin.php b/lib/plugins/acl/admin.php
index f4baec9941aeca5aaad3ec70eb4ac10c05cc5403..6edc6c6215578f1557a37817527b81e9ea525bb2 100644
--- a/lib/plugins/acl/admin.php
+++ b/lib/plugins/acl/admin.php
@@ -649,7 +649,6 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin {
         echo '</div></form>'.NL;
     }
 
-
     /**
      * Returns the permission which were set for exactly the given user/group
      * and page/namespace. Returns null if no exact match is available
diff --git a/lib/plugins/acl/remote.php b/lib/plugins/acl/remote.php
index 031686f959ab1c568b3a597e6060c02eb7fe04cc..3771d475d44fab17deeeef43cc34e58907df2467 100644
--- a/lib/plugins/acl/remote.php
+++ b/lib/plugins/acl/remote.php
@@ -12,7 +12,12 @@ class remote_plugin_acl extends DokuWiki_Remote_Plugin {
      */
     public function _getMethods() {
         return array(
-            'addAcl' => array(
+            'listAcls' => array(
+                'args' => array(),
+                'return' => 'Array of ACLs {scope, user, permission}',
+                'name' => 'listAcl',
+                'doc' => 'Get the list of all ACLs',
+            ),'addAcl' => array(
                 'args' => array('string','string','int'),
                 'return' => 'int',
                 'name' => 'addAcl',
@@ -26,6 +31,22 @@ class remote_plugin_acl extends DokuWiki_Remote_Plugin {
         );
     }
 
+    /**
+     * List all ACL config entries
+     *
+     * @throws RemoteAccessDeniedException
+     * @return dictionary {Scope: ACL}, where ACL = dictionnary {user/group: permissions_int}
+     */
+    public function listAcls(){
+        if(!auth_isadmin()) {
+         throw new RemoteAccessDeniedException('You are not allowed to access ACLs, superuser permission is required', 114);
+        }
+        /** @var admin_plugin_acl $apa */
+        $apa = plugin_load('admin', 'acl');
+        $apa->_init_acl_config();
+        return $apa->acl;
+    }
+
     /**
      * Add a new entry to ACL config
      *
diff --git a/lib/plugins/authplain/_test/escaping.test.php b/lib/plugins/authplain/_test/escaping.test.php
index 7139aa99b0b2bcc684588ef8c0c39f4a518a0539..a38940e1a7da22597887ba1c5d3138df1a395be0 100644
--- a/lib/plugins/authplain/_test/escaping.test.php
+++ b/lib/plugins/authplain/_test/escaping.test.php
@@ -103,9 +103,11 @@ class helper_plugin_authplain_escaping_test extends DokuWikiTest {
             $this->assertEquals($escaped, $result[2]);
         }
     }
-    
 }
 
+/**
+ * Class auth_plugin_authplainharness
+ */
 class auth_plugin_authplainharness extends auth_plugin_authplain {
 
     /**
@@ -115,12 +117,16 @@ class auth_plugin_authplainharness extends auth_plugin_authplain {
         $this->_pregsplit_safe = $bool;
     }
 
+    /**
+     * @return bool|mixed
+     */
     public function getPregsplit_safe(){
         return $this->_pregsplit_safe;
     }
 
     /**
      * @param string $line
+     * @return array
      */
     public function splitUserData($line){
         return $this->_splitUserData($line);