Completed
Push — master ( 7739b1...2cceef )
by Michael
23:56 queued 18:57
created

RemoteAPICore::deleteUsers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
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
            ), 'dokuwiki.deleteUsers' => array(
70
                'args' => array('array'),
71
                'return' => 'bool',
72
                'doc' => 'Remove one or more users from the list of registered users.'
73
            ),  'wiki.getPage' => array(
74
                'args' => array('string'),
75
                'return' => 'string',
76
                'doc' => 'Get the raw Wiki text of page, latest version.',
77
                'name' => 'rawPage',
78
            ), 'wiki.getPageVersion' => array(
79
                'args' => array('string', 'int'),
80
                'name' => 'rawPage',
81
                'return' => 'string',
82
                'doc' => 'Return a raw wiki page'
83
            ), 'wiki.getPageHTML' => array(
84
                'args' => array('string'),
85
                'return' => 'string',
86
                'doc' => 'Return page in rendered HTML, latest version.',
87
                'name' => 'htmlPage'
88
            ), 'wiki.getPageHTMLVersion' => array(
89
                'args' => array('string', 'int'),
90
                'return' => 'string',
91
                'doc' => 'Return page in rendered HTML.',
92
                'name' => 'htmlPage'
93
            ), 'wiki.getAllPages' => array(
94
                'args' => array(),
95
                'return' => 'array',
96
                'doc' => 'Returns a list of all pages. The result is an array of utf8 pagenames.',
97
                'name' => 'listPages'
98
            ), 'wiki.getAttachments' => array(
99
                'args' => array('string', 'array'),
100
                'return' => 'array',
101
                'doc' => 'Returns a list of all media files.',
102
                'name' => 'listAttachments'
103
            ), 'wiki.getBackLinks' => array(
104
                'args' => array('string'),
105
                'return' => 'array',
106
                'doc' => 'Returns the pages that link to this page.',
107
                'name' => 'listBackLinks'
108
            ), 'wiki.getPageInfo' => array(
109
                'args' => array('string'),
110
                'return' => 'array',
111
                'doc' => 'Returns a struct with info about the page, latest version.',
112
                'name' => 'pageInfo'
113
            ), 'wiki.getPageInfoVersion' => array(
114
                'args' => array('string', 'int'),
115
                'return' => 'array',
116
                'doc' => 'Returns a struct with info about the page.',
117
                'name' => 'pageInfo'
118
            ), 'wiki.getPageVersions' => array(
119
                'args' => array('string', 'int'),
120
                'return' => 'array',
121
                'doc' => 'Returns the available revisions of the page.',
122
                'name' => 'pageVersions'
123
            ), 'wiki.putPage' => array(
124
                'args' => array('string', 'string', 'array'),
125
                'return' => 'bool',
126
                'doc' => 'Saves a wiki page.'
127
            ), 'wiki.listLinks' => array(
128
                'args' => array('string'),
129
                'return' => 'array',
130
                'doc' => 'Lists all links contained in a wiki page.'
131
            ), 'wiki.getRecentChanges' => array(
132
                'args' => array('int'),
133
                'return' => 'array',
134
                'Returns a struct about all recent changes since given timestamp.'
135
            ), 'wiki.getRecentMediaChanges' => array(
136
                'args' => array('int'),
137
                'return' => 'array',
138
                'Returns a struct about all recent media changes since given timestamp.'
139
            ), 'wiki.aclCheck' => array(
140
                'args' => array('string', 'string', 'array'),
141
                'return' => 'int',
142
                'doc' => 'Returns the permissions of a given wiki page. By default, for current user/groups'
143
            ), 'wiki.putAttachment' => array(
144
                'args' => array('string', 'file', 'array'),
145
                'return' => 'array',
146
                'doc' => 'Upload a file to the wiki.'
147
            ), 'wiki.deleteAttachment' => array(
148
                'args' => array('string'),
149
                'return' => 'int',
150
                'doc' => 'Delete a file from the wiki.'
151
            ), 'wiki.getAttachment' => array(
152
                'args' => array('string'),
153
                'doc' => 'Return a media file',
154
                'return' => 'file',
155
                'name' => 'getAttachment',
156
            ), 'wiki.getAttachmentInfo' => array(
157
                'args' => array('string'),
158
                'return' => 'array',
159
                'doc' => 'Returns a struct with info about the attachment.'
160
            ), 'dokuwiki.getXMLRPCAPIVersion' => array(
161
                'args' => array(),
162
                'name' => 'getAPIVersion',
163
                'return' => 'int',
164
                'doc' => 'Returns the XMLRPC API version.',
165
                'public' => '1',
166
            ), 'wiki.getRPCVersionSupported' => array(
167
                'args' => array(),
168
                'name' => 'wiki_RPCVersion',
169
                'return' => 'int',
170
                'doc' => 'Returns 2 with the supported RPC API version.',
171
                'public' => '1'
172
            ),
