Completed
Push — remoteapiGetversions ( 33fe8b...b2f4ab )
by Gerrit
12s
created

RemoteAPICore::wiki_RPCVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 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
            ),  '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
    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...
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
        $pagelog = new PageChangeLog($id, 1024);
453
        $info = $pagelog->getRevisionInfo($time);
454
455
        $data = array(
456
            'name'         => $id,
457
            'lastModified' => $this->api->toDate($time),
458
            'author'       => (($info['user']) ? $info['user'] : $info['ip']),
459
            'version'      => $time
460
        );
461
462
        return ($data);
463
    }
464
465
    /**
466
     * Save a wiki page
467
     *
468
     * @author Michael Klier <[email protected]>
469
     *
470
     * @param string $id page id
471
     * @param string $text wiki text
472
     * @param array $params parameters: summary, minor edit
473
     * @return bool
474
     * @throws RemoteAccessDeniedException no write access for page
475
     * @throws RemoteException no id, empty new page or locked
476
     */
477
    public function putPage($id, $text, $params) {
478
        global $TEXT;
479
        global $lang;
480
481
        $id    = $this->resolvePageId($id);
482
        $TEXT  = cleanText($text);
483
        $sum   = $params['sum'];
484
        $minor = $params['minor'];
485
486
        if(empty($id)) {
487
            throw new RemoteException('Empty page ID', 131);
488
        }
489
490
        if(!page_exists($id) && trim($TEXT) == '' ) {
491
            throw new RemoteException('Refusing to write an empty new wiki page', 132);
492
        }
493
494
        if(auth_quickaclcheck($id) < AUTH_EDIT) {
495
            throw new RemoteAccessDeniedException('You are not allowed to edit this page', 112);
496
        }
497
498
        // Check, if page is locked
499
        if(checklock($id)) {
500
            throw new RemoteException('The page is currently locked', 133);
501
        }
502
503
        // SPAM check
504
        if(checkwordblock()) {
505
            throw new RemoteException('Positive wordblock check', 134);
506
        }
507
508
        // autoset summary on new pages
509
        if(!page_exists($id) && empty($sum)) {
510
            $sum = $lang['created'];
511
        }
512
513
        // autoset summary on deleted pages
514
        if(page_exists($id) && empty($TEXT) && empty($sum)) {
515
            $sum = $lang['deleted'];
516
        }
517
518
        lock($id);
519
520
        saveWikiText($id,$TEXT,$sum,$minor);
521
522
        unlock($id);
523
524
        // run the indexer if page wasn't indexed yet
525
        idx_addPage($id);
526
527
        return true;
528
    }
529
530
    /**
531
     * Appends text to a wiki page.
532
     *
533
     * @param string $id page id
534
     * @param string $text wiki text
535
     * @param array $params such as summary,minor
536
     * @return bool|string
537
     */
538
    public function appendPage($id, $text, $params) {
539
        $currentpage = $this->rawPage($id);
540
        if (!is_string($currentpage)) {
541
            return $currentpage;
542
        }
543
        return $this->putPage($id, $currentpage.$text, $params);
544
    }
545
546
    /**
547
     * Uploads a file to the wiki.
548
     *
549
     * Michael Klier <[email protected]>
550
     *
551
     * @param string $id page id
552
     * @param string $file
553
     * @param array $params such as overwrite
554
     * @return false|string
555
     * @throws RemoteException
556
     */
557
    public function putAttachment($id, $file, $params) {
558
        $id = cleanID($id);
559
        $auth = auth_quickaclcheck(getNS($id).':*');
560
561
        if(!isset($id)) {
562
            throw new RemoteException('Filename not given.', 231);
563
        }
564
565
        global $conf;
566
567
        $ftmp = $conf['tmpdir'] . '/' . md5($id.clientIP());
568
569
        // save temporary file
570
        @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...
571
        io_saveFile($ftmp, $file);
572
573
        $res = media_save(array('name' => $ftmp), $id, $params['ow'], $auth, 'rename');
574
        if (is_array($res)) {
575
            throw new RemoteException($res[0], -$res[1]);
576
        } else {
577
            return $res;
578
        }
579
    }
