Failed Conditions
Push — psr2 ( ffc2cc...de2261 )
by Andreas
05:28
created

inc/RemoteAPICore.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Increased whenever the API is changed
5
 */
6
define('DOKU_API_VERSION', 10);
7
8
/**
9
 * Provides the core methods for the remote API.
10
 * The methods are ordered in 'wiki.<method>' and 'dokuwiki.<method>' namespaces
11
 */
12
class RemoteAPICore {
13
14
    private $api;
15
16
    /**
17
     * @param RemoteAPI $api
18
     */
19
    public function __construct(RemoteAPI $api) {
20
        $this->api = $api;
21
    }
22
23
    /**
24
     * Returns details about the core methods
25
     *
26
     * @return array
27
     */
28
    public function __getRemoteInfo() {
29
        return array(
30
            'dokuwiki.getVersion' => array(
31
                'args' => array(),
32
                'return' => 'string',
33
                'doc' => 'Returns the running DokuWiki version.'
34
            ), 'dokuwiki.login' => array(
35
                'args' => array('string', 'string'),
36
                'return' => 'int',
37
                'doc' => 'Tries to login with the given credentials and sets auth cookies.',
38
                'public' => '1'
39
            ), 'dokuwiki.logoff' => array(
40
                'args' => array(),
41
                'return' => 'int',
42
                'doc' => 'Tries to logoff by expiring auth cookies and the associated PHP session.'
43
            ), 'dokuwiki.getPagelist' => array(
44
                'args' => array('string', 'array'),
45
                'return' => 'array',
46
                'doc' => 'List all pages within the given namespace.',
47
                'name' => 'readNamespace'
48
            ), 'dokuwiki.search' => array(
49
                'args' => array('string'),
50
                'return' => 'array',
51
                'doc' => 'Perform a fulltext search and return a list of matching pages'
52
            ), 'dokuwiki.getTime' => array(
53
                'args' => array(),
54
                'return' => 'int',
55
                'doc' =>  'Returns the current time at the remote wiki server as Unix timestamp.',
56
            ), 'dokuwiki.setLocks' => array(
57
                'args' => array('array'),
58
                'return' => 'array',
59
                'doc' => 'Lock or unlock pages.'
60
            ), 'dokuwiki.getTitle' => array(
61
                'args' => array(),
62
                'return' => 'string',
63
                'doc' => 'Returns the wiki title.',
64
                'public' => '1'
65
            ), 'dokuwiki.appendPage' => array(
66
                'args' => array('string', 'string', 'array'),
67
                'return' => 'bool',
68
                'doc' => 'Append text to a wiki page.'
69
            ),  'wiki.getPage' => array(
70
                'args' => array('string'),
71
                'return' => 'string',
72
                'doc' => 'Get the raw Wiki text of page, latest version.',
73
                'name' => 'rawPage',
74
            ), 'wiki.getPageVersion' => array(
75
                'args' => array('string', 'int'),
76
                'name' => 'rawPage',
77
                'return' => 'string',
78
                'doc' => 'Return a raw wiki page'
79
            ), 'wiki.getPageHTML' => array(
80
                'args' => array('string'),
81
                'return' => 'string',
82
                'doc' => 'Return page in rendered HTML, latest version.',
83
                'name' => 'htmlPage'
84
            ), 'wiki.getPageHTMLVersion' => array(
85
                'args' => array('string', 'int'),
86
                'return' => 'string',
87
                'doc' => 'Return page in rendered HTML.',
88
                'name' => 'htmlPage'
89
            ), 'wiki.getAllPages' => array(
90
                'args' => array(),
91
                'return' => 'array',
92
                'doc' => 'Returns a list of all pages. The result is an array of utf8 pagenames.',
93
                'name' => 'listPages'
94
            ), 'wiki.getAttachments' => array(
95
                'args' => array('string', 'array'),
96
                'return' => 'array',
97
                'doc' => 'Returns a list of all media files.',
98
                'name' => 'listAttachments'
99
            ), 'wiki.getBackLinks' => array(
100
                'args' => array('string'),
101
                'return' => 'array',
102
                'doc' => 'Returns the pages that link to this page.',
103
                'name' => 'listBackLinks'
104
            ), 'wiki.getPageInfo' => array(
105
                'args' => array('string'),
106
                'return' => 'array',
107
                'doc' => 'Returns a struct with info about the page, latest version.',
108
                'name' => 'pageInfo'
109
            ), 'wiki.getPageInfoVersion' => array(
110
                'args' => array('string', 'int'),
111
                'return' => 'array',
112
                'doc' => 'Returns a struct with info about the page.',
113
                'name' => 'pageInfo'
114
            ), 'wiki.getPageVersions' => array(
115
                'args' => array('string', 'int'),
116
                'return' => 'array',
117
                'doc' => 'Returns the available revisions of the page.',
118
                'name' => 'pageVersions'
119
            ), 'wiki.putPage' => array(
120
                'args' => array('string', 'string', 'array'),
121
                'return' => 'bool',
122
                'doc' => 'Saves a wiki page.'
123
            ), 'wiki.listLinks' => array(
124
                'args' => array('string'),
125
                'return' => 'array',
126
                'doc' => 'Lists all links contained in a wiki page.'
127
            ), 'wiki.getRecentChanges' => array(
128
                'args' => array('int'),
129
                'return' => 'array',
130
                'Returns a struct about all recent changes since given timestamp.'
131
            ), 'wiki.getRecentMediaChanges' => array(
132
                'args' => array('int'),
133
                'return' => 'array',
134
                'Returns a struct about all recent media changes since given timestamp.'
135
            ), 'wiki.aclCheck' => array(
136
                'args' => array('string', 'string', 'array'),
137
                'return' => 'int',
138
                'doc' => 'Returns the permissions of a given wiki page. By default, for current user/groups'
139
            ), 'wiki.putAttachment' => array(
140
                'args' => array('string', 'file', 'array'),
141
                'return' => 'array',
142
                'doc' => 'Upload a file to the wiki.'
143
            ), 'wiki.deleteAttachment' => array(
144
                'args' => array('string'),
145
                'return' => 'int',
146
                'doc' => 'Delete a file from the wiki.'
147
            ), 'wiki.getAttachment' => array(
148
                'args' => array('string'),
149
                'doc' => 'Return a media file',
150
                'return' => 'file',
151
                'name' => 'getAttachment',
152
            ), 'wiki.getAttachmentInfo' => array(
153
                'args' => array('string'),
154
                'return' => 'array',
155
                'doc' => 'Returns a struct with info about the attachment.'
156
            ), 'dokuwiki.getXMLRPCAPIVersion' => array(
157
                'args' => array(),
158
                'name' => 'getAPIVersion',
159
                'return' => 'int',
160
                'doc' => 'Returns the XMLRPC API version.',
161
                'public' => '1',
162
            ), 'wiki.getRPCVersionSupported' => array(
163
                'args' => array(),
164
                'name' => 'wiki_RPCVersion',
165
                'return' => 'int',
166
                'doc' => 'Returns 2 with the supported RPC API version.',
167
                'public' => '1'
168
            ),
169
170
        );
171
    }
172
173
    /**
174
     * @return string
175
     */
176
    public function getVersion() {
177
        return getVersion();
178
    }
179
180
    /**
181
     * @return int unix timestamp
182
     */
183
    public function getTime() {
184
        return time();
185
    }
186
187
    /**
188
     * Return a raw wiki page
189
     *
190
     * @param string $id wiki page id
191
     * @param int|string $rev revision timestamp of the page or empty string
192
     * @return string page text.
193
     * @throws RemoteAccessDeniedException if no permission for page
194
     */
195
    public function rawPage($id,$rev=''){
196
        $id = $this->resolvePageId($id);
197
        if(auth_quickaclcheck($id) < AUTH_READ){
198
            throw new RemoteAccessDeniedException('You are not allowed to read this file', 111);
199
        }
200
        $text = rawWiki($id,$rev);
201
        if(!$text) {
202
            return pageTemplate($id);
203
        } else {
204
            return $text;
205
        }
206
    }
207
208
    /**
209
     * Return a media file
210
     *
211
     * @author Gina Haeussge <[email protected]>
212
     *
213
     * @param string $id file id
214
     * @return mixed media file
215
     * @throws RemoteAccessDeniedException no permission for media
216
     * @throws RemoteException not exist
217
     */
218
    public function getAttachment($id){
219
        $id = cleanID($id);
220
        if (auth_quickaclcheck(getNS($id).':*') < AUTH_READ) {
221
            throw new RemoteAccessDeniedException('You are not allowed to read this file', 211);
222
        }
223
224
        $file = mediaFN($id);
225
        if (!@ file_exists($file)) {
226
            throw new RemoteException('The requested file does not exist', 221);
227
        }
228
229
        $data = io_readFile($file, false);
230
        return $this->api->toFile($data);
231
    }
232
233
    /**
234
     * Return info about a media file
235
     *
236
     * @author Gina Haeussge <[email protected]>
237
     *
238
     * @param string $id page id
239
     * @return array
240
     */
241
    public function getAttachmentInfo($id){
242
        $id = cleanID($id);
243
        $info = array(
244
            'lastModified' => $this->api->toDate(0),
245
            'size' => 0,
246
        );
247
248
        $file = mediaFN($id);
249
        if(auth_quickaclcheck(getNS($id) . ':*') >= AUTH_READ) {
250
            if(file_exists($file)) {
251
                $info['lastModified'] = $this->api->toDate(filemtime($file));
252
                $info['size'] = filesize($file);
253
            } else {
254
                //Is it deleted media with changelog?
255
                $medialog = new MediaChangeLog($id);
256
                $revisions = $medialog->getRevisions(0, 1);
257
                if(!empty($revisions)) {
258
                    $info['lastModified'] = $this->api->toDate($revisions[0]);
259
                }
260
            }
261
        }
262
263
        return $info;
264
    }
265
266
    /**
267
     * Return a wiki page rendered to html
268
     *
269
     * @param string     $id  page id
270
     * @param string|int $rev revision timestamp or empty string
271
     * @return null|string html
272
     * @throws RemoteAccessDeniedException no access to page
273
     */
274
    public function htmlPage($id,$rev=''){
275
        $id = $this->resolvePageId($id);
276
        if(auth_quickaclcheck($id) < AUTH_READ){
277
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
278
        }
279
        return p_wiki_xhtml($id,$rev,false);
280
    }
281
282
    /**
283
     * List all pages - we use the indexer list here
284
     *
285
     * @return array
286
     */
287
    public function listPages(){
288
        $list  = array();
289
        $pages = idx_get_indexer()->getPages();
290
        $pages = array_filter(array_filter($pages,'isVisiblePage'),'page_exists');
291
292
        foreach(array_keys($pages) as $idx) {
293
            $perm = auth_quickaclcheck($pages[$idx]);
294
            if($perm < AUTH_READ) {
295
                continue;
296
            }
297
            $page = array();
298
            $page['id'] = trim($pages[$idx]);
299
            $page['perms'] = $perm;
300
            $page['size'] = @filesize(wikiFN($pages[$idx]));
301
            $page['lastModified'] = $this->api->toDate(@filemtime(wikiFN($pages[$idx])));
302
            $list[] = $page;
303
        }
304
305
        return $list;
306
    }
307
308
    /**
309
     * List all pages in the given namespace (and below)
310
     *
311
     * @param string $ns
312
     * @param array  $opts
313
     *    $opts['depth']   recursion level, 0 for all
314
     *    $opts['hash']    do md5 sum of content?
315
     * @return array
316
     */
317
    public function readNamespace($ns,$opts){
318
        global $conf;
319
320
        if(!is_array($opts)) $opts=array();
321
322
        $ns = cleanID($ns);
323
        $dir = utf8_encodeFN(str_replace(':', '/', $ns));
324
        $data = array();
325
        $opts['skipacl'] = 0; // no ACL skipping for XMLRPC
326
        search($data, $conf['datadir'], 'search_allpages', $opts, $dir);
327
        return $data;
328
    }
329
330
    /**
331
     * List all pages in the given namespace (and below)
332
     *
333
     * @param string $query
334
     * @return array
335
     */
336
    public function search($query){
337
        $regex = array();
338
        $data  = ft_pageSearch($query,$regex);
339
        $pages = array();
340
341
        // prepare additional data
342
        $idx = 0;
343
        foreach($data as $id => $score){
344
            $file = wikiFN($id);
345
346
            if($idx < FT_SNIPPET_NUMBER){
347
                $snippet = ft_snippet($id,$regex);
348
                $idx++;
349
            }else{
350
                $snippet = '';
351
            }
352
353
            $pages[] = array(
354
                'id'      => $id,
355
                'score'   => intval($score),
356
                'rev'     => filemtime($file),
357
                'mtime'   => filemtime($file),
358
                'size'    => filesize($file),
359
                'snippet' => $snippet,
360
                'title' => useHeading('navigation') ? p_get_first_heading($id) : $id
361
            );
362
        }
363
        return $pages;
364
    }
365
366
    /**
367
     * Returns the wiki title.
368
     *
369
     * @return string
370
     */
371
    public function getTitle(){
372
        global $conf;
373
        return $conf['title'];
374
    }
375
376
    /**
377
     * List all media files.
378
     *
379
     * Available options are 'recursive' for also including the subnamespaces
380
     * in the listing, and 'pattern' for filtering the returned files against
381
     * a regular expression matching their name.
382
     *
383
     * @author Gina Haeussge <[email protected]>
384
     *
385
     * @param string $ns
386
     * @param array  $options
387
     *   $options['depth']     recursion level, 0 for all
388
     *   $options['showmsg']   shows message if invalid media id is used
389
     *   $options['pattern']   check given pattern
390
     *   $options['hash']      add hashes to result list
391
     * @return array
392
     * @throws RemoteAccessDeniedException no access to the media files
393
     */
394
    public function listAttachments($ns, $options = array()) {
395
        global $conf;
396
397
        $ns = cleanID($ns);
398
399
        if (!is_array($options)) $options = array();
400
        $options['skipacl'] = 0; // no ACL skipping for XMLRPC
401
402
        if(auth_quickaclcheck($ns.':*') >= AUTH_READ) {
403
            $dir = utf8_encodeFN(str_replace(':', '/', $ns));
404
405
            $data = array();
406
            search($data, $conf['mediadir'], 'search_media', $options, $dir);
407
            $len = count($data);
408
            if(!$len) return array();
409
410
            for($i=0; $i<$len; $i++) {
411
                unset($data[$i]['meta']);
412
                $data[$i]['perms'] = $data[$i]['perm'];
413
                unset($data[$i]['perm']);
414
                $data[$i]['lastModified'] = $this->api->toDate($data[$i]['mtime']);
415
            }
416
            return $data;
417
        } else {
418
            throw new RemoteAccessDeniedException('You are not allowed to list media files.', 215);
419
        }
420
    }
421
422
    /**
423
     * Return a list of backlinks
424
     *
425
     * @param string $id page id
426
     * @return array
427
     */
428
    public function listBackLinks($id){
429
        return ft_backlinks($this->resolvePageId($id));
430
    }
431
432
    /**
433
     * Return some basic data about a page
434
     *
435
     * @param string     $id page id
436
     * @param string|int $rev revision timestamp or empty string
437
     * @return array
438
     * @throws RemoteAccessDeniedException no access for page
439
     * @throws RemoteException page not exist
440
     */
441
    public function pageInfo($id,$rev=''){
442
        $id = $this->resolvePageId($id);
443
        if(auth_quickaclcheck($id) < AUTH_READ){
444
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
445
        }
446
        $file = wikiFN($id,$rev);
447
        $time = @filemtime($file);
448
        if(!$time){
449
            throw new RemoteException('The requested page does not exist', 121);
450
        }
451
452
        // set revision to current version if empty, use revision otherwise
453
        // as the timestamps of old files are not necessarily correct
454
        if($rev === '') {
455
            $rev = $time;
456
        }
457
458
        $pagelog = new PageChangeLog($id, 1024);
459
        $info = $pagelog->getRevisionInfo($rev);
460
461
        $data = array(
462
            'name'         => $id,
463
            'lastModified' => $this->api->toDate($rev),
464
            'author'       => (($info['user']) ? $info['user'] : $info['ip']),
465
            'version'      => $rev
466
        );
467
468
        return ($data);
469
    }
470
471
    /**
472
     * Save a wiki page
473
     *
474
     * @author Michael Klier <[email protected]>
475
     *
476
     * @param string $id page id
477
     * @param string $text wiki text
478
     * @param array $params parameters: summary, minor edit
479
     * @return bool
480
     * @throws RemoteAccessDeniedException no write access for page
481
     * @throws RemoteException no id, empty new page or locked
482
     */
483
    public function putPage($id, $text, $params) {
484
        global $TEXT;
485
        global $lang;
486
487
        $id    = $this->resolvePageId($id);
488
        $TEXT  = cleanText($text);
489
        $sum   = $params['sum'];
490
        $minor = $params['minor'];
491
492
        if(empty($id)) {
493
            throw new RemoteException('Empty page ID', 131);
494
        }
495
496
        if(!page_exists($id) && trim($TEXT) == '' ) {
497
            throw new RemoteException('Refusing to write an empty new wiki page', 132);
498
        }
499
500
        if(auth_quickaclcheck($id) < AUTH_EDIT) {
501
            throw new RemoteAccessDeniedException('You are not allowed to edit this page', 112);
502
        }
503
504
        // Check, if page is locked
505
        if(checklock($id)) {
506
            throw new RemoteException('The page is currently locked', 133);
507
        }
508
509
        // SPAM check
510
        if(checkwordblock()) {
511
            throw new RemoteException('Positive wordblock check', 134);
512
        }
513
514
        // autoset summary on new pages
515
        if(!page_exists($id) && empty($sum)) {
516
            $sum = $lang['created'];
517
        }
518
519
        // autoset summary on deleted pages
520
        if(page_exists($id) && empty($TEXT) && empty($sum)) {
521
            $sum = $lang['deleted'];
522
        }
523
524
        lock($id);
525
526
        saveWikiText($id,$TEXT,$sum,$minor);
527
528
        unlock($id);
529
530
        // run the indexer if page wasn't indexed yet
531
        idx_addPage($id);
532
533
        return true;
534
    }
535
536
    /**
537
     * Appends text to a wiki page.
538
     *
539
     * @param string $id page id
540
     * @param string $text wiki text
541
     * @param array $params such as summary,minor
542
     * @return bool|string
543
     */
544
    public function appendPage($id, $text, $params) {
545
        $currentpage = $this->rawPage($id);
546
        if (!is_string($currentpage)) {
547
            return $currentpage;
548
        }
549
        return $this->putPage($id, $currentpage.$text, $params);
550
    }
551
552
    /**
553
     * Uploads a file to the wiki.
554
     *
555
     * Michael Klier <[email protected]>
556
     *
557
     * @param string $id page id
558
     * @param string $file
559
     * @param array $params such as overwrite
560
     * @return false|string
561
     * @throws RemoteException
562
     */
563
    public function putAttachment($id, $file, $params) {
564
        $id = cleanID($id);
565
        $auth = auth_quickaclcheck(getNS($id).':*');
566
567
        if(!isset($id)) {
568
            throw new RemoteException('Filename not given.', 231);
569
        }
570
571
        global $conf;
572
573
        $ftmp = $conf['tmpdir'] . '/' . md5($id.clientIP());
574
575
        // save temporary file
576
        @unlink($ftmp);
577
        io_saveFile($ftmp, $file);
578
579
        $res = media_save(array('name' => $ftmp), $id, $params['ow'], $auth, 'rename');
580
        if (is_array($res)) {
581
            throw new RemoteException($res[0], -$res[1]);
582
        } else {
583
            return $res;
584
        }
585
    }
586
587
    /**
588
     * Deletes a file from the wiki.
589
     *
590
     * @author Gina Haeussge <[email protected]>
591
     *
592
     * @param string $id page id
593
     * @return int
594
     * @throws RemoteAccessDeniedException no permissions
595
     * @throws RemoteException file in use or not deleted
596
     */
597
    public function deleteAttachment($id){
598
        $id = cleanID($id);
599
        $auth = auth_quickaclcheck(getNS($id).':*');
600
        $res = media_delete($id, $auth);
601
        if ($res & DOKU_MEDIA_DELETED) {
602
            return 0;
603
        } elseif ($res & DOKU_MEDIA_NOT_AUTH) {
604
            throw new RemoteAccessDeniedException('You don\'t have permissions to delete files.', 212);
605
        } elseif ($res & DOKU_MEDIA_INUSE) {
606
            throw new RemoteException('File is still referenced', 232);
607
        } else {
608
            throw new RemoteException('Could not delete file', 233);
609
        }
610
    }
611
612
    /**
613
     * Returns the permissions of a given wiki page for the current user or another user
614
     *
615
     * @param string $id page id
616
     * @param string|null $user username
617
     * @param array|null $groups array of groups
618
     * @return int permission level
619
     */
620
    public function aclCheck($id, $user = null, $groups = null) {
621
        /** @var DokuWiki_Auth_Plugin $auth */
622
        global $auth;
623
624
        $id = $this->resolvePageId($id);
625
        if($user === null) {
626
            return auth_quickaclcheck($id);
627
        } else {
628
            if($groups === null) {
629
                $userinfo = $auth->getUserData($user);
630
                if($userinfo === false) {
631
                    $groups = array();
632
                } else {
633
                    $groups = $userinfo['grps'];
634
                }
635
            }
636
            return auth_aclcheck($id, $user, $groups);
637
        }
638
    }
639
640
    /**
641
     * Lists all links contained in a wiki page
642
     *
643
     * @author Michael Klier <[email protected]>
644
     *
645
     * @param string $id page id
646
     * @return array
647
     * @throws RemoteAccessDeniedException  no read access for page
648
     */
649
    public function listLinks($id) {
650
        $id = $this->resolvePageId($id);
651
        if(auth_quickaclcheck($id) < AUTH_READ){
652
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
653
        }
654
        $links = array();
655
656
        // resolve page instructions
657
        $ins   = p_cached_instructions(wikiFN($id));
658
659
        // instantiate new Renderer - needed for interwiki links
660
        $Renderer = new Doku_Renderer_xhtml();
661
        $Renderer->interwiki = getInterwiki();
662
663
        // parse parse instructions
664
        foreach($ins as $in) {
0 ignored issues
show
The expression $ins of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
665
            $link = array();
666
            switch($in[0]) {
667
                case 'internallink':
668
                    $link['type'] = 'local';
669
                    $link['page'] = $in[1][0];
670
                    $link['href'] = wl($in[1][0]);
671
                    array_push($links,$link);
672
                    break;
673
                case 'externallink':
674
                    $link['type'] = 'extern';
675
                    $link['page'] = $in[1][0];
676
                    $link['href'] = $in[1][0];
677
                    array_push($links,$link);
678
                    break;
679
                case 'interwikilink':
680
                    $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]);
681
                    $link['type'] = 'extern';
682
                    $link['page'] = $url;
683
                    $link['href'] = $url;
684
                    array_push($links,$link);
685
                    break;
686
            }
687
        }