173
174
        );
175
    }
176
177
    /**
178
     * @return string
179
     */
180
    public function getVersion() {
181
        return getVersion();
182
    }
183
184
    /**
185
     * @return int unix timestamp
186
     */
187
    public function getTime() {
188
        return time();
189
    }
190
191
    /**
192
     * Return a raw wiki page
193
     *
194
     * @param string $id wiki page id
195
     * @param int|string $rev revision timestamp of the page or empty string
196
     * @return string page text.
197
     * @throws RemoteAccessDeniedException if no permission for page
198
     */
199
    public function rawPage($id,$rev=''){
200
        $id = $this->resolvePageId($id);
201
        if(auth_quickaclcheck($id) < AUTH_READ){
202
            throw new RemoteAccessDeniedException('You are not allowed to read this file', 111);
203
        }
204
        $text = rawWiki($id,$rev);
205
        if(!$text) {
206
            return pageTemplate($id);
207
        } else {
208
            return $text;
209
        }
210
    }
211
212
    /**
213
     * Return a media file
214
     *
215
     * @author Gina Haeussge <[email protected]>
216
     *
217
     * @param string $id file id
218
     * @return mixed media file
219
     * @throws RemoteAccessDeniedException no permission for media
220
     * @throws RemoteException not exist
221
     */
222
    public function getAttachment($id){
223
        $id = cleanID($id);
224
        if (auth_quickaclcheck(getNS($id).':*') < AUTH_READ) {
225
            throw new RemoteAccessDeniedException('You are not allowed to read this file', 211);
226
        }
227
228
        $file = mediaFN($id);
229
        if (!@ file_exists($file)) {
230
            throw new RemoteException('The requested file does not exist', 221);
231
        }
232
233
        $data = io_readFile($file, false);
234
        return $this->api->toFile($data);
235
    }
236
237
    /**
238
     * Return info about a media file
239
     *
240
     * @author Gina Haeussge <[email protected]>
241
     *
242
     * @param string $id page id
243
     * @return array
244
     */
245
    public function getAttachmentInfo($id){
246
        $id = cleanID($id);
247
        $info = array(
248
            'lastModified' => $this->api->toDate(0),
249
            'size' => 0,
250
        );
251
252
        $file = mediaFN($id);
253
        if(auth_quickaclcheck(getNS($id) . ':*') >= AUTH_READ) {
254
            if(file_exists($file)) {
255
                $info['lastModified'] = $this->api->toDate(filemtime($file));
256
                $info['size'] = filesize($file);
257
            } else {
258
                //Is it deleted media with changelog?
259
                $medialog = new MediaChangeLog($id);
260
                $revisions = $medialog->getRevisions(0, 1);
261
                if(!empty($revisions)) {
262
                    $info['lastModified'] = $this->api->toDate($revisions[0]);
263
                }
264
            }
265
        }
266
267
        return $info;
268
    }
269
270
    /**
271
     * Return a wiki page rendered to html
272
     *
273
     * @param string     $id  page id
274
     * @param string|int $rev revision timestamp or empty string
275
     * @return null|string html
276
     * @throws RemoteAccessDeniedException no access to page
277
     */
278
    public function htmlPage($id,$rev=''){
279
        $id = $this->resolvePageId($id);
280
        if(auth_quickaclcheck($id) < AUTH_READ){
281
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
282
        }
283
        return p_wiki_xhtml($id,$rev,false);
284
    }
285
286
    /**
287
     * List all pages - we use the indexer list here
288
     *
289
     * @return array
290
     */