580
581
    /**
582
     * Deletes a file from the wiki.
583
     *
584
     * @author Gina Haeussge <[email protected]>
585
     *
586
     * @param string $id page id
587
     * @return int
588
     * @throws RemoteAccessDeniedException no permissions
589
     * @throws RemoteException file in use or not deleted
590
     */
591
    public function deleteAttachment($id){
592
        $id = cleanID($id);
593
        $auth = auth_quickaclcheck(getNS($id).':*');
594
        $res = media_delete($id, $auth);
595
        if ($res & DOKU_MEDIA_DELETED) {
596
            return 0;
597
        } elseif ($res & DOKU_MEDIA_NOT_AUTH) {
598
            throw new RemoteAccessDeniedException('You don\'t have permissions to delete files.', 212);
599
        } elseif ($res & DOKU_MEDIA_INUSE) {
600
            throw new RemoteException('File is still referenced', 232);
601
        } else {
602
            throw new RemoteException('Could not delete file', 233);
603
        }
604
    }
605
606
    /**
607
     * Returns the permissions of a given wiki page for the current user or another user
608
     *
609
     * @param string $id page id
610
     * @param string|null $user username
611
     * @param array|null $groups array of groups
612
     * @return int permission level
613
     */
614
    public function aclCheck($id, $user = null, $groups = null) {
615
        /** @var DokuWiki_Auth_Plugin $auth */
616
        global $auth;
617
618
        $id = $this->resolvePageId($id);
619
        if($user === null) {
620
            return auth_quickaclcheck($id);
621
        } else {
622
            if($groups === null) {
623
                $userinfo = $auth->getUserData($user);
624
                if($userinfo === false) {
625
                    $groups = array();
626
                } else {
627
                    $groups = $userinfo['grps'];
628
                }
629
            }
630
            return auth_aclcheck($id, $user, $groups);
631
        }
632
    }
633
634
    /**
635
     * Lists all links contained in a wiki page
636
     *
637
     * @author Michael Klier <[email protected]>
638
     *
639
     * @param string $id page id
640
     * @return array
641
     * @throws RemoteAccessDeniedException  no read access for page
642
     */
643
    public function listLinks($id) {
644
        $id = $this->resolvePageId($id);
645
        if(auth_quickaclcheck($id) < AUTH_READ){
646
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
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
        $Renderer = new Doku_Renderer_xhtml();
655
        $Renderer->interwiki = getInterwiki();
656
657
        // parse parse instructions
658
        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...
659
            $link = array();
660
            switch($in[0]) {
661
                case 'internallink':
662
                    $link['type'] = 'local';
663
                    $link['page'] = $in[1][0];
664
                    $link['href'] = wl($in[1][0]);
665
                    array_push($links,$link);
666
                    break;
667
                case 'externallink':
668
                    $link['type'] = 'extern';
669
                    $link['page'] = $in[1][0];
670
                    $link['href'] = $in[1][0];
671
                    array_push($links,$link);
672
                    break;
673
                case 'interwikilink':
674
                    $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]);
675
                    $link['type'] = 'extern';
676
                    $link['page'] = $url;
677
                    $link['href'] = $url;
678
                    array_push($links,$link);
679
                    break;
680
            }
681
        }
682
683
        return ($links);
684
    }
685
686
    /**
687
     * Returns a list of recent changes since give timestamp
688
     *
689
     * @author Michael Hamann <[email protected]>
690
     * @author Michael Klier <[email protected]>
691
     *
692
     * @param int $timestamp unix timestamp
693
     * @return array
694
     * @throws RemoteException no valid timestamp
695
     */