688
689
        return ($links);
690
    }
691
692
    /**
693
     * Returns a list of recent changes since give timestamp
694
     *
695
     * @author Michael Hamann <[email protected]>
696
     * @author Michael Klier <[email protected]>
697
     *
698
     * @param int $timestamp unix timestamp
699
     * @return array
700
     * @throws RemoteException no valid timestamp
701
     */
702
    public function getRecentChanges($timestamp) {
703
        if(strlen($timestamp) != 10) {
704
            throw new RemoteException('The provided value is not a valid timestamp', 311);
705
        }
706
707
        $recents = getRecentsSince($timestamp);
708
709
        $changes = array();
710
711
        foreach ($recents as $recent) {
712
            $change = array();
713
            $change['name']         = $recent['id'];
714
            $change['lastModified'] = $this->api->toDate($recent['date']);
715
            $change['author']       = $recent['user'];
716
            $change['version']      = $recent['date'];
717
            $change['perms']        = $recent['perms'];
718
            $change['size']         = @filesize(wikiFN($recent['id']));
719
            array_push($changes, $change);
720
        }
721
722
        if (!empty($changes)) {
723
            return $changes;
724
        } else {
725
            // in case we still have nothing at this point
726
            throw new RemoteException('There are no changes in the specified timeframe', 321);
727
        }
728
    }