291
    public function listPages(){
292
        $list  = array();
293
        $pages = idx_get_indexer()->getPages();
294
        $pages = array_filter(array_filter($pages,'isVisiblePage'),'page_exists');
295
296
        foreach(array_keys($pages) as $idx) {
297
            $perm = auth_quickaclcheck($pages[$idx]);
298
            if($perm < AUTH_READ) {
299
                continue;
300
            }
301
            $page = array();
302
            $page['id'] = trim($pages[$idx]);
303
            $page['perms'] = $perm;
304
            $page['size'] = @filesize(wikiFN($pages[$idx]));
305
            $page['lastModified'] = $this->api->toDate(@filemtime(wikiFN($pages[$idx])));
306
            $list[] = $page;
307
        }
308
309
        return $list;
310
    }
311
312
    /**
313
     * List all pages in the given namespace (and below)
314
     *
315
     * @param string $ns
316
     * @param array  $opts
317
     *    $opts['depth']   recursion level, 0 for all
318
     *    $opts['hash']    do md5 sum of content?
319
     * @return array
320
     */
321
    public function readNamespace($ns,$opts){
322
        global $conf;
323
324
        if(!is_array($opts)) $opts=array();
325
326
        $ns = cleanID($ns);
327
        $dir = utf8_encodeFN(str_replace(':', '/', $ns));
328
        $data = array();
329
        $opts['skipacl'] = 0; // no ACL skipping for XMLRPC
330
        search($data, $conf['datadir'], 'search_allpages', $opts, $dir);
331
        return $data;
332
    }
333
334
    /**
335
     * List all pages in the given namespace (and below)
336
     *
337
     * @param string $query
338
     * @return array
339
     */
340
    public function search($query){
341
        $regex = array();
342
        $data  = ft_pageSearch($query,$regex);
343
        $pages = array();
344
345
        // prepare additional data
346
        $idx = 0;
347
        foreach($data as $id => $score){
348
            $file = wikiFN($id);
349
350
            if($idx < FT_SNIPPET_NUMBER){
351
                $snippet = ft_snippet($id,$regex);
352
                $idx++;
353
            }else{
354
                $snippet = '';
355
            }
356
357
            $pages[] = array(
358
                'id'      => $id,
359
                'score'   => intval($score),
360
                'rev'     => filemtime($file),
361
                'mtime'   => filemtime($file),
362
                'size'    => filesize($file),
363
                'snippet' => $snippet,
364
                'title' => useHeading('navigation') ? p_get_first_heading($id) : $id
365
            );
366
        }
367
        return $pages;
368
    }
369
370
    /**
371
     * Returns the wiki title.
372
     *
373
     * @return string
374
     */
375
    public function getTitle(){
376
        global $conf;
377
        return $conf['title'];
378
    }
379
380
    /**
381
     * List all media files.
382
     *
383
     * Available options are 'recursive' for also including the subnamespaces
384
     * in the listing, and 'pattern' for filtering the returned files against
385
     * a regular expression matching their name.
386
     *
387
     * @author Gina Haeussge <[email protected]>
388
     *
389
     * @param string $ns
390
     * @param array  $options
391
     *   $options['depth']     recursion level, 0 for all
392
     *   $options['showmsg']   shows message if invalid media id is used
393
     *   $options['pattern']   check given pattern
394
     *   $options['hash']      add hashes to result list
395
     * @return array
396
     * @throws RemoteAccessDeniedException no access to the media files
397
     */
398
    public function listAttachments($ns, $options = array()) {
399
        global $conf;
400
401
        $ns = cleanID($ns);
402
403
        if (!is_array($options)) $options = array();
404
        $options['skipacl'] = 0; // no ACL skipping for XMLRPC
405
406
        if(auth_quickaclcheck($ns.':*') >= AUTH_READ) {
407
            $dir = utf8_encodeFN(str_replace(':', '/', $ns));
408
409
            $data = array();
410
            search($data, $conf['mediadir'], 'search_media', $options, $dir);
411
            $len = count($data);
412
            if(!$len) return array();
413
414
            for($i=0; $i<$len; $i++) {
415
                unset($data[$i]['meta']);
416
                $data[$i]['perms'] = $data[$i]['perm'];
417
                unset($data[$i]['perm']);
418
                $data[$i]['lastModified'] = $this->api->toDate($data[$i]['mtime']);
419
            }
420
            return $data;
421
        } else {
422
            throw new RemoteAccessDeniedException('You are not allowed to list media files.', 215);
423
        }
424
    }
