Completed
Push — remoteapiGetversions ( bcbcbe...a99e35 )
by Gerrit
04:42
created

RemoteAPICore::getAttachmentInfo()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
rs 8.6845
cc 4
eloc 16
nc 4
nop 1
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'),
137
                'return' => 'int',
138
                'doc' => 'Returns the permissions of a given wiki page.'
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
608
     *
609
     * @param string $id page id
610
     * @return int permission level
611
     */
612
    public function aclCheck($id) {
613
        $id = $this->resolvePageId($id);
614
        return auth_quickaclcheck($id);
615
    }
616
617
    /**
618
     * Lists all links contained in a wiki page
619
     *
620
     * @author Michael Klier <[email protected]>
621
     *
622
     * @param string $id page id
623
     * @return array
624
     * @throws RemoteAccessDeniedException  no read access for page
625
     */
626
    public function listLinks($id) {
627
        $id = $this->resolvePageId($id);
628
        if(auth_quickaclcheck($id) < AUTH_READ){
629
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
630
        }
631
        $links = array();
632
633
        // resolve page instructions
634
        $ins   = p_cached_instructions(wikiFN($id));
635
636
        // instantiate new Renderer - needed for interwiki links
637
        $Renderer = new Doku_Renderer_xhtml();
638
        $Renderer->interwiki = getInterwiki();
639
640
        // parse parse instructions
641
        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...
642
            $link = array();
643
            switch($in[0]) {
644
                case 'internallink':
645
                    $link['type'] = 'local';
646
                    $link['page'] = $in[1][0];
647
                    $link['href'] = wl($in[1][0]);
648
                    array_push($links,$link);
649
                    break;
650
                case 'externallink':
651
                    $link['type'] = 'extern';
652
                    $link['page'] = $in[1][0];
653
                    $link['href'] = $in[1][0];
654
                    array_push($links,$link);
655
                    break;
656
                case 'interwikilink':
657
                    $url = $Renderer->_resolveInterWiki($in[1][2],$in[1][3]);
658
                    $link['type'] = 'extern';
659
                    $link['page'] = $url;
660
                    $link['href'] = $url;
661
                    array_push($links,$link);
662
                    break;
663
            }
664
        }
665
666
        return ($links);
667
    }
668
669
    /**
670
     * Returns a list of recent changes since give timestamp
671
     *
672
     * @author Michael Hamann <[email protected]>
673
     * @author Michael Klier <[email protected]>
674
     *
675
     * @param int $timestamp unix timestamp
676
     * @return array
677
     * @throws RemoteException no valid timestamp
678
     */
679
    public function getRecentChanges($timestamp) {
680
        if(strlen($timestamp) != 10) {
681
            throw new RemoteException('The provided value is not a valid timestamp', 311);
682
        }
683
684
        $recents = getRecentsSince($timestamp);
685
686
        $changes = array();
687
688
        foreach ($recents as $recent) {
689
            $change = array();
690
            $change['name']         = $recent['id'];
691
            $change['lastModified'] = $this->api->toDate($recent['date']);
692
            $change['author']       = $recent['user'];
693
            $change['version']      = $recent['date'];
694
            $change['perms']        = $recent['perms'];
695
            $change['size']         = @filesize(wikiFN($recent['id']));
696
            array_push($changes, $change);
697
        }
698
699
        if (!empty($changes)) {
700
            return $changes;
701
        } else {
702
            // in case we still have nothing at this point
703
            throw new RemoteException('There are no changes in the specified timeframe', 321);
704
        }
705
    }
706
707
    /**
708
     * Returns a list of recent media changes since give timestamp
709
     *
710
     * @author Michael Hamann <[email protected]>
711
     * @author Michael Klier <[email protected]>
712
     *
713
     * @param int $timestamp unix timestamp
714
     * @return array
715
     * @throws RemoteException no valid timestamp
716
     */
717
    public function getRecentMediaChanges($timestamp) {
718
        if(strlen($timestamp) != 10)
719
            throw new RemoteException('The provided value is not a valid timestamp', 311);
720
721
        $recents = getRecentsSince($timestamp, null, '', RECENTS_MEDIA_CHANGES);
722
723
        $changes = array();
724
725
        foreach ($recents as $recent) {
726
            $change = array();
727
            $change['name']         = $recent['id'];
728
            $change['lastModified'] = $this->api->toDate($recent['date']);
729
            $change['author']       = $recent['user'];
730
            $change['version']      = $recent['date'];
731
            $change['perms']        = $recent['perms'];
732
            $change['size']         = @filesize(mediaFN($recent['id']));
733
            array_push($changes, $change);
734
        }
735
736
        if (!empty($changes)) {
737
            return $changes;
738
        } else {
739
            // in case we still have nothing at this point
740
            throw new RemoteException('There are no changes in the specified timeframe', 321);
741
        }
742
    }
743
744
    /**
745
     * Returns a list of available revisions of a given wiki page
746
     * Number of returned pages is set by $conf['recent']
747
     * However not accessible pages are skipped, so less than $conf['recent'] could be returned
748
     *
749
     * @author Michael Klier <[email protected]>
750
     *
751
     * @param string $id    page id
752
     * @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)
753
     * @return array
754
     * @throws RemoteAccessDeniedException no read access for page
755
     * @throws RemoteException empty id
756
     */