729
730
    /**
731
     * Returns a list of recent media changes since give timestamp
732
     *
733
     * @author Michael Hamann <[email protected]>
734
     * @author Michael Klier <[email protected]>
735
     *
736
     * @param int $timestamp unix timestamp
737
     * @return array
738
     * @throws RemoteException no valid timestamp
739
     */
740
    public function getRecentMediaChanges($timestamp) {
741
        if(strlen($timestamp) != 10)
742
            throw new RemoteException('The provided value is not a valid timestamp', 311);
743
744
        $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES);
745
746
        $changes = array();
747
748
        foreach ($recents as $recent) {
749
            $change = array();
750
            $change['name']         = $recent['id'];
751
            $change['lastModified'] = $this->api->toDate($recent['date']);
752
            $change['author']       = $recent['user'];
753
            $change['version']      = $recent['date'];
754
            $change['perms']        = $recent['perms'];
755
            $change['size']         = @filesize(mediaFN($recent['id']));
756
            array_push($changes, $change);
757
        }
758
759
        if (!empty($changes)) {
760
            return $changes;
761
        } else {
762
            // in case we still have nothing at this point
763
            throw new RemoteException('There are no changes in the specified timeframe', 321);
764
        }
765
    }
766
767
    /**
768
     * Returns a list of available revisions of a given wiki page
769
     * Number of returned pages is set by $conf['recent']
770
     * However not accessible pages are skipped, so less than $conf['recent'] could be returned
771
     *
772
     * @author Michael Klier <[email protected]>
773
     *
774
     * @param string $id    page id
775
     * @param int    $first skip the first n changelog lines
776
     *                      0 = from current(if exists)
777
     *                      1 = from 1st old rev
778
     *                      2 = from 2nd old rev, etc
779
     * @return array
780
     * @throws RemoteAccessDeniedException no read access for page
781
     * @throws RemoteException empty id
782
     */