425
426
    /**
427
     * Return a list of backlinks
428
     *
429
     * @param string $id page id
430
     * @return array
431
     */
432
    function listBackLinks($id){
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
433
        return ft_backlinks($this->resolvePageId($id));
434
    }
435
436
    /**
437
     * Return some basic data about a page
438
     *
439
     * @param string     $id page id
440
     * @param string|int $rev revision timestamp or empty string
441
     * @return array
442
     * @throws RemoteAccessDeniedException no access for page
443
     * @throws RemoteException page not exist
444
     */
445
    public function pageInfo($id,$rev=''){
446
        $id = $this->resolvePageId($id);
447
        if(auth_quickaclcheck($id) < AUTH_READ){
448
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
449
        }
450
        $file = wikiFN($id,$rev);
451
        $time = @filemtime($file);
452
        if(!$time){
453
            throw new RemoteException('The requested page does not exist', 121);
454
        }
455
456
        // set revision to current version if empty, use revision otherwise
457
        // as the timestamps of old files are not necessarily correct
458
        if($rev === '') {
459
            $rev = $time;
460
        }
461
462
        $pagelog = new PageChangeLog($id, 1024);
463
        $info = $pagelog->getRevisionInfo($rev);
464
465
        $data = array(
466
            'name'         => $id,
467
            'lastModified' => $this->api->toDate($rev),
468
            'author'       => (($info['user']) ? $info['user'] : $info['ip']),
469
            'version'      => $rev
470
        );
471
472
        return ($data);
473
    }
474
475
    /**
476
     * Save a wiki page
477
     *
478
     * @author Michael Klier <[email protected]>
479
     *
480
     * @param string $id page id
481
     * @param string $text wiki text
482
     * @param array $params parameters: summary, minor edit
483
     * @return bool
484
     * @throws RemoteAccessDeniedException no write access for page
485
     * @throws RemoteException no id, empty new page or locked
486
     */
487
    public function putPage($id, $text, $params) {
488
        global $TEXT;
489
        global $lang;
490
491
        $id    = $this->resolvePageId($id);
492
        $TEXT  = cleanText($text);
493
        $sum   = $params['sum'];
494
        $minor = $params['minor'];
495
496
        if(empty($id)) {
497
            throw new RemoteException('Empty page ID', 131);
498
        }
499
500
        if(!page_exists($id) && trim($TEXT) == '' ) {
501
            throw new RemoteException('Refusing to write an empty new wiki page', 132);
502
        }
503
504
        if(auth_quickaclcheck($id) < AUTH_EDIT) {
505
            throw new RemoteAccessDeniedException('You are not allowed to edit this page', 112);
506
        }
507
508
        // Check, if page is locked
509
        if(checklock($id)) {
510
            throw new RemoteException('The page is currently locked', 133);
511
        }
512
513
        // SPAM check
514
        if(checkwordblock()) {
515
            throw new RemoteException('Positive wordblock check', 134);
516
        }
517
518
        // autoset summary on new pages
519
        if(!page_exists($id) && empty($sum)) {
520
            $sum = $lang['created'];
521
        }
522
523
        // autoset summary on deleted pages
524
        if(page_exists($id) && empty($TEXT) && empty($sum)) {
525
            $sum = $lang['deleted'];
526
        }
527
528
        lock($id);
529
530
        saveWikiText($id,$TEXT,$sum,$minor);
531
532
        unlock($id);
533
534
        // run the indexer if page wasn't indexed yet
535
        idx_addPage($id);
536
537
        return true;
538
    }
539
540
    /**
541
     * Appends text to a wiki page.
542
     *
543
     * @param string $id page id
544
     * @param string $text wiki text
545
     * @param array $params such as summary,minor
546
     * @return bool|string
547
     */
548
    public function appendPage($id, $text, $params) {
549
        $currentpage = $this->rawPage($id);
550
        if (!is_string($currentpage)) {
551
            return $currentpage;
552
        }
553
        return $this->putPage($id, $currentpage.$text, $params);
554
    }
555
556
    /**
557
     * Remove one or more users from the list of registered users
558
     *
559
     * @param string[] $usernames List of usernames to remove
560
     *
561
     * @return bool
562
     *
563
     * @throws RemoteAccessDeniedException
564
     */
565
    public function deleteUsers($usernames)
