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