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