566
    {
567
        if (!auth_isadmin()) {
568
            throw new RemoteAccessDeniedException('Only admins are allowed to delete users', 114);
569
        }
570
        /** @var DokuWiki_Auth_Plugin $auth */
571
        global $auth;
572
        return (bool)$auth->triggerUserMod('deleteUsers', $usernames);
573
    }
574
575
    /**
576
     * Uploads a file to the wiki.
577
     *
578
     * Michael Klier <[email protected]>
579
     *
580
     * @param string $id page id
581
     * @param string $file
582
     * @param array $params such as overwrite
583
     * @return false|string
584
     * @throws RemoteException
585
     */
586
    public function putAttachment($id, $file, $params) {
587
        $id = cleanID($id);
588
        $auth = auth_quickaclcheck(getNS($id).':*');
589
590
        if(!isset($id)) {
591
            throw new RemoteException('Filename not given.', 231);
592
        }
593
594
        global $conf;
595
596
        $ftmp = $conf['tmpdir'] . '/' . md5($id.clientIP());
597
598
        // save temporary file
599
        @unlink($ftmp);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
600
        io_saveFile($ftmp, $file);
601
602
        $res = media_save(array('name' => $ftmp), $id, $params['ow'], $auth, 'rename');
603
        if (is_array($res)) {
604
            throw new RemoteException($res[0], -$res[1]);
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
     * @param string $id page id
616
     * @return int
617
     * @throws RemoteAccessDeniedException no permissions
618
     * @throws RemoteException file in use or not deleted
619
     */
620
    public function deleteAttachment($id){
621
        $id = cleanID($id);
622
        $auth = auth_quickaclcheck(getNS($id).':*');
623
        $res = media_delete($id, $auth);
624
        if ($res & DOKU_MEDIA_DELETED) {
625
            return 0;
626
        } elseif ($res & DOKU_MEDIA_NOT_AUTH) {
627
            throw new RemoteAccessDeniedException('You don\'t have permissions to delete files.', 212);
628
        } elseif ($res & DOKU_MEDIA_INUSE) {
629
            throw new RemoteException('File is still referenced', 232);
630
        } else {
631
            throw new RemoteException('Could not delete file', 233);
632
        }
633
    }
634
635
    /**
636
     * Returns the permissions of a given wiki page for the current user or another user
637
     *
638
     * @param string $id page id
639
     * @param string|null $user username
640
     * @param array|null $groups array of groups
641
     * @return int permission level
642
     */
643
    public function aclCheck($id, $user = null, $groups = null) {
644
        /** @var DokuWiki_Auth_Plugin $auth */
645
        global $auth;
646
647
        $id = $this->resolvePageId($id);
648
        if($user === null) {
649
            return auth_quickaclcheck($id);
650
        } else {
651
            if($groups === null) {
652
                $userinfo = $auth->getUserData($user);
653
                if($userinfo === false) {
654
                    $groups = array();
655
                } else {
656
                    $groups = $userinfo['grps'];
657
                }
658
            }
659
            return auth_aclcheck($id, $user, $groups);
660
        }
661
    }
662
663
    /**
664
     * Lists all links contained in a wiki page
665
     *
666
     * @author Michael Klier <[email protected]>
667
     *
668
     * @param string $id page id
669
     * @return array
670
     * @throws RemoteAccessDeniedException  no read access for page
671
     */
672
    public function listLinks($id) {
673
        $id = $this->resolvePageId($id);
674
        if(auth_quickaclcheck($id) < AUTH_READ){
675
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
676
        }
677
        $links = array();
678
679
        // resolve page instructions
680
        $ins   = p_cached_instructions(wikiFN($id));
681
682
        // instantiate new Renderer - needed for interwiki links
683
        $Renderer = new Doku_Renderer_xhtml();
684
        $Renderer->interwiki = getInterwiki();
685
686
        // parse parse instructions
687
        foreach($ins as $in) {
0 ignored issues
show
Bug introduced by
The expression $ins of type array|false|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...
688
            $link = array();
689
            switch($in[0]) {
690
                case 'internallink':
691
                    $link['type'] = 'local';
692
                    $link['page'] = $in[1][0];
693
                    $link['href'] = wl($in[1][0]);
694
                    array_push($links,$link);
695
                    break;
696
                case 'externallink':
697
                    $link['type'] = 'extern';
698
                    $link['page'] = $in[1][0];
699
                    $link['href'] = $in[1][0];
700
                    array_push($links,$link);
701
                    break;
702
                case 'interwikilink':
703
                    $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]);
704
                    $link['type'] = 'extern';
705
                    $link['page'] = $url;
706
                    $link['href'] = $url;
707
                    array_push($links,$link);
708
                    break;
709
            }
710
        }
711
712
        return ($links);
713
    }
