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