783
    public function pageVersions($id, $first) {
784
        $id = $this->resolvePageId($id);
785
        if(auth_quickaclcheck($id) < AUTH_READ) {
786
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
787
        }
788
        global $conf;
789
790
        $versions = array();
791
792
        if(empty($id)) {
793
            throw new RemoteException('Empty page ID', 131);
794
        }
795
796
        $first = (int) $first;
797
        $first_rev = $first - 1;
798
        $first_rev = $first_rev < 0 ? 0 : $first_rev;
799
        $pagelog = new PageChangeLog($id);
800
        $revisions = $pagelog->getRevisions($first_rev, $conf['recent']);
801
802
        if($first == 0) {
803
            array_unshift($revisions, '');  // include current revision
804
            if ( count($revisions) > $conf['recent'] ){
805
                array_pop($revisions);          // remove extra log entry
806
            }
807
        }
808
809
        if(!empty($revisions)) {
810
            foreach($revisions as $rev) {
811
                $file = wikiFN($id,$rev);
812
                $time = @filemtime($file);
813
                // we check if the page actually exists, if this is not the
814
                // case this can lead to less pages being returned than
815
                // specified via $conf['recent']
816
                if($time){
817
                    $pagelog->setChunkSize(1024);
818
                    $info = $pagelog->getRevisionInfo($rev ? $rev : $time);
819
                    if(!empty($info)) {
820
                        $data = array();
821
                        $data['user'] = $info['user'];
822
                        $data['ip']   = $info['ip'];
823
                        $data['type'] = $info['type'];
824
                        $data['sum']  = $info['sum'];
825
                        $data['modified'] = $this->api->toDate($info['date']);
826
                        $data['version'] = $info['date'];
827
                        array_push($versions, $data);
828
                    }
829
                }
830
            }
831
            return $versions;
832
        } else {
833
            return array();
834
        }
835
    }
