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