696
    public function getRecentChanges($timestamp) {
697
        if(strlen($timestamp) != 10) {
698
            throw new RemoteException('The provided value is not a valid timestamp', 311);
699
        }
700
701
        $recents = getRecentsSince($timestamp);
702
703
        $changes = array();
704
705
        foreach ($recents as $recent) {
706
            $change = array();
707
            $change['name']         = $recent['id'];
708
            $change['lastModified'] = $this->api->toDate($recent['date']);
709
            $change['author']       = $recent['user'];
710
            $change['version']      = $recent['date'];
711
            $change['perms']        = $recent['perms'];
712
            $change['size']         = @filesize(wikiFN($recent['id']));
713
            array_push($changes, $change);
714
        }
715
716
        if (!empty($changes)) {
717
            return $changes;
718
        } else {
719
            // in case we still have nothing at this point
720
            throw new RemoteException('There are no changes in the specified timeframe', 321);
721
        }
722
    }
723
724
    /**
725
     * Returns a list of recent media changes since give timestamp
726
     *
727
     * @author Michael Hamann <[email protected]>
728
     * @author Michael Klier <[email protected]>
729
     *
730
     * @param int $timestamp unix timestamp
731
     * @return array
732
     * @throws RemoteException no valid timestamp
733
     */
734
    public function getRecentMediaChanges($timestamp) {
735
        if(strlen($timestamp) != 10)
736
            throw new RemoteException('The provided value is not a valid timestamp', 311);
737
738
        $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES);
739
740
        $changes = array();
741
742
        foreach ($recents as $recent) {
743
            $change = array();
744
            $change['name']         = $recent['id'];
745
            $change['lastModified'] = $this->api->toDate($recent['date']);
746
            $change['author']       = $recent['user'];
747
            $change['version']      = $recent['date'];
748
            $change['perms']        = $recent['perms'];
749
            $change['size']         = @filesize(mediaFN($recent['id']));
750
            array_push($changes, $change);
751
        }
752
753
        if (!empty($changes)) {
754
            return $changes;
755
        } else {
756
            // in case we still have nothing at this point
757
            throw new RemoteException('There are no changes in the specified timeframe', 321);
758
        }
759
    }
760
761
    /**
762
     * Returns a list of available revisions of a given wiki page
763
     * Number of returned pages is set by $conf['recent']
764
     * However not accessible pages are skipped, so less than $conf['recent'] could be returned
765
     *
766
     * @author Michael Klier <[email protected]>
767
     *
768
     * @param string $id    page id
769
     * @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)
770
     * @return array
771
     * @throws RemoteAccessDeniedException no read access for page
772
     * @throws RemoteException empty id
773
     */
774
    public function pageVersions($id, $first) {
775
        $id = $this->resolvePageId($id);
776
        if(auth_quickaclcheck($id) < AUTH_READ) {
777
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
778
        }
779
        global $conf;
780
781
        $versions = array();
782
783
        if(empty($id)) {
784
            throw new RemoteException('Empty page ID', 131);
785
        }
786
787
        $first = (int) $first;
788
        $first_rev = $first - 1;
789
        $first_rev = $first_rev < 0 ? 0 : $first_rev;
790
        $pagelog = new PageChangeLog($id);
791
        $revisions = $pagelog->getRevisions($first_rev, $conf['recent']);
792
793
        if($first == 0) {
794
            array_unshift($revisions, '');  // include current revision
795
            if ( count($revisions) > $conf['recent'] ){
796
                array_pop($revisions);          // remove extra log entry
797
            }
798
        }
799
800
        if(!empty($revisions)) {
801
            foreach($revisions as $rev) {
802
                $file = wikiFN($id,$rev);
803
                $time = @filemtime($file);
804
                // we check if the page actually exists, if this is not the
805
                // case this can lead to less pages being returned than
806
                // specified via $conf['recent']
807
                if($time){
808
                    $pagelog->setChunkSize(1024);
809
                    $info = $pagelog->getRevisionInfo($time);
810
                    if(!empty($info)) {
811
                        $data = array();
812
                        $data['user'] = $info['user'];
813
                        $data['ip']   = $info['ip'];
814
                        $data['type'] = $info['type'];
815
                        $data['sum']  = $info['sum'];
816
                        $data['modified'] = $this->api->toDate($info['date']);
817
                        $data['version'] = $info['date'];
818
                        array_push($versions, $data);
819
                    }
820
                }
821
            }
822
            return $versions;
823
        } else {
824
            return array();
825
        }
826
    }