836
837
    /**
838
     * The version of Wiki RPC API supported
839
     */
840
    public function wiki_RPCVersion(){
841
        return 2;
842
    }
843
844
845
    /**
846
     * Locks or unlocks a given batch of pages
847
     *
848
     * Give an associative array with two keys: lock and unlock. Both should contain a
849
     * list of pages to lock or unlock
850
     *
851
     * Returns an associative array with the keys locked, lockfail, unlocked and
852
     * unlockfail, each containing lists of pages.
853
     *
854
     * @param array[] $set list pages with array('lock' => array, 'unlock' => array)
855
     * @return array
856
     */
857
    public function setLocks($set){
858
        $locked     = array();
859
        $lockfail   = array();
860
        $unlocked   = array();
861
        $unlockfail = array();
862
863
        foreach((array) $set['lock'] as $id){
864
            $id = $this->resolvePageId($id);
865
            if(auth_quickaclcheck($id) < AUTH_EDIT || checklock($id)){
866
                $lockfail[] = $id;
867
            }else{
868
                lock($id);
869
                $locked[] = $id;
870
            }
871
        }
872
873
        foreach((array) $set['unlock'] as $id){
874
            $id = $this->resolvePageId($id);
875
            if(auth_quickaclcheck($id) < AUTH_EDIT || !unlock($id)){
876
                $unlockfail[] = $id;
877
            }else{
878
                $unlocked[] = $id;
879
            }
880
        }
881
882
        return array(
883
            'locked'     => $locked,
884
            'lockfail'   => $lockfail,
885
            'unlocked'   => $unlocked,
886
            'unlockfail' => $unlockfail,
887
        );
888
    }