714
715
    /**
716
     * Returns a list of recent changes since give timestamp
717
     *
718
     * @author Michael Hamann <[email protected]>
719
     * @author Michael Klier <[email protected]>
720
     *
721
     * @param int $timestamp unix timestamp
722
     * @return array
723
     * @throws RemoteException no valid timestamp
724
     */
725
    public function getRecentChanges($timestamp) {
726
        if(strlen($timestamp) != 10) {
727
            throw new RemoteException('The provided value is not a valid timestamp', 311);
728
        }
729
730
        $recents = getRecentsSince($timestamp);
731
732
        $changes = array();
733
734
        foreach ($recents as $recent) {
735
            $change = array();
736
            $change['name']         = $recent['id'];
737
            $change['lastModified'] = $this->api->toDate($recent['date']);
738
            $change['author']       = $recent['user'];
739
            $change['version']      = $recent['date'];
740
            $change['perms']        = $recent['perms'];
741
            $change['size']         = @filesize(wikiFN($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
            throw new RemoteException('There are no changes in the specified timeframe', 321);
750
        }
751
    }
752
753
    /**
754
     * Returns a list of recent media changes since give timestamp
755
     *
756
     * @author Michael Hamann <[email protected]>
757
     * @author Michael Klier <[email protected]>
758
     *
759
     * @param int $timestamp unix timestamp
760
     * @return array
761
     * @throws RemoteException no valid timestamp
762
     */
763
    public function getRecentMediaChanges($timestamp) {
764
        if(strlen($timestamp) != 10)
765
            throw new RemoteException('The provided value is not a valid timestamp', 311);
766
767
        $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES);
768
769
        $changes = array();
770
771
        foreach ($recents as $recent) {
772
            $change = array();
773
            $change['name']         = $recent['id'];
774
            $change['lastModified'] = $this->api->toDate($recent['date']);
775
            $change['author']       = $recent['user'];
776
            $change['version']      = $recent['date'];
777
            $change['perms']        = $recent['perms'];
778
            $change['size']         = @filesize(mediaFN($recent['id']));
779
            array_push($changes, $change);
780
        }
781
782
        if (!empty($changes)) {
783
            return $changes;
784
        } else {
785
            // in case we still have nothing at this point
786
            throw new RemoteException('There are no changes in the specified timeframe', 321);
787
        }
788
    }
789
790
    /**
791
     * Returns a list of available revisions of a given wiki page
792
     * Number of returned pages is set by $conf['recent']
793
     * However not accessible pages are skipped, so less than $conf['recent'] could be returned
794
     *
795
     * @author Michael Klier <[email protected]>
796
     *
797
     * @param string $id    page id
798
     * @param int    $first skip the first n changelog lines (0 = from current(if exists), 1 = from 1st old rev, 2 = from 2nd old rev, etc)
799
     * @return array
800
     * @throws RemoteAccessDeniedException no read access for page
801
     * @throws RemoteException empty id
802
     */
803
    public function pageVersions($id, $first) {
804
        $id = $this->resolvePageId($id);
805
        if(auth_quickaclcheck($id) < AUTH_READ) {
806
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
807
        }
808
        global $conf;
809
810
        $versions = array();
811
812
        if(empty($id)) {
813
            throw new RemoteException('Empty page ID', 131);
814
        }
815
816
        $first = (int) $first;
817
        $first_rev = $first - 1;
818
        $first_rev = $first_rev < 0 ? 0 : $first_rev;
819
        $pagelog = new PageChangeLog($id);
820
        $revisions = $pagelog->getRevisions($first_rev, $conf['recent']);
821
822
        if($first == 0) {
823
            array_unshift($revisions, '');  // include current revision
824
            if ( count($revisions) > $conf['recent'] ){
825
                array_pop($revisions);          // remove extra log entry
826
            }
827
        }
828
829
        if(!empty($revisions)) {
830
            foreach($revisions as $rev) {
831
                $file = wikiFN($id,$rev);
832
                $time = @filemtime($file);
833
                // we check if the page actually exists, if this is not the
834
                // case this can lead to less pages being returned than
835
                // specified via $conf['recent']
836
                if($time){
837
                    $pagelog->setChunkSize(1024);
838
                    $info = $pagelog->getRevisionInfo($rev ? $rev : $time);
839
                    if(!empty($info)) {
840
                        $data = array();
841
                        $data['user'] = $info['user'];
842
                        $data['ip']   = $info['ip'];
843
                        $data['type'] = $info['type'];
844
                        $data['sum']  = $info['sum'];
845
                        $data['modified'] = $this->api->toDate($info['date']);
846
                        $data['version'] = $info['date'];
847
                        array_push($versions, $data);
848
                    }
849
                }
850
            }
851
            return $versions;
852
        } else {
853
            return array();
854
        }
855
    }
856
857
    /**
858
     * The version of Wiki RPC API supported
859
     */
860
    public function wiki_RPCVersion(){
861
        return 2;
862
    }
863
864
865
    /**
866
     * Locks or unlocks a given batch of pages
867
     *
868
     * Give an associative array with two keys: lock and unlock. Both should contain a
869
     * list of pages to lock or unlock
870
     *
871
     * Returns an associative array with the keys locked, lockfail, unlocked and
872
     * unlockfail, each containing lists of pages.
873
     *
874
     * @param array[] $set list pages with array('lock' => array, 'unlock' => array)
875
     * @return array
876
     */
877
    public function setLocks($set){
878
        $locked     = array();
879
        $lockfail   = array();
880
        $unlocked   = array();
881
        $unlockfail = array();
882
883
        foreach((array) $set['lock'] as $id){
884
            $id = $this->resolvePageId($id);
885
            if(auth_quickaclcheck($id) < AUTH_EDIT || checklock($id)){
886
                $lockfail[] = $id;
887
            }else{
888
                lock($id);
889
                $locked[] = $id;
890
            }
891
        }
892
893
        foreach((array) $set['unlock'] as $id){
894
            $id = $this->resolvePageId($id);
895
            if(auth_quickaclcheck($id) < AUTH_EDIT || !unlock($id)){
896
                $unlockfail[] = $id;
897
            }else{
898
                $unlocked[] = $id;
899
            }
900
        }
901
902
        return array(
903
            'locked'     => $locked,
904
            'lockfail'   => $lockfail,
905
            'unlocked'   => $unlocked,
906
            'unlockfail' => $unlockfail,
907
        );
908
    }
909
910
    /**
911
     * Return API version
912
     *
913
     * @return int
914
     */
915
    public function getAPIVersion(){
916
        return DOKU_API_VERSION;
917
    }
918
919
    /**
920
     * Login
921
     *
922
     * @param string $user
923
     * @param string $pass
924
     * @return int
925
     */
926
    public function login($user,$pass){
927
        global $conf;
928
        /** @var DokuWiki_Auth_Plugin $auth */
929
        global $auth;
930
931
        if(!$conf['useacl']) return 0;
932
        if(!$auth) return 0;
933
934
        @session_start(); // reopen session for login
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
935
        if($auth->canDo('external')){
936
            $ok = $auth->trustExternal($user,$pass,false);
937
        }else{
938
            $evdata = array(
939
                'user'     => $user,
940
                'password' => $pass,
941
                'sticky'   => false,
942
                'silent'   => true,
943
            );
944
            $ok = trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
945
        }
946
        session_write_close(); // we're done with the session
947
948
        return $ok;
949
    }
950
951
    /**
952
     * Log off
953
     *
954
     * @return int
955
     */
956
    public function logoff(){
957
        global $conf;
958
        global $auth;
959
        if(!$conf['useacl']) return 0;
960
        if(!$auth) return 0;
961
962
        auth_logoff();
963
964
        return 1;
965
    }
966
967
    /**
968
     * Resolve page id
969
     *
970
     * @param string $id page id
971
     * @return string
972
     */
973
    private function resolvePageId($id) {
974
        $id = cleanID($id);
975
        if(empty($id)) {
976
            global $conf;
977
            $id = cleanID($conf['start']);
978
        }
979
        return $id;
980
    }
981
982
}
983
984