827
828
    /**
829
     * The version of Wiki RPC API supported
830
     */
831
    public function wiki_RPCVersion(){
832
        return 2;
833
    }
834
835
836
    /**
837
     * Locks or unlocks a given batch of pages
838
     *
839
     * Give an associative array with two keys: lock and unlock. Both should contain a
840
     * list of pages to lock or unlock
841
     *
842
     * Returns an associative array with the keys locked, lockfail, unlocked and
843
     * unlockfail, each containing lists of pages.
844
     *
845
     * @param array[] $set list pages with array('lock' => array, 'unlock' => array)
846
     * @return array
847
     */
848
    public function setLocks($set){
849
        $locked     = array();
850
        $lockfail   = array();
851
        $unlocked   = array();
852
        $unlockfail = array();
853
854
        foreach((array) $set['lock'] as $id){
855
            $id = $this->resolvePageId($id);
856
            if(auth_quickaclcheck($id) < AUTH_EDIT || checklock($id)){
857
                $lockfail[] = $id;
858
            }else{
859
                lock($id);
860
                $locked[] = $id;
861
            }
862
        }
863
864
        foreach((array) $set['unlock'] as $id){
865
            $id = $this->resolvePageId($id);
866
            if(auth_quickaclcheck($id) < AUTH_EDIT || !unlock($id)){
867
                $unlockfail[] = $id;
868
            }else{
869
                $unlocked[] = $id;
870
            }
871
        }
872
873
        return array(
874
            'locked'     => $locked,
875
            'lockfail'   => $lockfail,
876
            'unlocked'   => $unlocked,
877
            'unlockfail' => $unlockfail,
878
        );
879
    }
880
881
    /**
882
     * Return API version
883
     *
884
     * @return int
885
     */
886
    public function getAPIVersion(){
887
        return DOKU_API_VERSION;
888
    }
889
890
    /**
891
     * Login
892
     *
893
     * @param string $user
894
     * @param string $pass
895
     * @return int
896
     */
897
    public function login($user,$pass){
898
        global $conf;
899
        /** @var DokuWiki_Auth_Plugin $auth */
900
        global $auth;
901
902
        if(!$conf['useacl']) return 0;
903
        if(!$auth) return 0;
904
905
        @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...
906
        if($auth->canDo('external')){
907
            $ok = $auth->trustExternal($user,$pass,false);
908
        }else{
909
            $evdata = array(
910
                'user'     => $user,
911
                'password' => $pass,
912
                'sticky'   => false,
913
                'silent'   => true,
914
            );
915
            $ok = trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
916
        }
917
        session_write_close(); // we're done with the session
918
919
        return $ok;
920
    }
921
922
    /**
923
     * Log off
924
     *
925
     * @return int
926
     */
927
    public function logoff(){
928
        global $conf;
929
        global $auth;
930
        if(!$conf['useacl']) return 0;
931
        if(!$auth) return 0;
932
933
        auth_logoff();
934
935
        return 1;
936
    }
937
938
    /**
939
     * Resolve page id
940
     *
941
     * @param string $id page id
942
     * @return string
943
     */
944
    private function resolvePageId($id) {
945
        $id = cleanID($id);
946
        if(empty($id)) {
947
            global $conf;
948
            $id = cleanID($conf['start']);
949
        }
950
        return $id;
951
    }
952
953
}
954
955