889
890
    /**
891
     * Return API version
892
     *
893
     * @return int
894
     */
895
    public function getAPIVersion(){
896
        return DOKU_API_VERSION;
897
    }
898
899
    /**
900
     * Login
901
     *
902
     * @param string $user
903
     * @param string $pass
904
     * @return int
905
     */
906
    public function login($user,$pass){
907
        global $conf;
908
        /** @var DokuWiki_Auth_Plugin $auth */
909
        global $auth;
910
911
        if(!$conf['useacl']) return 0;
912
        if(!$auth) return 0;
913
914
        @session_start(); // reopen session for login
915
        if($auth->canDo('external')){
916
            $ok = $auth->trustExternal($user,$pass,false);
917
        }else{
918
            $evdata = array(
919
                'user'     => $user,
920
                'password' => $pass,
921
                'sticky'   => false,
922
                'silent'   => true,
923
            );
924
            $ok = trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
925
        }
926
        session_write_close(); // we're done with the session
927
928
        return $ok;
929
    }
930
931
    /**
932
     * Log off
933
     *
934
     * @return int
935
     */
936
    public function logoff(){
937
        global $conf;
938
        global $auth;
939
        if(!$conf['useacl']) return 0;
940
        if(!$auth) return 0;
941
942
        auth_logoff();
943
944
        return 1;
945
    }
946
947
    /**
948
     * Resolve page id
949
     *
950
     * @param string $id page id
951
     * @return string
952
     */
953
    private function resolvePageId($id) {
954
        $id = cleanID($id);
955
        if(empty($id)) {
956
            global $conf;
957
            $id = cleanID($conf['start']);
958
        }
959
        return $id;
960
    }
961
962
}
963
964