removed debug statements
[dokuwiki/radio.git] / lib / exe / xmlrpc.php
blob4b08e44875d03517a3e3451c3730721fbb835de8
1 <?php
2 if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../');
4 // fix when '<?xml' isn't on the very first line
5 if(isset($HTTP_RAW_POST_DATA)) $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);
7 /**
8 * Increased whenever the API is changed
9 */
10 define('DOKU_XMLRPC_API_VERSION',1);
12 require_once(DOKU_INC.'inc/init.php');
13 require_once(DOKU_INC.'inc/common.php');
14 require_once(DOKU_INC.'inc/auth.php');
15 session_write_close(); //close session
17 if(!$conf['xmlrpc']) {
18 die('XML-RPC server not enabled.');
19 // FIXME check for groups allowed
22 require_once(DOKU_INC.'inc/IXR_Library.php');
25 /**
26 * Contains needed wrapper functions and registers all available
27 * XMLRPC functions.
29 class dokuwiki_xmlrpc_server extends IXR_IntrospectionServer {
30 var $methods = array();
32 /**
33 * Constructor. Register methods and run Server
35 function dokuwiki_xmlrpc_server(){
36 $this->IXR_IntrospectionServer();
38 /* DokuWiki's own methods */
39 $this->addCallback(
40 'dokuwiki.getXMLRPCAPIVersion',
41 'this:getAPIVersion',
42 array('integer'),
43 'Returns the XMLRPC API version.'
46 $this->addCallback(
47 'dokuwiki.getVersion',
48 'getVersion',
49 array('string'),
50 'Returns the running DokuWiki version.'
53 $this->addCallback(
54 'dokuwiki.login',
55 'this:login',
56 array('integer','string','string'),
57 'Tries to login with the given credentials and sets auth cookies.'
60 $this->addCallback(
61 'dokuwiki.getPagelist',
62 'this:readNamespace',
63 array('struct','string','struct'),
64 'List all pages within the given namespace.'
67 $this->addCallback(
68 'dokuwiki.getTime',
69 'time',
70 array('int'),
71 'Return the current time at the wiki server.'
74 $this->addCallback(
75 'dokuwiki.setLocks',
76 'this:setLocks',
77 array('struct','struct'),
78 'Lock or unlock pages.'
81 /* Wiki API v2 http://www.jspwiki.org/wiki/WikiRPCInterface2 */
82 $this->addCallback(
83 'wiki.getRPCVersionSupported',
84 'this:wiki_RPCVersion',
85 array('int'),
86 'Returns 2 with the supported RPC API version.'
88 $this->addCallback(
89 'wiki.getPage',
90 'this:rawPage',
91 array('string','string'),
92 'Get the raw Wiki text of page, latest version.'
94 $this->addCallback(
95 'wiki.getPageVersion',
96 'this:rawPage',
97 array('string','string','int'),
98 'Get the raw Wiki text of page.'
100 $this->addCallback(
101 'wiki.getPageHTML',
102 'this:htmlPage',
103 array('string','string'),
104 'Return page in rendered HTML, latest version.'
106 $this->addCallback(
107 'wiki.getPageHTMLVersion',
108 'this:htmlPage',
109 array('string','string','int'),
110 'Return page in rendered HTML.'
112 $this->addCallback(
113 'wiki.getAllPages',
114 'this:listPages',
115 array('struct'),
116 'Returns a list of all pages. The result is an array of utf8 pagenames.'
118 $this->addCallback(
119 'wiki.getAttachments',
120 'this:listAttachments',
121 array('struct', 'string', 'struct'),
122 'Returns a list of all media files.'
124 $this->addCallback(
125 'wiki.getBackLinks',
126 'this:listBackLinks',
127 array('struct','string'),
128 'Returns the pages that link to this page.'
130 $this->addCallback(
131 'wiki.getPageInfo',
132 'this:pageInfo',
133 array('struct','string'),
134 'Returns a struct with infos about the page.'
136 $this->addCallback(
137 'wiki.getPageInfoVersion',
138 'this:pageInfo',
139 array('struct','string','int'),
140 'Returns a struct with infos about the page.'
142 $this->addCallback(
143 'wiki.getPageVersions',
144 'this:pageVersions',
145 array('struct','string','int'),
146 'Returns the available revisions of the page.'
148 $this->addCallback(
149 'wiki.putPage',
150 'this:putPage',
151 array('int', 'string', 'string', 'struct'),
152 'Saves a wiki page.'
154 $this->addCallback(
155 'wiki.listLinks',
156 'this:listLinks',
157 array('struct','string'),
158 'Lists all links contained in a wiki page.'
160 $this->addCallback(
161 'wiki.getRecentChanges',
162 'this:getRecentChanges',
163 array('struct','int'),
164 'Returns a struct about all recent changes since given timestamp.'
166 $this->addCallback(
167 'wiki.getRecentMediaChanges',
168 'this:getRecentMediaChanges',
169 array('struct','int'),
170 'Returns a struct about all recent media changes since given timestamp.'
172 $this->addCallback(
173 'wiki.aclCheck',
174 'this:aclCheck',
175 array('int', 'string'),
176 'Returns the permissions of a given wiki page.'
178 $this->addCallback(
179 'wiki.putAttachment',
180 'this:putAttachment',
181 array('struct', 'string', 'base64', 'struct'),
182 'Upload a file to the wiki.'
184 $this->addCallback(
185 'wiki.deleteAttachment',
186 'this:deleteAttachment',
187 array('int', 'string'),
188 'Delete a file from the wiki.'
190 $this->addCallback(
191 'wiki.getAttachment',
192 'this:getAttachment',
193 array('base64', 'string'),
194 'Download a file from the wiki.'
196 $this->addCallback(
197 'wiki.getAttachmentInfo',
198 'this:getAttachmentInfo',
199 array('struct', 'string'),
200 'Returns a struct with infos about the attachment.'
204 * Trigger XMLRPC_CALLBACK_REGISTER, action plugins can use this event
205 * to extend the XMLRPC interface and register their own callbacks.
207 * Event data:
208 * The XMLRPC server object:
210 * $event->data->addCallback() - register a callback, the second
211 * paramter has to be of the form "plugin:<pluginname>:<plugin
212 * method>"
214 * $event->data->callbacks - an array which holds all awaylable
215 * callbacks
217 trigger_event('XMLRPC_CALLBACK_REGISTER', $this);
219 $this->serve();
223 * Return a raw wiki page
225 function rawPage($id,$rev=''){
226 if(auth_quickaclcheck($id) < AUTH_READ){
227 return new IXR_Error(1, 'You are not allowed to read this page');
229 $text = rawWiki($id,$rev);
230 if(!$text) {
231 $data = array($id);
232 return trigger_event('HTML_PAGE_FROMTEMPLATE',$data,'pageTemplate',true);
233 } else {
234 return $text;
239 * Return a media file encoded in base64
241 * @author Gina Haeussge <osd@foosel.net>
243 function getAttachment($id){
244 $id = cleanID($id);
245 if (auth_quickaclcheck(getNS($id).':*') < AUTH_READ)
246 return new IXR_Error(1, 'You are not allowed to read this file');
248 $file = mediaFN($id);
249 if (!@ file_exists($file))
250 return new IXR_Error(1, 'The requested file does not exist');
252 $data = io_readFile($file, false);
253 $base64 = base64_encode($data);
254 return $base64;
258 * Return info about a media file
260 * @author Gina Haeussge <osd@foosel.net>
262 function getAttachmentInfo($id){
263 $id = cleanID($id);
264 $info = array(
265 'lastModified' => 0,
266 'size' => 0,
269 $file = mediaFN($id);
270 if ((auth_quickaclcheck(getNS($id).':*') >= AUTH_READ) && file_exists($file)){
271 $info['lastModified'] = new IXR_Date(filemtime($file));
272 $info['size'] = filesize($file);
275 return $info;
279 * Return a wiki page rendered to html
281 function htmlPage($id,$rev=''){
282 if(auth_quickaclcheck($id) < AUTH_READ){
283 return new IXR_Error(1, 'You are not allowed to read this page');
285 return p_wiki_xhtml($id,$rev,false);
289 * List all pages - we use the indexer list here
291 function listPages(){
292 global $conf;
294 $list = array();
295 $pages = file($conf['indexdir'] . '/page.idx');
296 $pages = array_filter($pages, 'isVisiblePage');
298 foreach(array_keys($pages) as $idx) {
299 if(page_exists($pages[$idx])) {
300 $perm = auth_quickaclcheck($pages[$idx]);
301 if($perm >= AUTH_READ) {
302 $page = array();
303 $page['id'] = trim($pages[$idx]);
304 $page['perms'] = $perm;
305 $page['size'] = @filesize(wikiFN($pages[$idx]));
306 $page['lastModified'] = new IXR_Date(@filemtime(wikiFN($pages[$idx])));
307 $list[] = $page;
312 return $list;
316 * List all pages in the given namespace (and below)
318 function readNamespace($ns,$opts){
319 global $conf;
321 if(!is_array($opts)) $opts=array();
323 $ns = cleanID($ns);
324 $dir = utf8_encodeFN(str_replace(':', '/', $ns));
325 $data = array();
326 require_once(DOKU_INC.'inc/search.php');
327 search($data, $conf['datadir'], 'search_allpages', $opts, $dir);
328 return $data;
332 * List all media files.
334 * Available options are 'recursive' for also including the subnamespaces
335 * in the listing, and 'pattern' for filtering the returned files against
336 * a regular expression matching their name.
338 * @author Gina Haeussge <osd@foosel.net>
340 function listAttachments($ns, $options = array()) {
341 global $conf;
342 global $lang;
344 $ns = cleanID($ns);
346 if (!is_array($options))
347 $options = array();
350 if(auth_quickaclcheck($ns.':*') >= AUTH_READ) {
351 $dir = utf8_encodeFN(str_replace(':', '/', $ns));
353 $data = array();
354 require_once(DOKU_INC.'inc/search.php');
355 search($data, $conf['mediadir'], 'search_media', $options, $dir);
356 $len = count($data);
357 if(!$len) return array();
359 for($i=0; $i<$len; $i++) {
360 unset($data[$i]['meta']);
361 $data[$i]['lastModified'] = new IXR_Date($data[$i]['mtime']);
363 return $data;
364 } else {
365 return new IXR_Error(1, 'You are not allowed to list media files.');
370 * Return a list of backlinks
372 function listBackLinks($id){
373 require_once(DOKU_INC.'inc/fulltext.php');
374 return ft_backlinks($id);
378 * Return some basic data about a page
380 function pageInfo($id,$rev=''){
381 if(auth_quickaclcheck($id) < AUTH_READ){
382 return new IXR_Error(1, 'You are not allowed to read this page');
384 $file = wikiFN($id,$rev);
385 $time = @filemtime($file);
386 if(!$time){
387 return new IXR_Error(10, 'The requested page does not exist');
390 $info = getRevisionInfo($id, $time, 1024);
392 $data = array(
393 'name' => $id,
394 'lastModified' => new IXR_Date($time),
395 'author' => (($info['user']) ? $info['user'] : $info['ip']),
396 'version' => $time
399 return ($data);
403 * Save a wiki page
405 * @author Michael Klier <chi@chimeric.de>
407 function putPage($id, $text, $params) {
408 global $TEXT;
409 global $lang;
410 global $conf;
412 $id = cleanID($id);
413 $TEXT = trim($text);
414 $sum = $params['sum'];
415 $minor = $params['minor'];
417 if(empty($id))
418 return new IXR_Error(1, 'Empty page ID');
420 if(!page_exists($id) && empty($TEXT)) {
421 return new IXR_ERROR(1, 'Refusing to write an empty new wiki page');
424 if(auth_quickaclcheck($id) < AUTH_EDIT)
425 return new IXR_Error(1, 'You are not allowed to edit this page');
427 // Check, if page is locked
428 if(checklock($id))
429 return new IXR_Error(1, 'The page is currently locked');
431 // SPAM check
432 if(checkwordblock())
433 return new IXR_Error(1, 'Positive wordblock check');
435 // autoset summary on new pages
436 if(!page_exists($id) && empty($sum)) {
437 $sum = $lang['created'];
440 // autoset summary on deleted pages
441 if(page_exists($id) && empty($TEXT) && empty($sum)) {
442 $sum = $lang['deleted'];
445 lock($id);
447 saveWikiText($id,$TEXT,$sum,$minor);
449 unlock($id);
451 // run the indexer if page wasn't indexed yet
452 if(!@file_exists(metaFN($id, '.indexed'))) {
453 // try to aquire a lock
454 $lock = $conf['lockdir'].'/_indexer.lock';
455 while(!@mkdir($lock,$conf['dmode'])){
456 usleep(50);
457 if(time()-@filemtime($lock) > 60*5){
458 // looks like a stale lock - remove it
459 @rmdir($lock);
460 }else{
461 return false;
464 if($conf['dperm']) chmod($lock, $conf['dperm']);
466 require_once(DOKU_INC.'inc/indexer.php');
468 // do the work
469 idx_addPage($id);
471 // we're finished - save and free lock
472 io_saveFile(metaFN($id,'.indexed'),INDEXER_VERSION);
473 @rmdir($lock);
476 return 0;
480 * Uploads a file to the wiki.
482 * Michael Klier <chi@chimeric.de>
484 function putAttachment($id, $file, $params) {
485 global $conf;
486 global $lang;
488 $auth = auth_quickaclcheck(getNS($id).':*');
489 if($auth >= AUTH_UPLOAD) {
490 if(!isset($id)) {
491 return new IXR_ERROR(1, 'Filename not given.');
494 $ftmp = $conf['tmpdir'] . '/' . $id;
496 // save temporary file
497 @unlink($ftmp);
498 $buff = base64_decode($file);
499 io_saveFile($ftmp, $buff);
501 // get filename
502 list($iext, $imime,$dl) = mimetype($id);
503 $id = cleanID($id);
504 $fn = mediaFN($id);
506 // get filetype regexp
507 $types = array_keys(getMimeTypes());
508 $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
509 $regex = join('|',$types);
511 // because a temp file was created already
512 if(preg_match('/\.('.$regex.')$/i',$fn)) {
513 //check for overwrite
514 $overwrite = @file_exists($fn);
515 if($overwrite && (!$params['ow'] || $auth < AUTH_DELETE)) {
516 return new IXR_ERROR(1, $lang['uploadexist'].'1');
518 // check for valid content
519 @require_once(DOKU_INC.'inc/media.php');
520 $ok = media_contentcheck($ftmp, $imime);
521 if($ok == -1) {
522 return new IXR_ERROR(1, sprintf($lang['uploadexist'].'2', ".$iext"));
523 } elseif($ok == -2) {
524 return new IXR_ERROR(1, $lang['uploadspam']);
525 } elseif($ok == -3) {
526 return new IXR_ERROR(1, $lang['uploadxss']);
529 // prepare event data
530 $data[0] = $ftmp;
531 $data[1] = $fn;
532 $data[2] = $id;
533 $data[3] = $imime;
534 $data[4] = $overwrite;
536 // trigger event
537 require_once(DOKU_INC.'inc/events.php');
538 return trigger_event('MEDIA_UPLOAD_FINISH', $data, array($this, '_media_upload_action'), true);
540 } else {
541 return new IXR_ERROR(1, $lang['uploadwrong']);
543 } else {
544 return new IXR_ERROR(1, "You don't have permissions to upload files.");
549 * Deletes a file from the wiki.
551 * @author Gina Haeussge <osd@foosel.net>
553 function deleteAttachment($id){
554 $auth = auth_quickaclcheck(getNS($id).':*');
555 if($auth < AUTH_DELETE) return new IXR_ERROR(1, "You don't have permissions to delete files.");
556 global $conf;
557 global $lang;
559 // check for references if needed
560 $mediareferences = array();
561 if($conf['refcheck']){
562 require_once(DOKU_INC.'inc/fulltext.php');
563 $mediareferences = ft_mediause($id,$conf['refshow']);
566 if(!count($mediareferences)){
567 $file = mediaFN($id);
568 if(@unlink($file)){
569 require_once(DOKU_INC.'inc/changelog.php');
570 addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE);
571 io_sweepNS($id,'mediadir');
572 return 0;
574 //something went wrong
575 return new IXR_ERROR(1, 'Could not delete file');
576 } else {
577 return new IXR_ERROR(1, 'File is still referenced');
582 * Moves the temporary file to its final destination.
584 * Michael Klier <chi@chimeric.de>
586 function _media_upload_action($data) {
587 global $conf;
589 if(is_array($data) && count($data)===5) {
590 io_createNamespace($data[2], 'media');
591 if(rename($data[0], $data[1])) {
592 chmod($data[1], $conf['fmode']);
593 media_notify($data[2], $data[1], $data[3]);
594 // add a log entry to the media changelog
595 require_once(DOKU_INC.'inc/changelog.php');
596 if ($data[4]) {
597 addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_EDIT);
598 } else {
599 addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_CREATE);
601 return $data[2];
602 } else {
603 return new IXR_ERROR(1, 'Upload failed.');
605 } else {
606 return new IXR_ERROR(1, 'Upload failed.');
611 * Returns the permissions of a given wiki page
613 function aclCheck($id) {
614 return auth_quickaclcheck($id);
618 * Lists all links contained in a wiki page
620 * @author Michael Klier <chi@chimeric.de>
622 function listLinks($id) {
623 if(auth_quickaclcheck($id) < AUTH_READ){
624 return new IXR_Error(1, 'You are not allowed to read this page');
626 $links = array();
628 // resolve page instructions
629 $ins = p_cached_instructions(wikiFN(cleanID($id)));
631 // instantiate new Renderer - needed for interwiki links
632 include(DOKU_INC.'inc/parser/xhtml.php');
633 $Renderer = new Doku_Renderer_xhtml();
634 $Renderer->interwiki = getInterwiki();
636 // parse parse instructions
637 foreach($ins as $in) {
638 $link = array();
639 switch($in[0]) {
640 case 'internallink':
641 $link['type'] = 'local';
642 $link['page'] = $in[1][0];
643 $link['href'] = wl($in[1][0]);
644 array_push($links,$link);
645 break;
646 case 'externallink':
647 $link['type'] = 'extern';
648 $link['page'] = $in[1][0];
649 $link['href'] = $in[1][0];
650 array_push($links,$link);
651 break;
652 case 'interwikilink':
653 $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]);
654 $link['type'] = 'extern';
655 $link['page'] = $url;
656 $link['href'] = $url;
657 array_push($links,$link);
658 break;
662 return ($links);
666 * Returns a list of recent changes since give timestamp
668 * @author Michael Hamann <michael@content-space.de>
669 * @author Michael Klier <chi@chimeric.de>
671 function getRecentChanges($timestamp) {
672 if(strlen($timestamp) != 10)
673 return new IXR_Error(20, 'The provided value is not a valid timestamp');
675 require_once(DOKU_INC.'inc/changelog.php');
676 require_once(DOKU_INC.'inc/pageutils.php');
678 $recents = getRecentsSince($timestamp);
680 $changes = array();
682 foreach ($recents as $recent) {
683 $change = array();
684 $change['name'] = $recent['id'];
685 $change['lastModified'] = new IXR_Date($recent['date']);
686 $change['author'] = $recent['user'];
687 $change['version'] = $recent['date'];
688 $change['perms'] = $recent['perms'];
689 $change['size'] = @filesize(wikiFN($recent['id']));
690 array_push($changes, $change);
693 if (!empty($changes)) {
694 return $changes;
695 } else {
696 // in case we still have nothing at this point
697 return new IXR_Error(30, 'There are no changes in the specified timeframe');
702 * Returns a list of recent media changes since give timestamp
704 * @author Michael Hamann <michael@content-space.de>
705 * @author Michael Klier <chi@chimeric.de>
707 function getRecentMediaChanges($timestamp) {
708 if(strlen($timestamp) != 10)
709 return new IXR_Error(20, 'The provided value is not a valid timestamp');
711 require_once(DOKU_INC.'inc/changelog.php');
712 require_once(DOKU_INC.'inc/pageutils.php');
714 $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES);
716 $changes = array();
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);
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');
738 * Returns a list of available revisions of a given wiki page
740 * @author Michael Klier <chi@chimeric.de>
742 function pageVersions($id, $first) {
743 global $conf;
745 $versions = array();
747 if(empty($id))
748 return new IXR_Error(1, 'Empty page ID');
750 require_once(DOKU_INC.'inc/changelog.php');
752 $revisions = getRevisions($id, $first, $conf['recent']+1);
754 if(count($revisions)==0 && $first!=0) {
755 $first=0;
756 $revisions = getRevisions($id, $first, $conf['recent']+1);
759 if(count($revisions)>0 && $first==0) {
760 array_unshift($revisions, ''); // include current revision
761 array_pop($revisions); // remove extra log entry
764 $hasNext = false;
765 if(count($revisions)>$conf['recent']) {
766 $hasNext = true;
767 array_pop($revisions); // remove extra log entry
770 if(!empty($revisions)) {
771 foreach($revisions as $rev) {
772 $file = wikiFN($id,$rev);
773 $time = @filemtime($file);
774 // we check if the page actually exists, if this is not the
775 // case this can lead to less pages being returned than
776 // specified via $conf['recent']
777 if($time){
778 $info = getRevisionInfo($id, $time, 1024);
779 if(!empty($info)) {
780 $data['user'] = $info['user'];
781 $data['ip'] = $info['ip'];
782 $data['type'] = $info['type'];
783 $data['sum'] = $info['sum'];
784 $data['modified'] = new IXR_Date($info['date']);
785 $data['version'] = $info['date'];
786 array_push($versions, $data);
790 return $versions;
791 } else {
792 return array();
797 * The version of Wiki RPC API supported
799 function wiki_RPCVersion(){
800 return 2;
805 * Locks or unlocks a given batch of pages
807 * Give an associative array with two keys: lock and unlock. Both should contain a
808 * list of pages to lock or unlock
810 * Returns an associative array with the keys locked, lockfail, unlocked and
811 * unlockfail, each containing lists of pages.
813 function setLocks($set){
814 $locked = array();
815 $lockfail = array();
816 $unlocked = array();
817 $unlockfail = array();
819 foreach((array) $set['lock'] as $id){
820 if(checklock($id)){
821 $lockfail[] = $id;
822 }else{
823 lock($id);
824 $locked[] = $id;
828 foreach((array) $set['unlock'] as $id){
829 if(unlock($id)){
830 $unlocked[] = $id;
831 }else{
832 $unlockfail[] = $id;
836 return array(
837 'locked' => $locked,
838 'lockfail' => $lockfail,
839 'unlocked' => $unlocked,
840 'unlockfail' => $unlockfail,
844 function getAPIVersion(){
845 return DOKU_XMLRPC_API_VERSION;
848 function login($user,$pass){
849 global $conf;
850 global $auth;
851 if(!$conf['useacl']) return 0;
852 if(!$auth) return 0;
853 if($auth->canDo('external')){
854 return $auth->trustExternal($user,$pass,false);
855 }else{
856 return auth_login($user,$pass,false,true);
861 $server = new dokuwiki_xmlrpc_server();
863 // vim:ts=4:sw=4:et:enc=utf-8: