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