757
    public function pageVersions($id, $first) {
758
        $id = $this->resolvePageId($id);
759
        if(auth_quickaclcheck($id) < AUTH_READ) {
760
            throw new RemoteAccessDeniedException('You are not allowed to read this page', 111);
761
        }
762
        global $conf;
763
764
        $versions = array();
765
766
        if(empty($id)) {
767
            throw new RemoteException('Empty page ID', 131);
768
        }
769
770
        $first = (int) $first;
771
        $first_rev = $first - 1;
772
        $first_rev = $first_rev < 0 ? 0 : $first_rev;
773
        $pagelog = new PageChangeLog($id);
774
        $revisions = $pagelog->getRevisions($first_rev, $conf['recent']);
775
776
        if($first == 0) {
777
            array_unshift($revisions, '');  // include current revision
778
            if ( count($revisions) > $conf['recent'] ){
779
                array_pop($revisions);          // remove extra log entry
780
            }
781
        }
782
783
        if(!empty($revisions)) {
784
            foreach($revisions as $rev) {
785
                $file = wikiFN($id,$rev);
786
                $time = @filemtime($file);
787
                // we check if the page actually exists, if this is not the
788
                // case this can lead to less pages being returned than
789
                // specified via $conf['recent']
790
                if($time){
791
                    $pagelog->setChunkSize(1024);
792
                    $info = $pagelog->getRevisionInfo($time);
793
                    if(!empty($info)) {
794
                        $data = array();
795
                        $data['user'] = $info['user'];
796
                        $data['ip']   = $info['ip'];
797
                        $data['type'] = $info['type'];
798
                        $data['sum']  = $info['sum'];
799
                        $data['modified'] = $this->api->toDate($info['date']);
800
                        $data['version'] = $info['date'];
801
                        array_push($versions, $data);
802
                    }
803
                }
804
            }
805
            return $versions;
806
        } else {
807
            return array();
808
        }
809
    }
810
811
    /**
812
     * The version of Wiki RPC API supported
813
     */
814
    public function wiki_RPCVersion(){
815
        return 2;
816
    }
817
818
819
    /**
820
     * Locks or unlocks a given batch of pages
821
     *
822
     * Give an associative array with two keys: lock and unlock. Both should contain a
823
     * list of pages to lock or unlock
824
     *
825
     * Returns an associative array with the keys locked, lockfail, unlocked and
826
     * unlockfail, each containing lists of pages.
827
     *
828
     * @param array[] $set list pages with array('lock' => array, 'unlock' => array)
829
     * @return array
830
     */
831
    public function setLocks($set){
832
        $locked     = array();
833
        $lockfail   = array();
834
        $unlocked   = array();
835
        $unlockfail = array();
836
837
        foreach((array) $set['lock'] as $id){
838
            $id = $this->resolvePageId($id);
839
            if(auth_quickaclcheck($id) < AUTH_EDIT || checklock($id)){
840
                $lockfail[] = $id;
841
            }else{
842
                lock($id);
843
                $locked[] = $id;
844
            }
845
        }
846
847
        foreach((array) $set['unlock'] as $id){
848
            $id = $this->resolvePageId($id);
849
            if(auth_quickaclcheck($id) < AUTH_EDIT || !unlock($id)){
850
                $unlockfail[] = $id;
851
            }else{
852
                $unlocked[] = $id;
853
            }
854
        }
855
856
        return array(
857
            'locked'     => $locked,
858
            'lockfail'   => $lockfail,
859
            'unlocked'   => $unlocked,
860
            'unlockfail' => $unlockfail,
861
        );
862
    }
863
864
    /**
865
     * Return API version
866
     *
867
     * @return int
868
     */
869
    public function getAPIVersion(){
870
        return DOKU_API_VERSION;
871
    }
872
873
    /**
874
     * Login
875
     *
876
     * @param string $user
877
     * @param string $pass
878
     * @return int
879
     */
880
    public function login($user,$pass){
881
        global $conf;
882
        /** @var DokuWiki_Auth_Plugin $auth */
883
        global $auth;
884
885
        if(!$conf['useacl']) return 0;
886
        if(!$auth) return 0;
887
888
        @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...
889
        if($auth->canDo('external')){
890
            $ok = $auth->trustExternal($user,$pass,false);
891
        }else{
892
            $evdata = array(
893
                'user'     => $user,
894
                'password' => $pass,
895
                'sticky'   => false,
896
                'silent'   => true,
897
            );
898
            $ok = trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
899
        }
900
        session_write_close(); // we're done with the session
901
902
        return $ok;
903
    }
904
905
    /**
906
     * Log off
907
     *
908
     * @return int
909
     */
910
    public function logoff(){
911
        global $conf;
912
        global $auth;
913
        if(!$conf['useacl']) return 0;
914
        if(!$auth) return 0;
915
916
        auth_logoff();
917
918
        return 1;
919
    }
920
921
    /**
922
     * Resolve page id
923
     *
924
     * @param string $id page id
925
     * @return string
926
     */
927
    private function resolvePageId($id) {
928
        $id = cleanID($id);
929
        if(empty($id)) {
930
            global $conf;
931
            $id = cleanID($conf['start']);
932
        }
933
        return $id;
934
    }
935
936
}
937
938