xref: /dokuwiki/lib/exe/xmlrpc.php (revision 4f616b5f20590d5f3a592bd53f2fc932968940d8)
1<?php
2if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../');
3
4// fix when '< ?xml' isn't on the very first line
5if(isset($HTTP_RAW_POST_DATA)) $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);
6
7/**
8 * Increased whenever the API is changed
9 */
10define('DOKU_XMLRPC_API_VERSION',5);
11
12require_once(DOKU_INC.'inc/init.php');
13session_write_close();  //close session
14
15if(!$conf['xmlrpc']) die('XML-RPC server not enabled.');
16
17/**
18 * Contains needed wrapper functions and registers all available
19 * XMLRPC functions.
20 */
21class dokuwiki_xmlrpc_server extends IXR_IntrospectionServer {
22    var $methods       = array();
23    var $public_methods = array();
24
25    /**
26     * Checks if the current user is allowed to execute non anonymous methods
27     */
28    function checkAuth(){
29        global $conf;
30        global $USERINFO;
31
32        if(!$conf['useacl']) return true; //no ACL - then no checks
33        if(trim($conf['xmlrpcuser']) == '') return true; //no restrictions
34
35        return auth_isMember($conf['xmlrpcuser'],$_SERVER['REMOTE_USER'],(array) $USERINFO['grps']);
36    }
37
38    /**
39     * Adds a callback, extends parent method
40     *
41     * add another parameter to define if anonymous access to
42     * this method should be granted.
43     */
44    function addCallback($method, $callback, $args, $help, $public=false){
45        if($public) $this->public_methods[] = $method;
46        return parent::addCallback($method, $callback, $args, $help);
47    }
48
49    /**
50     * Execute a call, extends parent method
51     *
52     * Checks for authentication first
53     */
54    function call($methodname, $args){
55        if(!in_array($methodname,$this->public_methods) && !$this->checkAuth()){
56            return new IXR_Error(-32603, 'server error. not authorized to call method "'.$methodname.'".');
57        }
58        return parent::call($methodname, $args);
59    }
60
61    /**
62     * Constructor. Register methods and run Server
63     */
64    function dokuwiki_xmlrpc_server(){
65        $this->IXR_IntrospectionServer();
66
67        /* DokuWiki's own methods */
68        $this->addCallback(
69            'dokuwiki.getXMLRPCAPIVersion',
70            'this:getAPIVersion',
71            array('integer'),
72            'Returns the XMLRPC API version.',
73            true
74        );
75
76        $this->addCallback(
77            'dokuwiki.getVersion',
78            'getVersion',
79            array('string'),
80            'Returns the running DokuWiki version.',
81            true
82        );
83
84        $this->addCallback(
85            'dokuwiki.login',
86            'this:login',
87            array('integer','string','string'),
88            'Tries to login with the given credentials and sets auth cookies.',
89            true
90        );
91
92        $this->addCallback(
93            'dokuwiki.getPagelist',
94            'this:readNamespace',
95            array('struct','string','struct'),
96            'List all pages within the given namespace.'
97        );
98
99        $this->addCallback(
100            'dokuwiki.search',
101            'this:search',
102            array('struct','string'),
103            'Perform a fulltext search and return a list of matching pages'
104        );
105
106        $this->addCallback(
107            'dokuwiki.getTime',
108            'time',
109            array('int'),
110            'Return the current time at the wiki server.'
111        );
112
113        $this->addCallback(
114            'dokuwiki.setLocks',
115            'this:setLocks',
116            array('struct','struct'),
117            'Lock or unlock pages.'
118        );
119
120
121        $this->addCallback(
122            'dokuwiki.getTitle',
123            'this:getTitle',
124            array('string'),
125            'Returns the wiki title.',
126            true
127        );
128
129        $this->addCallback(
130            'dokuwiki.appendPage',
131            'this:appendPage',
132            array('int', 'string', 'string', 'struct'),
133            'Append text to a wiki page.'
134        );
135
136        /* Wiki API v2 http://www.jspwiki.org/wiki/WikiRPCInterface2 */
137        $this->addCallback(
138            'wiki.getRPCVersionSupported',
139            'this:wiki_RPCVersion',
140            array('int'),
141            'Returns 2 with the supported RPC API version.',
142            true
143        );
144        $this->addCallback(
145            'wiki.getPage',
146            'this:rawPage',
147            array('string','string'),
148            'Get the raw Wiki text of page, latest version.'
149        );
150        $this->addCallback(
151            'wiki.getPageVersion',
152            'this:rawPage',
153            array('string','string','int'),
154            'Get the raw Wiki text of page.'
155        );
156        $this->addCallback(
157            'wiki.getPageHTML',
158            'this:htmlPage',
159            array('string','string'),
160            'Return page in rendered HTML, latest version.'
161        );
162        $this->addCallback(
163            'wiki.getPageHTMLVersion',
164            'this:htmlPage',
165            array('string','string','int'),
166            'Return page in rendered HTML.'
167        );
168        $this->addCallback(
169            'wiki.getAllPages',
170            'this:listPages',
171            array('struct'),
172            'Returns a list of all pages. The result is an array of utf8 pagenames.'
173        );
174        $this->addCallback(
175            'wiki.getAttachments',
176            'this:listAttachments',
177            array('struct', 'string', 'struct'),
178            'Returns a list of all media files.'
179        );
180        $this->addCallback(
181            'wiki.getBackLinks',
182            'this:listBackLinks',
183            array('struct','string'),
184            'Returns the pages that link to this page.'
185        );
186        $this->addCallback(
187            'wiki.getPageInfo',
188            'this:pageInfo',
189            array('struct','string'),
190            'Returns a struct with infos about the page.'
191        );
192        $this->addCallback(
193            'wiki.getPageInfoVersion',
194            'this:pageInfo',
195            array('struct','string','int'),
196            'Returns a struct with infos about the page.'
197        );
198        $this->addCallback(
199            'wiki.getPageVersions',
200            'this:pageVersions',
201            array('struct','string','int'),
202            'Returns the available revisions of the page.'
203        );
204        $this->addCallback(
205            'wiki.putPage',
206            'this:putPage',
207            array('int', 'string', 'string', 'struct'),
208            'Saves a wiki page.'
209        );
210        $this->addCallback(
211            'wiki.listLinks',
212            'this:listLinks',
213            array('struct','string'),
214            'Lists all links contained in a wiki page.'
215        );
216        $this->addCallback(
217            'wiki.getRecentChanges',
218            'this:getRecentChanges',
219            array('struct','int'),
220            'Returns a struct about all recent changes since given timestamp.'
221        );
222        $this->addCallback(
223            'wiki.getRecentMediaChanges',
224            'this:getRecentMediaChanges',
225            array('struct','int'),
226            'Returns a struct about all recent media changes since given timestamp.'
227        );
228        $this->addCallback(
229            'wiki.aclCheck',
230            'this:aclCheck',
231            array('int', 'string'),
232            'Returns the permissions of a given wiki page.'
233        );
234        $this->addCallback(
235            'wiki.putAttachment',
236            'this:putAttachment',
237            array('struct', 'string', 'base64', 'struct'),
238            'Upload a file to the wiki.'
239        );
240        $this->addCallback(
241            'wiki.deleteAttachment',
242            'this:deleteAttachment',
243            array('int', 'string'),
244            'Delete a file from the wiki.'
245        );
246        $this->addCallback(
247            'wiki.getAttachment',
248            'this:getAttachment',
249            array('base64', 'string'),
250            'Download a file from the wiki.'
251        );
252        $this->addCallback(
253            'wiki.getAttachmentInfo',
254            'this:getAttachmentInfo',
255            array('struct', 'string'),
256            'Returns a struct with infos about the attachment.'
257        );
258
259        /**
260         * Trigger XMLRPC_CALLBACK_REGISTER, action plugins can use this event
261         * to extend the XMLRPC interface and register their own callbacks.
262         *
263         * Event data:
264         *  The XMLRPC server object:
265         *
266         *  $event->data->addCallback() - register a callback, the second
267         *  paramter has to be of the form "plugin:<pluginname>:<plugin
268         *  method>"
269         *
270         *  $event->data->callbacks - an array which holds all awaylable
271         *  callbacks
272         */
273        trigger_event('XMLRPC_CALLBACK_REGISTER', $this);
274
275        $this->serve();
276    }
277
278    /**
279     * Return a raw wiki page
280     */
281    function rawPage($id,$rev=''){
282        $id = cleanID($id);
283        if(auth_quickaclcheck($id) < AUTH_READ){
284            return new IXR_Error(1, 'You are not allowed to read this page');
285        }
286        $text = rawWiki($id,$rev);
287        if(!$text) {
288            return pageTemplate($id);
289        } else {
290            return $text;
291        }
292    }
293
294    /**
295     * Return a media file encoded in base64
296     *
297     * @author Gina Haeussge <osd@foosel.net>
298     */
299    function getAttachment($id){
300        $id = cleanID($id);
301        if (auth_quickaclcheck(getNS($id).':*') < AUTH_READ)
302            return new IXR_Error(1, 'You are not allowed to read this file');
303
304        $file = mediaFN($id);
305        if (!@ file_exists($file))
306            return new IXR_Error(1, 'The requested file does not exist');
307
308        $data = io_readFile($file, false);
309        $base64 = base64_encode($data);
310        return $base64;
311    }
312
313    /**
314     * Return info about a media file
315     *
316     * @author Gina Haeussge <osd@foosel.net>
317     */
318    function getAttachmentInfo($id){
319        $id = cleanID($id);
320        $info = array(
321            'lastModified' => 0,
322            'size' => 0,
323        );
324
325        $file = mediaFN($id);
326        if ((auth_quickaclcheck(getNS($id).':*') >= AUTH_READ) && file_exists($file)){
327            $info['lastModified'] = new IXR_Date(filemtime($file));
328            $info['size'] = filesize($file);
329        }
330
331        return $info;
332    }
333
334    /**
335     * Return a wiki page rendered to html
336     */
337    function htmlPage($id,$rev=''){
338        $id = cleanID($id);
339        if(auth_quickaclcheck($id) < AUTH_READ){
340            return new IXR_Error(1, 'You are not allowed to read this page');
341        }
342        return p_wiki_xhtml($id,$rev,false);
343    }
344
345    /**
346     * List all pages - we use the indexer list here
347     */
348    function listPages(){
349        $list  = array();
350        $pages = idx_get_indexer()->getPages();
351        $pages = array_filter(array_filter($pages,'isVisiblePage'),'page_exists');
352
353        foreach(array_keys($pages) as $idx) {
354            $perm = auth_quickaclcheck($pages[$idx]);
355            if($perm < AUTH_READ) {
356                continue;
357            }
358            $page = array();
359            $page['id'] = trim($pages[$idx]);
360            $page['perms'] = $perm;
361            $page['size'] = @filesize(wikiFN($pages[$idx]));
362            $page['lastModified'] = new IXR_Date(@filemtime(wikiFN($pages[$idx])));
363            $list[] = $page;
364        }
365
366        return $list;
367    }
368
369    /**
370     * List all pages in the given namespace (and below)
371     */
372    function readNamespace($ns,$opts){
373        global $conf;
374
375        if(!is_array($opts)) $opts=array();
376
377        $ns = cleanID($ns);
378        $dir = utf8_encodeFN(str_replace(':', '/', $ns));
379        $data = array();
380        $opts['skipacl'] = 0; // no ACL skipping for XMLRPC
381        search($data, $conf['datadir'], 'search_allpages', $opts, $dir);
382        return $data;
383    }
384
385    /**
386     * List all pages in the given namespace (and below)
387     */
388    function search($query){
389        require_once(DOKU_INC.'inc/fulltext.php');
390
391        $regex = '';
392        $data  = ft_pageSearch($query,$regex);
393        $pages = array();
394
395        // prepare additional data
396        $idx = 0;
397        foreach($data as $id => $score){
398            $file = wikiFN($id);
399
400            if($idx < FT_SNIPPET_NUMBER){
401                $snippet = ft_snippet($id,$regex);
402                $idx++;
403            }else{
404                $snippet = '';
405            }
406
407            $pages[] = array(
408                'id'      => $id,
409                'score'   => $score,
410                'rev'     => filemtime($file),
411                'mtime'   => filemtime($file),
412                'size'    => filesize($file),
413                'snippet' => $snippet,
414            );
415        }
416        return $pages;
417    }
418
419    /**
420     * Returns the wiki title.
421     */
422    function getTitle(){
423        global $conf;
424        return $conf['title'];
425    }
426
427    /**
428     * List all media files.
429     *
430     * Available options are 'recursive' for also including the subnamespaces
431     * in the listing, and 'pattern' for filtering the returned files against
432     * a regular expression matching their name.
433     *
434     * @author Gina Haeussge <osd@foosel.net>
435     */
436    function listAttachments($ns, $options = array()) {
437        global $conf;
438        global $lang;
439
440        $ns = cleanID($ns);
441
442        if (!is_array($options)) $options = array();
443        $options['skipacl'] = 0; // no ACL skipping for XMLRPC
444
445
446        if(auth_quickaclcheck($ns.':*') >= AUTH_READ) {
447            $dir = utf8_encodeFN(str_replace(':', '/', $ns));
448
449            $data = array();
450            search($data, $conf['mediadir'], 'search_media', $options, $dir);
451            $len = count($data);
452            if(!$len) return array();
453
454            for($i=0; $i<$len; $i++) {
455                unset($data[$i]['meta']);
456                $data[$i]['lastModified'] = new IXR_Date($data[$i]['mtime']);
457            }
458            return $data;
459        } else {
460            return new IXR_Error(1, 'You are not allowed to list media files.');
461        }
462    }
463
464    /**
465     * Return a list of backlinks
466     */
467    function listBackLinks($id){
468        return ft_backlinks(cleanID($id));
469    }
470
471    /**
472     * Return some basic data about a page
473     */
474    function pageInfo($id,$rev=''){
475        $id = cleanID($id);
476        if(auth_quickaclcheck($id) < AUTH_READ){
477            return new IXR_Error(1, 'You are not allowed to read this page');
478        }
479        $file = wikiFN($id,$rev);
480        $time = @filemtime($file);
481        if(!$time){
482            return new IXR_Error(10, 'The requested page does not exist');
483        }
484
485        $info = getRevisionInfo($id, $time, 1024);
486
487        $data = array(
488            'name'         => $id,
489            'lastModified' => new IXR_Date($time),
490            'author'       => (($info['user']) ? $info['user'] : $info['ip']),
491            'version'      => $time
492        );
493
494        return ($data);
495    }
496
497    /**
498     * Save a wiki page
499     *
500     * @author Michael Klier <chi@chimeric.de>
501     */
502    function putPage($id, $text, $params) {
503        global $TEXT;
504        global $lang;
505        global $conf;
506
507        $id    = cleanID($id);
508        $TEXT  = cleanText($text);
509        $sum   = $params['sum'];
510        $minor = $params['minor'];
511
512        if(empty($id))
513            return new IXR_Error(1, 'Empty page ID');
514
515        if(!page_exists($id) && trim($TEXT) == '' ) {
516            return new IXR_ERROR(1, 'Refusing to write an empty new wiki page');
517        }
518
519        if(auth_quickaclcheck($id) < AUTH_EDIT)
520            return new IXR_Error(1, 'You are not allowed to edit this page');
521
522        // Check, if page is locked
523        if(checklock($id))
524            return new IXR_Error(1, 'The page is currently locked');
525
526        // SPAM check
527        if(checkwordblock())
528            return new IXR_Error(1, 'Positive wordblock check');
529
530        // autoset summary on new pages
531        if(!page_exists($id) && empty($sum)) {
532            $sum = $lang['created'];
533        }
534
535        // autoset summary on deleted pages
536        if(page_exists($id) && empty($TEXT) && empty($sum)) {
537            $sum = $lang['deleted'];
538        }
539
540        lock($id);
541
542        saveWikiText($id,$TEXT,$sum,$minor);
543
544        unlock($id);
545
546        // run the indexer if page wasn't indexed yet
547        idx_addPage($id);
548
549        return 0;
550    }
551
552    /**
553     * Appends text to a wiki page.
554     */
555    function appendPage($id, $text, $params) {
556        $currentpage = $this->rawPage($id);
557        if (!is_string($currentpage)) {
558            return $currentpage;
559        }
560        return $this->putPage($id, $currentpage.$text, $params);
561    }
562
563    /**
564     * Uploads a file to the wiki.
565     *
566     * Michael Klier <chi@chimeric.de>
567     */
568    function putAttachment($id, $file, $params) {
569        $id = cleanID($id);
570        $auth = auth_quickaclcheck(getNS($id).':*');
571
572        if(!isset($id)) {
573            return new IXR_ERROR(1, 'Filename not given.');
574        }
575
576        global $conf;
577
578        $ftmp = $conf['tmpdir'] . '/' . md5($id.clientIP());
579
580        // save temporary file
581        @unlink($ftmp);
582        $buff = base64_decode($file);
583        io_saveFile($ftmp, $buff);
584
585        $res = media_save(array('name' => $ftmp), $id, $params['ow'], $auth, 'rename');
586        if (is_array($res)) {
587            return new IXR_ERROR(-$res[1], $res[0]);
588        } else {
589            return $res;
590        }
591    }
592
593    /**
594     * Deletes a file from the wiki.
595     *
596     * @author Gina Haeussge <osd@foosel.net>
597     */
598    function deleteAttachment($id){
599        $id = cleanID($id);
600        $auth = auth_quickaclcheck(getNS($id).':*');
601        $res = media_delete($id, $auth);
602        if ($res & DOKU_MEDIA_DELETED) {
603            return 0;
604        } elseif ($res & DOKU_MEDIA_NOT_AUTH) {
605            return new IXR_ERROR(1, "You don't have permissions to delete files.");
606        } elseif ($res & DOKU_MEDIA_INUSE) {
607            return new IXR_ERROR(1, 'File is still referenced');
608        } else {
609            return new IXR_ERROR(1, 'Could not delete file');
610        }
611    }
612
613    /**
614    * Returns the permissions of a given wiki page
615    */
616    function aclCheck($id) {
617        $id = cleanID($id);
618        return auth_quickaclcheck($id);
619    }
620
621    /**
622     * Lists all links contained in a wiki page
623     *
624     * @author Michael Klier <chi@chimeric.de>
625     */
626    function listLinks($id) {
627        $id = cleanID($id);
628        if(auth_quickaclcheck($id) < AUTH_READ){
629            return new IXR_Error(1, 'You are not allowed to read this page');
630        }
631        $links = array();
632
633        // resolve page instructions
634        $ins   = p_cached_instructions(wikiFN($id));
635
636        // instantiate new Renderer - needed for interwiki links
637        include(DOKU_INC.'inc/parser/xhtml.php');
638        $Renderer = new Doku_Renderer_xhtml();
639        $Renderer->interwiki = getInterwiki();
640
641        // parse parse instructions
642        foreach($ins as $in) {
643            $link = array();
644            switch($in[0]) {
645                case 'internallink':
646                    $link['type'] = 'local';
647                    $link['page'] = $in[1][0];
648                    $link['href'] = wl($in[1][0]);
649                    array_push($links,$link);
650                    break;
651                case 'externallink':
652                    $link['type'] = 'extern';
653                    $link['page'] = $in[1][0];
654                    $link['href'] = $in[1][0];
655                    array_push($links,$link);
656                    break;
657                case 'interwikilink':
658                    $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]);
659                    $link['type'] = 'extern';
660                    $link['page'] = $url;
661                    $link['href'] = $url;
662                    array_push($links,$link);
663                    break;
664            }
665        }
666
667        return ($links);
668    }
669
670    /**
671     * Returns a list of recent changes since give timestamp
672     *
673     * @author Michael Hamann <michael@content-space.de>
674     * @author Michael Klier <chi@chimeric.de>
675     */
676    function getRecentChanges($timestamp) {
677        if(strlen($timestamp) != 10)
678            return new IXR_Error(20, 'The provided value is not a valid timestamp');
679
680        $recents = getRecentsSince($timestamp);
681
682        $changes = array();
683
684        foreach ($recents as $recent) {
685            $change = array();
686            $change['name']         = $recent['id'];
687            $change['lastModified'] = new IXR_Date($recent['date']);
688            $change['author']       = $recent['user'];
689            $change['version']      = $recent['date'];
690            $change['perms']        = $recent['perms'];
691            $change['size']         = @filesize(wikiFN($recent['id']));
692            array_push($changes, $change);
693        }
694
695        if (!empty($changes)) {
696            return $changes;
697        } else {
698            // in case we still have nothing at this point
699            return new IXR_Error(30, 'There are no changes in the specified timeframe');
700        }
701    }
702
703    /**
704     * Returns a list of recent media changes since give timestamp
705     *
706     * @author Michael Hamann <michael@content-space.de>
707     * @author Michael Klier <chi@chimeric.de>
708     */
709    function getRecentMediaChanges($timestamp) {
710        if(strlen($timestamp) != 10)
711            return new IXR_Error(20, 'The provided value is not a valid timestamp');
712
713        $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES);
714
715        $changes = array();
716
717        foreach ($recents as $recent) {
718            $change = array();
719            $change['name']         = $recent['id'];
720            $change['lastModified'] = new IXR_Date($recent['date']);
721            $change['author']       = $recent['user'];
722            $change['version']      = $recent['date'];
723            $change['perms']        = $recent['perms'];
724            $change['size']         = @filesize(mediaFN($recent['id']));
725            array_push($changes, $change);
726        }
727
728        if (!empty($changes)) {
729            return $changes;
730        } else {
731            // in case we still have nothing at this point
732            return new IXR_Error(30, 'There are no changes in the specified timeframe');
733        }
734    }
735
736    /**
737     * Returns a list of available revisions of a given wiki page
738     *
739     * @author Michael Klier <chi@chimeric.de>
740     */
741    function pageVersions($id, $first) {
742        $id = cleanID($id);
743        if(auth_quickaclcheck($id) < AUTH_READ){
744            return new IXR_Error(1, 'You are not allowed to read this page');
745        }
746        global $conf;
747
748        $versions = array();
749
750        if(empty($id))
751            return new IXR_Error(1, 'Empty page ID');
752
753        $revisions = getRevisions($id, $first, $conf['recent']+1);
754
755        if(count($revisions)==0 && $first!=0) {
756            $first=0;
757            $revisions = getRevisions($id, $first, $conf['recent']+1);
758        }
759
760        if(count($revisions)>0 && $first==0) {
761            array_unshift($revisions, '');  // include current revision
762            array_pop($revisions);          // remove extra log entry
763        }
764
765        $hasNext = false;
766        if(count($revisions)>$conf['recent']) {
767            $hasNext = true;
768            array_pop($revisions); // remove extra log entry
769        }
770
771        if(!empty($revisions)) {
772            foreach($revisions as $rev) {
773                $file = wikiFN($id,$rev);
774                $time = @filemtime($file);
775                // we check if the page actually exists, if this is not the
776                // case this can lead to less pages being returned than
777                // specified via $conf['recent']
778                if($time){
779                    $info = getRevisionInfo($id, $time, 1024);
780                    if(!empty($info)) {
781                        $data['user'] = $info['user'];
782                        $data['ip']   = $info['ip'];
783                        $data['type'] = $info['type'];
784                        $data['sum']  = $info['sum'];
785                        $data['modified'] = new IXR_Date($info['date']);
786                        $data['version'] = $info['date'];
787                        array_push($versions, $data);
788                    }
789                }
790            }
791            return $versions;
792        } else {
793            return array();
794        }
795    }
796
797    /**
798     * The version of Wiki RPC API supported
799     */
800    function wiki_RPCVersion(){
801        return 2;
802    }
803
804
805    /**
806     * Locks or unlocks a given batch of pages
807     *
808     * Give an associative array with two keys: lock and unlock. Both should contain a
809     * list of pages to lock or unlock
810     *
811     * Returns an associative array with the keys locked, lockfail, unlocked and
812     * unlockfail, each containing lists of pages.
813     */
814    function setLocks($set){
815        $locked     = array();
816        $lockfail   = array();
817        $unlocked   = array();
818        $unlockfail = array();
819
820        foreach((array) $set['lock'] as $id){
821            $id = cleanID($id);
822            if(auth_quickaclcheck($id) < AUTH_EDIT || checklock($id)){
823                $lockfail[] = $id;
824            }else{
825                lock($id);
826                $locked[] = $id;
827            }
828        }
829
830        foreach((array) $set['unlock'] as $id){
831            $id = cleanID($id);
832            if(auth_quickaclcheck($id) < AUTH_EDIT || !unlock($id)){
833                $unlockfail[] = $id;
834            }else{
835                $unlocked[] = $id;
836            }
837        }
838
839        return array(
840            'locked'     => $locked,
841            'lockfail'   => $lockfail,
842            'unlocked'   => $unlocked,
843            'unlockfail' => $unlockfail,
844        );
845    }
846
847    function getAPIVersion(){
848        return DOKU_XMLRPC_API_VERSION;
849    }
850
851    function login($user,$pass){
852        global $conf;
853        global $auth;
854        if(!$conf['useacl']) return 0;
855        if(!$auth) return 0;
856        if($auth->canDo('external')){
857            return $auth->trustExternal($user,$pass,false);
858        }else{
859            return auth_login($user,$pass,false,true);
860        }
861    }
862
863
864}
865
866$server = new dokuwiki_xmlrpc_server();
867
868// vim:ts=4:sw=4:et:
869